diff options
Diffstat (limited to 'src/core/hle/service')
96 files changed, 4103 insertions, 1039 deletions
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index c6437a671..1f8ed265e 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -21,17 +21,6 @@ namespace Service::Account { -// TODO: RE this structure -struct UserData { - INSERT_PADDING_WORDS(1); - u32 icon_id; - u8 bg_color_id; - INSERT_PADDING_BYTES(0x7); - INSERT_PADDING_BYTES(0x10); - INSERT_PADDING_BYTES(0x60); -}; -static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); - // Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg // used as a backup should the one on disk not exist constexpr u32 backup_jpeg_size = 107; @@ -72,9 +61,11 @@ private: void Get(Kernel::HLERequestContext& ctx) { LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); ProfileBase profile_base{}; - std::array<u8, MAX_DATA> data{}; + ProfileData data{}; if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { - ctx.WriteBuffer(data); + std::array<u8, sizeof(ProfileData)> raw_data; + std::memcpy(raw_data.data(), &data, sizeof(ProfileData)); + ctx.WriteBuffer(raw_data); IPC::ResponseBuilder rb{ctx, 16}; rb.Push(RESULT_SUCCESS); rb.PushRaw(profile_base); @@ -216,10 +207,11 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; UUID user_id = rp.PopRaw<UUID>(); + LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IProfile>(user_id, *profile_manager); - LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); } void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx) { @@ -236,10 +228,34 @@ void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx } void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IManagerForApplication>(); +} + +void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); + // A u8 is passed into this function which we can safely ignore. It's to determine if we have + // access to use the network or not by the looks of it + IPC::ResponseBuilder rb{ctx, 6}; + if (profile_manager->GetUserCount() != 1) { + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u128>(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; })) { + rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code + rb.PushRaw<u128>(INVALID_UUID); + return; + } + + // Select the first user we have + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid); } Module::Interface::Interface(std::shared_ptr<Module> module, diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index c7ed74351..89b2104fa 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -27,6 +27,7 @@ public: void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx); + void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx); protected: std::shared_ptr<Module> module; diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index ad455c3a7..5e2030355 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -17,7 +17,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {5, &ACC_SU::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, - {51, nullptr, "TrySelectUserWithoutInteraction"}, + {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 72d4adf35..a4d705b45 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -17,7 +17,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {5, &ACC_U0::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, - {51, nullptr, "TrySelectUserWithoutInteraction"}, + {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index d480f08e5..8fffc93b5 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -17,7 +17,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {5, &ACC_U1::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, - {51, nullptr, "TrySelectUserWithoutInteraction"}, + {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 3cac1b4ff..1316d0b07 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> #include <random> +#include <fmt/format.h> + #include "common/file_util.h" #include "core/hle/service/acc/profile_manager.h" #include "core/settings.h" @@ -15,7 +18,7 @@ struct UserRaw { UUID uuid2; u64 timestamp; ProfileUsername username; - INSERT_PADDING_BYTES(0x80); + ProfileData extra_data; }; static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size."); @@ -39,6 +42,19 @@ UUID UUID::Generate() { 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(); @@ -195,7 +211,7 @@ std::size_t ProfileManager::GetOpenUserCount() const { /// Checks if a user id exists in our profile manager bool ProfileManager::UserExists(UUID uuid) const { - return GetUserIndex(uuid) != std::nullopt; + return GetUserIndex(uuid).has_value(); } bool ProfileManager::UserExistsIndex(std::size_t index) const { @@ -325,11 +341,12 @@ void ProfileManager::ParseUserSaveFile() { return; } - for (std::size_t i = 0; i < MAX_USERS; ++i) { - const auto& user = data.users[i]; + for (const auto& user : data.users) { + if (user.uuid == UUID(INVALID_UUID)) { + continue; + } - if (user.uuid != UUID(INVALID_UUID)) - AddUser({user.uuid, user.username, user.timestamp, {}, false}); + AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false}); } std::stable_partition(profiles.begin(), profiles.end(), @@ -344,6 +361,7 @@ void ProfileManager::WriteUserSaveFile() { raw.users[i].uuid2 = profiles[i].user_uuid; raw.users[i].uuid = profiles[i].user_uuid; raw.users[i].timestamp = profiles[i].creation_time; + raw.users[i].extra_data = profiles[i].data; } const auto raw_path = diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 1cd2e51b2..c4ce2e0b3 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -13,7 +13,6 @@ namespace Service::Account { constexpr std::size_t MAX_USERS = 8; -constexpr std::size_t MAX_DATA = 128; constexpr u128 INVALID_UUID{{0, 0}}; struct UUID { @@ -42,25 +41,28 @@ struct UUID { void Invalidate() { uuid = INVALID_UUID; } - std::string Format() const { - return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); - } - std::string 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]); - } + std::string Format() const; + std::string FormatSwitch() const; }; static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); -using ProfileUsername = std::array<u8, 0x20>; -using ProfileData = std::array<u8, MAX_DATA>; +constexpr std::size_t profile_username_size = 32; +using ProfileUsername = std::array<u8, profile_username_size>; using UserIDArray = std::array<UUID, MAX_USERS>; +/// Contains extra data related to a user. +/// TODO: RE this structure +struct ProfileData { + INSERT_PADDING_WORDS(1); + u32 icon_id; + u8 bg_color_id; + INSERT_PADDING_BYTES(0x7); + INSERT_PADDING_BYTES(0x10); + INSERT_PADDING_BYTES(0x60); +}; +static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size"); + /// This holds general information about a users profile. This is where we store all the information /// based on a specific user struct ProfileInfo { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 59aafd616..d13ce4dca 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -6,14 +6,23 @@ #include <cinttypes> #include <cstring> #include <stack> +#include "audio_core/audio_renderer.h" #include "core/core.h" +#include "core/file_sys/savedata_factory.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/am/applets/profile_select.h" +#include "core/hle/service/am/applets/software_keyboard.h" +#include "core/hle/service/am/applets/stub_applet.h" #include "core/hle/service/am/idle.h" #include "core/hle/service/am/omm.h" #include "core/hle/service/am/spsm.h" @@ -28,6 +37,14 @@ namespace Service::AM { +constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2}; +constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; + +enum class AppletId : u32 { + ProfileSelect = 0x10, + SoftwareKeyboard = 0x11, +}; + constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; struct LaunchParameters { @@ -57,10 +74,13 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") { IWindowController::~IWindowController() = default; void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); + const u64 process_id = Core::System::GetInstance().Kernel().CurrentProcess()->GetProcessID(); + + LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push<u64>(0); + rb.Push<u64>(process_id); } void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) { @@ -196,15 +216,16 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - launchable_event = - Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent"); + launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "ISelfController:LaunchableEvent"); } ISelfController::~ISelfController() = default; void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous u8, these are - // bool flags. No output. + // Takes 3 input u8s with each field located immediately after the previous + // u8, these are bool flags. No output. + LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::RequestParser rp{ctx}; @@ -217,128 +238,172 @@ void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; bool flag = rp.Pop<bool>(); + LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); } void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; bool flag = rp.Pop<bool>(); + LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); } void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous u8, these are - // bool flags. No output. + // Takes 3 input u8s with each field located immediately after the previous + // u8, these are bool flags. No output. IPC::RequestParser rp{ctx}; bool enabled = rp.Pop<bool>(); + LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); } void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { - launchable_event->Signal(); + LOG_WARNING(Service_AM, "(STUBBED) called"); + + launchable_event.writable->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(launchable_event); - - LOG_WARNING(Service_AM, "(STUBBED) called"); + rb.PushCopyObjects(launchable_event.readable); } void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { - // TODO(Subv): Find out how AM determines the display to use, for now just create the layer - // in the Default display. + LOG_WARNING(Service_AM, "(STUBBED) called"); + // TODO(Subv): Find out how AM determines the display to use, for now just + // create the layer in the Default display. u64 display_id = nvflinger->OpenDisplay("Default"); u64 layer_id = nvflinger->CreateLayer(display_id); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push(layer_id); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; idle_time_detection_extension = rp.Pop<u32>(); + LOG_WARNING(Service_AM, "(STUBBED) called idle_time_detection_extension={}", + idle_time_detection_extension); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(idle_time_detection_extension); +} - LOG_WARNING(Service_AM, "(STUBBED) called"); +AppletMessageQueue::AppletMessageQueue() { + auto& kernel = Core::System::GetInstance().Kernel(); + on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "AMMessageQueue:OnMessageRecieved"); + on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "AMMessageQueue:OperationModeChanged"); +} + +AppletMessageQueue::~AppletMessageQueue() = default; + +const Kernel::SharedPtr<Kernel::ReadableEvent>& AppletMessageQueue::GetMesssageRecieveEvent() + const { + return on_new_message.readable; +} + +const Kernel::SharedPtr<Kernel::ReadableEvent>& AppletMessageQueue::GetOperationModeChangedEvent() + const { + return on_operation_mode_changed.readable; +} + +void AppletMessageQueue::PushMessage(AppletMessage msg) { + messages.push(msg); + on_new_message.writable->Signal(); +} + +AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { + if (messages.empty()) { + on_new_message.writable->Clear(); + return AppletMessage::NoMessage; + } + auto msg = messages.front(); + messages.pop(); + if (messages.empty()) { + on_new_message.writable->Clear(); + } + return msg; } -ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { +std::size_t AppletMessageQueue::GetMessageCount() const { + return messages.size(); +} + +void AppletMessageQueue::OperationModeChanged() { + PushMessage(AppletMessage::OperationModeChanged); + PushMessage(AppletMessage::PerformanceModeChanged); + on_operation_mode_changed.writable->Signal(); +} + +ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) { // clang-format off static const FunctionInfo functions[] = { {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, @@ -371,101 +436,131 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter" // clang-format on RegisterHandlers(functions); - - auto& kernel = Core::System::GetInstance().Kernel(); - event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ICommonStateGetter:Event"); } ICommonStateGetter::~ICommonStateGetter() = default; void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode - - LOG_DEBUG(Service_AM, "called"); } void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) { - event->Signal(); + LOG_DEBUG(Service_AM, "called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(event); - - LOG_WARNING(Service_AM, "(STUBBED) called"); + rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent()); } void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(15); - - LOG_WARNING(Service_AM, "(STUBBED) called"); + rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage()); } void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u8>(FocusState::InFocus)); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { - event->Signal(); + LOG_DEBUG(Service_AM, "called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(event); - - LOG_WARNING(Service_AM, "(STUBBED) called"); + rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent()); } void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); if (Settings::values.use_docked_mode) { - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * + static_cast<u32>(Settings::values.resolution_factor)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * + static_cast<u32>(Settings::values.resolution_factor)); } else { - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * + static_cast<u32>(Settings::values.resolution_factor)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * + static_cast<u32>(Settings::values.resolution_factor)); } +} - LOG_DEBUG(Service_AM, "called"); +IStorage::IStorage(std::vector<u8> buffer) + : ServiceFramework("IStorage"), buffer(std::move(buffer)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IStorage::Open, "Open"}, + {1, nullptr, "OpenTransferStorage"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IStorage::~IStorage() = default; + +const std::vector<u8>& IStorage::GetData() const { + return buffer; } void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { const bool use_docked_mode{Settings::values.use_docked_mode}; + LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld)); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { const bool use_docked_mode{Settings::values.use_docked_mode}; + LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked : APM::PerformanceMode::Handheld)); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } -class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { +class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { public: - explicit IStorageAccessor(std::vector<u8> buffer) - : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { + explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet) + : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) { // clang-format off static const FunctionInfo functions[] = { - {0, &IStorageAccessor::GetSize, "GetSize"}, - {10, &IStorageAccessor::Write, "Write"}, - {11, &IStorageAccessor::Read, "Read"}, + {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, + {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"}, + {10, &ILibraryAppletAccessor::Start, "Start"}, + {20, nullptr, "RequestExit"}, + {25, nullptr, "Terminate"}, + {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, + {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, + {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, + {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, + {102, nullptr, "PushExtraStorage"}, + {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"}, + {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"}, + {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"}, + {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"}, + {110, nullptr, "NeedsToExitProcess"}, + {120, nullptr, "GetLibraryAppletInfo"}, + {150, nullptr, "RequestForAppletToGetForeground"}, + {160, nullptr, "GetIndirectLayerConsumerHandle"}, }; // clang-format on @@ -473,158 +568,199 @@ public: } private: - std::vector<u8> buffer; + void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); - void GetSize(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 4}; + const auto event = applet->GetBroker().GetStateChangedEvent(); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u64>(buffer.size())); + rb.PushCopyObjects(event); + } + void IsCompleted(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(applet->TransactionComplete()); } - void Write(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; + void GetResult(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(applet->GetStatus()); + } - const u64 offset{rp.Pop<u64>()}; - const std::vector<u8> data{ctx.ReadBuffer()}; + void Start(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); - ASSERT(offset + data.size() <= buffer.size()); + ASSERT(applet != nullptr); - std::memcpy(&buffer[offset], data.data(), data.size()); + applet->Initialize(); + applet->Execute(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_DEBUG(Service_AM, "called, offset={}", offset); } - void Read(Kernel::HLERequestContext& ctx) { + void PushInData(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::RequestParser rp{ctx}; + applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>()); - const u64 offset{rp.Pop<u64>()}; - const std::size_t size{ctx.GetWriteBufferSize()}; + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } - ASSERT(offset + size <= buffer.size()); + void PopOutData(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); - ctx.WriteBuffer(buffer.data() + offset, size); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + const auto storage = applet->GetBroker().PopNormalDataToGame(); + if (storage == nullptr) { + LOG_ERROR(Service_AM, + "storage is a nullptr. There is no data in the current normal channel"); - LOG_DEBUG(Service_AM, "called, offset={}", offset); + rb.Push(ERR_NO_DATA_IN_CHANNEL); + return; + } + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IStorage>(std::move(*storage)); } -}; -class IStorage final : public ServiceFramework<IStorage> { -public: - explicit IStorage(std::vector<u8> buffer) - : ServiceFramework("IStorage"), buffer(std::move(buffer)) { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IStorage::Open, "Open"}, - {1, nullptr, "OpenTransferStorage"}, - }; - // clang-format on + void PushInteractiveInData(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); - RegisterHandlers(functions); + IPC::RequestParser rp{ctx}; + applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>()); + + ASSERT(applet->IsInitialized()); + applet->ExecuteInteractive(); + applet->Execute(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); } -private: - std::vector<u8> buffer; + void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); - void Open(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + const auto storage = applet->GetBroker().PopInteractiveDataToGame(); + if (storage == nullptr) { + LOG_ERROR(Service_AM, + "storage is a nullptr. There is no data in the current interactive channel"); + + rb.Push(ERR_NO_DATA_IN_CHANNEL); + return; + } + rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<AM::IStorageAccessor>(buffer); + rb.PushIpcInterface<IStorage>(std::move(*storage)); + } + void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent()); + } + + void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent()); } + + std::shared_ptr<Applets::Applet> applet; }; -class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { -public: - explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { - // clang-format off +void IStorage::Open(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IStorageAccessor>(*this); +} + +IStorageAccessor::IStorageAccessor(IStorage& storage) + : ServiceFramework("IStorageAccessor"), backing(storage) { + // clang-format off static const FunctionInfo functions[] = { - {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, - {1, nullptr, "IsCompleted"}, - {10, &ILibraryAppletAccessor::Start, "Start"}, - {20, nullptr, "RequestExit"}, - {25, nullptr, "Terminate"}, - {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, - {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, - {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, - {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, - {102, nullptr, "PushExtraStorage"}, - {103, nullptr, "PushInteractiveInData"}, - {104, nullptr, "PopInteractiveOutData"}, - {105, nullptr, "GetPopOutDataEvent"}, - {106, nullptr, "GetPopInteractiveOutDataEvent"}, - {110, nullptr, "NeedsToExitProcess"}, - {120, nullptr, "GetLibraryAppletInfo"}, - {150, nullptr, "RequestForAppletToGetForeground"}, - {160, nullptr, "GetIndirectLayerConsumerHandle"}, + {0, &IStorageAccessor::GetSize, "GetSize"}, + {10, &IStorageAccessor::Write, "Write"}, + {11, &IStorageAccessor::Read, "Read"}, }; - // clang-format on + // clang-format on - RegisterHandlers(functions); + RegisterHandlers(functions); +} - auto& kernel = Core::System::GetInstance().Kernel(); - state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "ILibraryAppletAccessor:StateChangedEvent"); - } +IStorageAccessor::~IStorageAccessor() = default; -private: - void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { - state_changed_event->Signal(); +void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(state_changed_event); + IPC::ResponseBuilder rb{ctx, 4}; - LOG_WARNING(Service_AM, "(STUBBED) called"); - } + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u64>(backing.buffer.size())); +} - void GetResult(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); +void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; - LOG_WARNING(Service_AM, "(STUBBED) called"); - } + const u64 offset{rp.Pop<u64>()}; + LOG_DEBUG(Service_AM, "called, offset={}", offset); - void Start(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + const std::vector<u8> data{ctx.ReadBuffer()}; + + if (data.size() > backing.buffer.size() - offset) { + LOG_ERROR(Service_AM, + "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}", + backing.buffer.size(), data.size(), offset); - LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_SIZE_OUT_OF_BOUNDS); } - void PushInData(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - storage_stack.push(rp.PopIpcInterface<AM::IStorage>()); + std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} - LOG_DEBUG(Service_AM, "called"); - } +void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; - void PopOutData(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top())); + const u64 offset{rp.Pop<u64>()}; + LOG_DEBUG(Service_AM, "called, offset={}", offset); - storage_stack.pop(); + const std::size_t size{ctx.GetWriteBufferSize()}; - LOG_DEBUG(Service_AM, "called"); + if (size > backing.buffer.size() - offset) { + LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}", + backing.buffer.size(), size, offset); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_SIZE_OUT_OF_BOUNDS); } - std::stack<std::shared_ptr<AM::IStorage>> storage_stack; - Kernel::SharedPtr<Kernel::Event> state_changed_event; -}; + ctx.WriteBuffer(backing.buffer.data() + offset, size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { static const FunctionInfo functions[] = { @@ -632,7 +768,7 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple {1, nullptr, "TerminateAllLibraryApplets"}, {2, nullptr, "AreAnyLibraryAppletsLeft"}, {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, - {11, nullptr, "CreateTransferMemoryStorage"}, + {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, {12, nullptr, "CreateHandleStorage"}, }; RegisterHandlers(functions); @@ -640,25 +776,81 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple ILibraryAppletCreator::~ILibraryAppletCreator() = default; +static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) { + switch (id) { + case AppletId::ProfileSelect: + return std::make_shared<Applets::ProfileSelect>(); + case AppletId::SoftwareKeyboard: + return std::make_shared<Applets::SoftwareKeyboard>(); + default: + LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!", + static_cast<u32>(id)); + return std::make_shared<Applets::StubApplet>(); + } +} + void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_id = rp.PopRaw<AppletId>(); + const auto applet_mode = rp.PopRaw<u32>(); + + LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", + static_cast<u32>(applet_id), applet_mode); + + const auto applet = GetAppletFromId(applet_id); + + if (applet == nullptr) { + LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); + return; + } + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<AM::ILibraryAppletAccessor>(); - - LOG_DEBUG(Service_AM, "called"); + rb.PushIpcInterface<AM::ILibraryAppletAccessor>(applet); } void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 size{rp.Pop<u64>()}; + LOG_DEBUG(Service_AM, "called, size={}", size); + std::vector<u8> buffer(size); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<AM::IStorage>(std::move(buffer)); +} - LOG_DEBUG(Service_AM, "called, size={}", size); +void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::RequestParser rp{ctx}; + + rp.SetCurrentOffset(3); + const auto handle{rp.Pop<Kernel::Handle>()}; + + const auto shared_mem = + Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>( + handle); + + if (shared_mem == nullptr) { + LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); + return; + } + + const u8* mem_begin = shared_mem->GetPointer(); + const u8* mem_end = mem_begin + shared_mem->GetSize(); + std::vector<u8> memory{mem_begin, mem_end}; + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory))); } IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { @@ -674,8 +866,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"}, {24, nullptr, "GetLaunchStorageInfoForDebug"}, - {25, nullptr, "ExtendSaveData"}, - {26, nullptr, "GetSaveDataSize"}, + {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, + {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, @@ -690,7 +882,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {70, nullptr, "RequestToShutdown"}, {71, nullptr, "RequestToReboot"}, {80, nullptr, "ExitAndRequestToShowThanksMessage"}, - {90, nullptr, "EnableApplicationCrashReport"}, + {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"}, {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"}, {101, nullptr, "SetApplicationCopyrightImage"}, {102, nullptr, "SetApplicationCopyrightVisibility"}, @@ -709,33 +901,46 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF IApplicationFunctions::~IApplicationFunctions() = default; +void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed( Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed( Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + LaunchParameters params{}; params.magic = POP_LAUNCH_PARAMETER_MAGIC; @@ -743,7 +948,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { Account::ProfileManager profile_manager{}; const auto uuid = profile_manager.GetUser(Settings::values.current_user); - ASSERT(uuid != std::nullopt); + ASSERT(uuid); params.current_user = uuid->uuid; IPC::ResponseBuilder rb{ctx, 2, 0, 1}; @@ -754,21 +959,19 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { std::memcpy(buffer.data(), ¶ms, buffer.size()); rb.PushIpcInterface<AM::IStorage>(buffer); - - LOG_DEBUG(Service_AM, "called"); } void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; u128 uid = rp.PopRaw<u128>(); // What does this do? - LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); IPC::ResponseBuilder rb{ctx, 4}; @@ -778,70 +981,119 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { // Takes an input u32 Result, no output. - // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. + // For example, in some cases official apps use this with error 0x2A2 then + // uses svcBreak. IPC::RequestParser rp{ctx}; u32 result = rp.Pop<u32>(); + LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result); } void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(1); rb.Push<u64>(0); - LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { // TODO(bunnei): This should be configurable + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push( static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index))); - LOG_DEBUG(Service_AM, "called"); } void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u8>(0); // Unknown, seems to be ignored by official processes - - LOG_WARNING(Service_AM, "(STUBBED) called"); } void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); // Returns a 128-bit UUID rb.Push<u64>(0); rb.Push<u64>(0); +} - LOG_WARNING(Service_AM, "(STUBBED) called"); +void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto type{rp.PopRaw<FileSys::SaveDataType>()}; + rp.Skip(1, false); + const auto user_id{rp.PopRaw<u128>()}; + const auto new_normal_size{rp.PopRaw<u64>()}; + const auto new_journal_size{rp.PopRaw<u64>()}; + + LOG_DEBUG(Service_AM, + "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, " + "new_journal={:016X}", + static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); + + FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id, + {new_normal_size, new_journal_size}); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + + // The following value is used upon failure to help the system recover. + // Since we always succeed, this should be 0. + rb.Push<u64>(0); +} + +void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto type{rp.PopRaw<FileSys::SaveDataType>()}; + rp.Skip(1, false); + const auto user_id{rp.PopRaw<u128>()}; + + LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type), + user_id[1], user_id[0]); + + const auto size = + FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.Push(size.normal); + rb.Push(size.journal); } void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { - std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); - std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager); + auto message_queue = std::make_shared<AppletMessageQueue>(); + message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on + // game boot + + std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager); + std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager); std::make_shared<IdleSys>()->InstallAsService(service_manager); std::make_shared<OMM>()->InstallAsService(service_manager); std::make_shared<SPSM>()->InstallAsService(service_manager); @@ -868,9 +1120,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions" IHomeMenuFunctions::~IHomeMenuFunctions() = default; void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_AM, "(STUBBED) called"); } IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") { diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 095f94851..b6113cfdd 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -5,12 +5,10 @@ #pragma once #include <memory> +#include <queue> +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/service.h" -namespace Kernel { -class Event; -} - namespace Service { namespace NVFlinger { class NVFlinger; @@ -39,6 +37,31 @@ enum SystemLanguage { TraditionalChinese = 16, }; +class AppletMessageQueue { +public: + enum class AppletMessage : u32 { + NoMessage = 0, + FocusStateChanged = 15, + OperationModeChanged = 30, + PerformanceModeChanged = 31, + }; + + AppletMessageQueue(); + ~AppletMessageQueue(); + + const Kernel::SharedPtr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const; + const Kernel::SharedPtr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const; + void PushMessage(AppletMessage msg); + AppletMessage PopMessage(); + std::size_t GetMessageCount() const; + void OperationModeChanged(); + +private: + std::queue<AppletMessage> messages; + Kernel::EventPair on_new_message; + Kernel::EventPair on_operation_mode_changed; +}; + class IWindowController final : public ServiceFramework<IWindowController> { public: IWindowController(); @@ -96,13 +119,13 @@ private: void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); std::shared_ptr<NVFlinger::NVFlinger> nvflinger; - Kernel::SharedPtr<Kernel::Event> launchable_event; + Kernel::EventPair launchable_event; u32 idle_time_detection_extension = 0; }; class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { public: - ICommonStateGetter(); + explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue); ~ICommonStateGetter() override; private: @@ -125,7 +148,35 @@ private: void GetBootMode(Kernel::HLERequestContext& ctx); void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); - Kernel::SharedPtr<Kernel::Event> event; + std::shared_ptr<AppletMessageQueue> msg_queue; +}; + +class IStorage final : public ServiceFramework<IStorage> { +public: + explicit IStorage(std::vector<u8> buffer); + ~IStorage() override; + + const std::vector<u8>& GetData() const; + +private: + void Open(Kernel::HLERequestContext& ctx); + + std::vector<u8> buffer; + + friend class IStorageAccessor; +}; + +class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { +public: + explicit IStorageAccessor(IStorage& backing); + ~IStorageAccessor() override; + +private: + void GetSize(Kernel::HLERequestContext& ctx); + void Write(Kernel::HLERequestContext& ctx); + void Read(Kernel::HLERequestContext& ctx); + + IStorage& backing; }; class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { @@ -136,6 +187,7 @@ public: private: void CreateLibraryApplet(Kernel::HLERequestContext& ctx); void CreateStorage(Kernel::HLERequestContext& ctx); + void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx); }; class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { @@ -154,10 +206,13 @@ private: void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); void NotifyRunning(Kernel::HLERequestContext& ctx); void GetPseudoDeviceId(Kernel::HLERequestContext& ctx); + void ExtendSaveData(Kernel::HLERequestContext& ctx); + void GetSaveDataSize(Kernel::HLERequestContext& ctx); void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); + void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx); }; class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 68ea778e8..41a573a91 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -12,8 +12,10 @@ namespace Service::AM { class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { public: - explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) { + explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), + msg_queue(std::move(msg_queue)) { static const FunctionInfo functions[] = { {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, @@ -30,75 +32,87 @@ public: private: void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(); - LOG_DEBUG(Service_AM, "called"); + rb.PushIpcInterface<ICommonStateGetter>(msg_queue); } void GetSelfController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISelfController>(nvflinger); - LOG_DEBUG(Service_AM, "called"); } void GetWindowController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IWindowController>(); - LOG_DEBUG(Service_AM, "called"); } void GetAudioController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAudioController>(); - LOG_DEBUG(Service_AM, "called"); } void GetDisplayController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDisplayController>(); - LOG_DEBUG(Service_AM, "called"); } void GetProcessWindingController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IProcessWindingController>(); - LOG_DEBUG(Service_AM, "called"); } void GetDebugFunctions(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDebugFunctions>(); - LOG_DEBUG(Service_AM, "called"); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILibraryAppletCreator>(); - LOG_DEBUG(Service_AM, "called"); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IApplicationFunctions>(); - LOG_DEBUG(Service_AM, "called"); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + std::shared_ptr<AppletMessageQueue> msg_queue; }; class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { public: - explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)) { + explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), + msg_queue(std::move(msg_queue)) { static const FunctionInfo functions[] = { {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, @@ -117,100 +131,116 @@ public: private: void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(); - LOG_DEBUG(Service_AM, "called"); + rb.PushIpcInterface<ICommonStateGetter>(msg_queue); } void GetSelfController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISelfController>(nvflinger); - LOG_DEBUG(Service_AM, "called"); } void GetWindowController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IWindowController>(); - LOG_DEBUG(Service_AM, "called"); } void GetAudioController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAudioController>(); - LOG_DEBUG(Service_AM, "called"); } void GetDisplayController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDisplayController>(); - LOG_DEBUG(Service_AM, "called"); } void GetDebugFunctions(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDebugFunctions>(); - LOG_DEBUG(Service_AM, "called"); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILibraryAppletCreator>(); - LOG_DEBUG(Service_AM, "called"); } void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IHomeMenuFunctions>(); - LOG_DEBUG(Service_AM, "called"); } void GetGlobalStateController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IGlobalStateController>(); - LOG_DEBUG(Service_AM, "called"); } void GetApplicationCreator(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IApplicationCreator>(); - LOG_DEBUG(Service_AM, "called"); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + std::shared_ptr<AppletMessageQueue> msg_queue; }; void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemAppletProxy>(nvflinger); - LOG_DEBUG(Service_AM, "called"); + rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue); } void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); - LOG_DEBUG(Service_AM, "called"); + rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue); } void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); - LOG_DEBUG(Service_AM, "called"); + rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue); } -AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { +AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)), + msg_queue(std::move(msg_queue)) { // clang-format off static const FunctionInfo functions[] = { {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, @@ -228,4 +258,8 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) AppletAE::~AppletAE() = default; +const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const { + return msg_queue; +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index 1ed77baa4..902db2665 100644 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h @@ -17,15 +17,19 @@ namespace AM { class AppletAE final : public ServiceFramework<AppletAE> { public: - explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue); ~AppletAE() override; + const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const; + private: void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx); void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx); void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx); std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + std::shared_ptr<AppletMessageQueue> msg_queue; }; } // namespace AM diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index 60717afd9..d3a0a1568 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -12,8 +12,10 @@ namespace Service::AM { class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { public: - explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { + explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)), + msg_queue(std::move(msg_queue)) { // clang-format off static const FunctionInfo functions[] = { {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, @@ -33,73 +35,85 @@ public: private: void GetAudioController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAudioController>(); - LOG_DEBUG(Service_AM, "called"); } void GetDisplayController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDisplayController>(); - LOG_DEBUG(Service_AM, "called"); } void GetDebugFunctions(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDebugFunctions>(); - LOG_DEBUG(Service_AM, "called"); } void GetWindowController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IWindowController>(); - LOG_DEBUG(Service_AM, "called"); } void GetSelfController(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISelfController>(nvflinger); - LOG_DEBUG(Service_AM, "called"); } void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(); - LOG_DEBUG(Service_AM, "called"); + rb.PushIpcInterface<ICommonStateGetter>(msg_queue); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILibraryAppletCreator>(); - LOG_DEBUG(Service_AM, "called"); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IApplicationFunctions>(); - LOG_DEBUG(Service_AM, "called"); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + std::shared_ptr<AppletMessageQueue> msg_queue; }; void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationProxy>(nvflinger); - LOG_DEBUG(Service_AM, "called"); + rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue); } -AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) { +AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)), + msg_queue(std::move(msg_queue)) { static const FunctionInfo functions[] = { {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, }; @@ -108,4 +122,8 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) AppletOE::~AppletOE() = default; +const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const { + return msg_queue; +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index 60cfdfd9d..bbd0108ef 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -17,13 +17,17 @@ namespace AM { class AppletOE final : public ServiceFramework<AppletOE> { public: - explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue); ~AppletOE() override; + const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const; + private: void OpenApplicationProxy(Kernel::HLERequestContext& ctx); std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + std::shared_ptr<AppletMessageQueue> msg_queue; }; } // namespace AM diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp new file mode 100644 index 000000000..a6064c63f --- /dev/null +++ b/src/core/hle/service/am/applets/applets.cpp @@ -0,0 +1,114 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/assert.h" +#include "core/core.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/writable_event.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Service::AM::Applets { + +AppletDataBroker::AppletDataBroker() { + auto& kernel = Core::System::GetInstance().Kernel(); + state_changed_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent"); + pop_out_data_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent"); + pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); +} + +AppletDataBroker::~AppletDataBroker() = default; + +std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { + if (out_channel.empty()) + return nullptr; + + auto out = std::move(out_channel.front()); + out_channel.pop(); + return out; +} + +std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { + if (in_channel.empty()) + return nullptr; + + auto out = std::move(in_channel.front()); + in_channel.pop(); + return out; +} + +std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { + if (out_interactive_channel.empty()) + return nullptr; + + auto out = std::move(out_interactive_channel.front()); + out_interactive_channel.pop(); + return out; +} + +std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { + if (in_interactive_channel.empty()) + return nullptr; + + auto out = std::move(in_interactive_channel.front()); + in_interactive_channel.pop(); + return out; +} + +void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { + in_channel.push(std::make_unique<IStorage>(storage)); +} + +void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { + out_channel.push(std::make_unique<IStorage>(storage)); + pop_out_data_event.writable->Signal(); +} + +void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { + in_interactive_channel.push(std::make_unique<IStorage>(storage)); +} + +void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { + out_interactive_channel.push(std::make_unique<IStorage>(storage)); + pop_interactive_out_data_event.writable->Signal(); +} + +void AppletDataBroker::SignalStateChanged() const { + state_changed_event.writable->Signal(); +} + +Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetNormalDataEvent() const { + return pop_out_data_event.readable; +} + +Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetInteractiveDataEvent() const { + return pop_interactive_out_data_event.readable; +} + +Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetStateChangedEvent() const { + return state_changed_event.readable; +} + +Applet::Applet() = default; + +Applet::~Applet() = default; + +void Applet::Initialize() { + const auto common = broker.PopNormalDataToApplet(); + ASSERT(common != nullptr); + + const auto common_data = common->GetData(); + + ASSERT(common_data.size() >= sizeof(CommonArguments)); + std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments)); + + initialized = true; +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h new file mode 100644 index 000000000..37424c379 --- /dev/null +++ b/src/core/hle/service/am/applets/applets.h @@ -0,0 +1,109 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <queue> +#include "common/swap.h" +#include "core/hle/kernel/object.h" +#include "core/hle/kernel/writable_event.h" + +union ResultCode; + +namespace Service::AM { + +class IStorage; + +namespace Applets { + +class AppletDataBroker final { +public: + AppletDataBroker(); + ~AppletDataBroker(); + + std::unique_ptr<IStorage> PopNormalDataToGame(); + std::unique_ptr<IStorage> PopNormalDataToApplet(); + + std::unique_ptr<IStorage> PopInteractiveDataToGame(); + std::unique_ptr<IStorage> PopInteractiveDataToApplet(); + + void PushNormalDataFromGame(IStorage storage); + void PushNormalDataFromApplet(IStorage storage); + + void PushInteractiveDataFromGame(IStorage storage); + void PushInteractiveDataFromApplet(IStorage storage); + + void SignalStateChanged() const; + + Kernel::SharedPtr<Kernel::ReadableEvent> GetNormalDataEvent() const; + Kernel::SharedPtr<Kernel::ReadableEvent> GetInteractiveDataEvent() const; + Kernel::SharedPtr<Kernel::ReadableEvent> GetStateChangedEvent() const; + +private: + // Queues are named from applet's perspective + + // PopNormalDataToApplet and PushNormalDataFromGame + std::queue<std::unique_ptr<IStorage>> in_channel; + + // PopNormalDataToGame and PushNormalDataFromApplet + std::queue<std::unique_ptr<IStorage>> out_channel; + + // PopInteractiveDataToApplet and PushInteractiveDataFromGame + std::queue<std::unique_ptr<IStorage>> in_interactive_channel; + + // PopInteractiveDataToGame and PushInteractiveDataFromApplet + std::queue<std::unique_ptr<IStorage>> out_interactive_channel; + + Kernel::EventPair state_changed_event; + + // Signaled on PushNormalDataFromApplet + Kernel::EventPair pop_out_data_event; + + // Signaled on PushInteractiveDataFromApplet + Kernel::EventPair pop_interactive_out_data_event; +}; + +class Applet { +public: + Applet(); + virtual ~Applet(); + + virtual void Initialize(); + + virtual bool TransactionComplete() const = 0; + virtual ResultCode GetStatus() const = 0; + virtual void ExecuteInteractive() = 0; + virtual void Execute() = 0; + + bool IsInitialized() const { + return initialized; + } + + AppletDataBroker& GetBroker() { + return broker; + } + + const AppletDataBroker& GetBroker() const { + return broker; + } + +protected: + struct CommonArguments { + u32_le arguments_version; + u32_le size; + u32_le library_version; + u32_le theme_color; + u8 play_startup_sound; + u64_le system_tick; + }; + static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); + + CommonArguments common_args{}; + AppletDataBroker broker; + bool initialized = false; +}; + +} // namespace Applets +} // namespace Service::AM diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp new file mode 100644 index 000000000..4c7b45454 --- /dev/null +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -0,0 +1,77 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> + +#include "common/assert.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/profile_select.h" + +namespace Service::AM::Applets { + +constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; + +ProfileSelect::ProfileSelect() = default; +ProfileSelect::~ProfileSelect() = default; + +void ProfileSelect::Initialize() { + complete = false; + status = RESULT_SUCCESS; + final_data.clear(); + + Applet::Initialize(); + + const auto user_config_storage = broker.PopNormalDataToApplet(); + ASSERT(user_config_storage != nullptr); + const auto& user_config = user_config_storage->GetData(); + + ASSERT(user_config.size() >= sizeof(UserSelectionConfig)); + std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig)); +} + +bool ProfileSelect::TransactionComplete() const { + return complete; +} + +ResultCode ProfileSelect::GetStatus() const { + return status; +} + +void ProfileSelect::ExecuteInteractive() { + UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); +} + +void ProfileSelect::Execute() { + if (complete) { + broker.PushNormalDataFromApplet(IStorage{final_data}); + return; + } + + const auto& frontend{Core::System::GetInstance().GetProfileSelector()}; + + frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); }); +} + +void ProfileSelect::SelectionComplete(std::optional<Account::UUID> uuid) { + UserSelectionOutput output{}; + + if (uuid.has_value() && uuid->uuid != Account::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; + } + + final_data = std::vector<u8>(sizeof(UserSelectionOutput)); + std::memcpy(final_data.data(), &output, final_data.size()); + broker.PushNormalDataFromApplet(IStorage{final_data}); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h new file mode 100644 index 000000000..787485f22 --- /dev/null +++ b/src/core/hle/service/am/applets/profile_select.h @@ -0,0 +1,50 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> + +#include "common/common_funcs.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Service::AM::Applets { + +struct UserSelectionConfig { + // TODO(DarkLordZach): RE this structure + // It seems to be flags and the like that determine the UI of the applet on the switch... from + // my research this is safe to ignore for now. + INSERT_PADDING_BYTES(0xA0); +}; +static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size."); + +struct UserSelectionOutput { + u64 result; + u128 uuid_selected; +}; +static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size."); + +class ProfileSelect final : public Applet { +public: + ProfileSelect(); + ~ProfileSelect() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void SelectionComplete(std::optional<Account::UUID> uuid); + +private: + UserSelectionConfig config; + bool complete = false; + ResultCode status = RESULT_SUCCESS; + std::vector<u8> final_data; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp new file mode 100644 index 000000000..f255f74b5 --- /dev/null +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -0,0 +1,160 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/assert.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/software_keyboard.h" + +namespace Service::AM::Applets { + +constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; +constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; +constexpr std::size_t DEFAULT_MAX_LENGTH = 500; +constexpr bool INTERACTIVE_STATUS_OK = false; + +static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( + KeyboardConfig config, std::u16string initial_text) { + Core::Frontend::SoftwareKeyboardParameters params{}; + + params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + config.submit_text.data(), config.submit_text.size()); + params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + config.header_text.data(), config.header_text.size()); + params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(), + config.sub_text.size()); + params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), + config.guide_text.size()); + params.initial_text = initial_text; + params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; + params.password = static_cast<bool>(config.is_password); + params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position); + params.value = static_cast<u8>(config.keyset_disable_bitmask); + + return params; +} + +SoftwareKeyboard::SoftwareKeyboard() = default; + +SoftwareKeyboard::~SoftwareKeyboard() = default; + +void SoftwareKeyboard::Initialize() { + complete = false; + initial_text.clear(); + final_data.clear(); + + Applet::Initialize(); + + const auto keyboard_config_storage = broker.PopNormalDataToApplet(); + ASSERT(keyboard_config_storage != nullptr); + const auto& keyboard_config = keyboard_config_storage->GetData(); + + ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); + std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); + + const auto work_buffer_storage = broker.PopNormalDataToApplet(); + ASSERT(work_buffer_storage != nullptr); + const auto& work_buffer = work_buffer_storage->GetData(); + + if (config.initial_string_size == 0) + return; + + std::vector<char16_t> string(config.initial_string_size); + std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset, + string.size() * 2); + initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()); +} + +bool SoftwareKeyboard::TransactionComplete() const { + return complete; +} + +ResultCode SoftwareKeyboard::GetStatus() const { + return RESULT_SUCCESS; +} + +void SoftwareKeyboard::ExecuteInteractive() { + if (complete) + return; + + const auto storage = broker.PopInteractiveDataToApplet(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + const auto status = static_cast<bool>(data[0]); + + if (status == INTERACTIVE_STATUS_OK) { + complete = true; + } else { + const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; + + std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; + std::memcpy(string.data(), data.data() + 4, string.size() * 2); + frontend.SendTextCheckDialog( + Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), + [this] { broker.SignalStateChanged(); }); + } +} + +void SoftwareKeyboard::Execute() { + if (complete) { + broker.PushNormalDataFromApplet(IStorage{final_data}); + return; + } + + const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; + + const auto parameters = ConvertToFrontendParameters(config, initial_text); + + frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); }, + parameters); +} + +void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { + std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE); + + if (text.has_value()) { + std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE); + + if (config.utf_8) { + const u64 size = text->size() + 8; + const auto new_text = Common::UTF16ToUTF8(*text); + + std::memcpy(output_sub.data(), &size, sizeof(u64)); + std::memcpy(output_sub.data() + 8, new_text.data(), + std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8)); + + output_main[0] = INTERACTIVE_STATUS_OK; + std::memcpy(output_main.data() + 4, new_text.data(), + std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); + } else { + const u64 size = text->size() * 2 + 8; + std::memcpy(output_sub.data(), &size, sizeof(u64)); + std::memcpy(output_sub.data() + 8, text->data(), + std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); + + output_main[0] = INTERACTIVE_STATUS_OK; + std::memcpy(output_main.data() + 4, text->data(), + std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); + } + + complete = !config.text_check; + final_data = output_main; + + if (complete) { + broker.PushNormalDataFromApplet(IStorage{output_main}); + broker.SignalStateChanged(); + } else { + broker.PushInteractiveDataFromApplet(IStorage{output_sub}); + } + } else { + output_main[0] = 1; + complete = true; + broker.PushNormalDataFromApplet(IStorage{output_main}); + broker.SignalStateChanged(); + } +} +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h new file mode 100644 index 000000000..efd5753a1 --- /dev/null +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -0,0 +1,74 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <string> +#include <vector> + +#include "common/common_funcs.h" +#include "common/swap.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Service::AM::Applets { + +enum class KeysetDisable : u32 { + Space = 0x02, + Address = 0x04, + Percent = 0x08, + Slashes = 0x10, + Numbers = 0x40, + DownloadCode = 0x80, +}; + +struct KeyboardConfig { + INSERT_PADDING_BYTES(4); + std::array<char16_t, 9> submit_text; + u16_le left_symbol_key; + u16_le right_symbol_key; + INSERT_PADDING_BYTES(1); + KeysetDisable keyset_disable_bitmask; + u32_le initial_cursor_position; + std::array<char16_t, 65> header_text; + std::array<char16_t, 129> sub_text; + std::array<char16_t, 257> guide_text; + u32_le length_limit; + INSERT_PADDING_BYTES(4); + u32_le is_password; + INSERT_PADDING_BYTES(5); + bool utf_8; + bool draw_background; + u32_le initial_string_offset; + u32_le initial_string_size; + u32_le user_dictionary_offset; + u32_le user_dictionary_size; + bool text_check; + u64_le text_check_callback; +}; +static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size."); + +class SoftwareKeyboard final : public Applet { +public: + SoftwareKeyboard(); + ~SoftwareKeyboard() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void WriteText(std::optional<std::u16string> text); + +private: + KeyboardConfig config; + std::u16string initial_text; + bool complete = false; + std::vector<u8> final_data; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/stub_applet.cpp b/src/core/hle/service/am/applets/stub_applet.cpp new file mode 100644 index 000000000..ed166b87d --- /dev/null +++ b/src/core/hle/service/am/applets/stub_applet.cpp @@ -0,0 +1,70 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <string> + +#include "common/hex_util.h" +#include "common/logging/log.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/stub_applet.h" + +namespace Service::AM::Applets { + +static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) { + std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet(); + for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { + const auto data = storage->GetData(); + LOG_INFO(Service_AM, + "called (STUBBED), during {} recieved normal data with size={:08X}, data={}", + prefix, data.size(), Common::HexVectorToString(data)); + } + + storage = broker.PopInteractiveDataToApplet(); + for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) { + const auto data = storage->GetData(); + LOG_INFO(Service_AM, + "called (STUBBED), during {} recieved interactive data with size={:08X}, data={}", + prefix, data.size(), Common::HexVectorToString(data)); + } +} + +StubApplet::StubApplet() = default; + +StubApplet::~StubApplet() = default; + +void StubApplet::Initialize() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + Applet::Initialize(); + LogCurrentStorage(broker, "Initialize"); +} + +bool StubApplet::TransactionComplete() const { + LOG_WARNING(Service_AM, "called (STUBBED)"); + return true; +} + +ResultCode StubApplet::GetStatus() const { + LOG_WARNING(Service_AM, "called (STUBBED)"); + return RESULT_SUCCESS; +} + +void StubApplet::ExecuteInteractive() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + LogCurrentStorage(broker, "ExecuteInteractive"); + + broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)}); + broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); + broker.SignalStateChanged(); +} + +void StubApplet::Execute() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + LogCurrentStorage(broker, "Execute"); + + broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)}); + broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); + broker.SignalStateChanged(); +} +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/stub_applet.h b/src/core/hle/service/am/applets/stub_applet.h new file mode 100644 index 000000000..7d8dc968d --- /dev/null +++ b/src/core/hle/service/am/applets/stub_applet.h @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/am/applets/applets.h" + +namespace Service::AM::Applets { + +class StubApplet final : public Applet { +public: + StubApplet(); + ~StubApplet() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 54305cf05..b506bc3dd 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -13,11 +13,14 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" +#include "core/settings.h" namespace Service::AOC { @@ -32,14 +35,14 @@ static std::vector<u64> AccumulateAOCTitleIDs() { std::vector<u64> add_on_content; const auto rcu = FileSystem::GetUnionContents(); const auto list = - rcu->ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); + rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), [](const FileSys::RegisteredCacheEntry& rce) { return rce.title_id; }); add_on_content.erase( std::remove_if( add_on_content.begin(), add_on_content.end(), [&rcu](u64 tid) { - return rcu->GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() != + return rcu.GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() != Loader::ResultStatus::Success; }), add_on_content.end()); @@ -61,17 +64,26 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, - "GetAddOnContentListChanged:Event"); + aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "GetAddOnContentListChanged:Event"); } AOC_U::~AOC_U() = default; void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AOC, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); + + const auto& disabled = Settings::values.disabled_addons[current]; + if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) { + rb.Push<u32>(0); + return; + } + rb.Push<u32>(static_cast<u32>( std::count_if(add_on_content.begin(), add_on_content.end(), [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); @@ -82,6 +94,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { const auto offset = rp.PopRaw<u32>(); auto count = rp.PopRaw<u32>(); + LOG_DEBUG(Service_AOC, "called with offset={}, count={}", offset, count); const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); @@ -91,6 +104,10 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); } + const auto& disabled = Settings::values.disabled_addons[current]; + if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) + out = {}; + if (out.size() < offset) { IPC::ResponseBuilder rb{ctx, 2}; // TODO(DarkLordZach): Find the correct error code. @@ -110,6 +127,8 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { } void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AOC, "called"); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); @@ -128,7 +147,6 @@ void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto aoc_id = rp.PopRaw<u32>(); - LOG_WARNING(Service_AOC, "(STUBBED) called with aoc_id={:08X}", aoc_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -140,7 +158,7 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(aoc_change_event); + rb.PushCopyObjects(aoc_change_event.readable); } void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 68d94fdaa..5effea730 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -6,6 +6,10 @@ #include "core/hle/service/service.h" +namespace Kernel { +class WritableEvent; +} + namespace Service::AOC { class AOC_U final : public ServiceFramework<AOC_U> { @@ -21,7 +25,7 @@ private: void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx); std::vector<u64> add_on_content; - Kernel::SharedPtr<Kernel::Event> aoc_change_event; + Kernel::EventPair aoc_change_event; }; /// Registers all AOC services with the specified service manager. diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index c22bd3859..fcacbab72 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp @@ -40,24 +40,22 @@ private: auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); u32 config = rp.Pop<u32>(); + LOG_WARNING(Service_APM, "(STUBBED) called mode={} config={}", static_cast<u32>(mode), + config); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_APM, "(STUBBED) called mode={} config={}", static_cast<u32>(mode), - config); } void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); + LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode)); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(PerformanceConfiguration::Config1)); - - LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode)); } }; @@ -73,11 +71,11 @@ APM::APM(std::shared_ptr<Module> apm, const char* name) APM::~APM() = default; void APM::OpenSession(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_APM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISession>(); - - LOG_DEBUG(Service_APM, "called"); } APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { @@ -98,11 +96,11 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { APM_Sys::~APM_Sys() = default; void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_APM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISession>(); - - LOG_DEBUG(Service_APM, "called"); } } // namespace Service::APM diff --git a/src/core/hle/service/arp/arp.cpp b/src/core/hle/service/arp/arp.cpp index 358ef2576..e675b0188 100644 --- a/src/core/hle/service/arp/arp.cpp +++ b/src/core/hle/service/arp/arp.cpp @@ -59,11 +59,11 @@ public: private: void AcquireRegistrar(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ARP, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IRegistrar>(); - - LOG_DEBUG(Service_ARP, "called"); } }; diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index ff1edefbb..dc6a6b188 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -13,8 +13,10 @@ #include "common/swap.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/audio/audout_u.h" #include "core/memory.h" @@ -44,8 +46,10 @@ enum class AudioState : u32 { class IAudioOut final : public ServiceFramework<IAudioOut> { public: - IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) - : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params) { + IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name, + std::string&& unique_name) + : ServiceFramework("IAudioOut"), audio_core(audio_core), + device_name(std::move(device_name)), audio_params(audio_params) { static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, @@ -65,11 +69,12 @@ public: // This is the event handle used to check if the audio buffer was released auto& kernel = Core::System::GetInstance().Kernel(); - buffer_event = - Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); + buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "IAudioOutBufferReleased"); stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, - "IAudioOut", [=]() { buffer_event->Signal(); }); + std::move(unique_name), + [=]() { buffer_event.writable->Signal(); }); } private: @@ -84,6 +89,7 @@ private: void GetAudioOutState(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped)); @@ -118,7 +124,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(buffer_event); + rb.PushCopyObjects(buffer_event.readable); } void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { @@ -146,6 +152,7 @@ private: void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); + IPC::RequestParser rp{ctx}; const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; @@ -161,6 +168,7 @@ private: void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); + IPC::RequestParser rp{ctx}; const u64 tag{rp.Pop<u64>()}; IPC::ResponseBuilder rb{ctx, 3}; @@ -170,6 +178,7 @@ private: void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u32>(stream->GetQueueSize())); @@ -177,15 +186,17 @@ private: AudioCore::AudioOut& audio_core; AudioCore::StreamPtr stream; + std::string device_name; AudoutParams audio_params{}; - /// This is the evend handle used to check if the audio buffer was released - Kernel::SharedPtr<Kernel::Event> buffer_event; + /// This is the event handle used to check if the audio buffer was released + Kernel::EventPair buffer_event; }; void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); + IPC::RequestParser rp{ctx}; ctx.WriteBuffer(DefaultDevice); @@ -199,7 +210,15 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - ctx.WriteBuffer(DefaultDevice); + const auto device_name_data{ctx.ReadBuffer()}; + std::string device_name; + if (device_name_data[0] != '\0') { + device_name.assign(device_name_data.begin(), device_name_data.end()); + } else { + device_name.assign(DefaultDevice.begin(), DefaultDevice.end()); + } + ctx.WriteBuffer(device_name); + IPC::RequestParser rp{ctx}; auto params{rp.PopRaw<AudoutParams>()}; if (params.channel_count <= 2) { @@ -212,10 +231,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { params.sample_rate = DefaultSampleRate; } - // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl - // will likely need to be updated as well. - ASSERT_MSG(!audio_out_interface, "Unimplemented"); - audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core); + std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; + auto audio_out_interface = std::make_shared<IAudioOut>( + params, *audio_core, std::move(device_name), std::move(unique_name)); IPC::ResponseBuilder rb{ctx, 6, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -224,6 +242,8 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); + + audio_out_interfaces.push_back(std::move(audio_out_interface)); } AudOutU::AudOutU() : ServiceFramework("audout:u") { diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index dcaf64708..aed4c43b2 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -4,6 +4,7 @@ #pragma once +#include <vector> #include "core/hle/service/service.h" namespace AudioCore { @@ -24,7 +25,7 @@ public: ~AudOutU() override; private: - std::shared_ptr<IAudioOut> audio_out_interface; + std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; std::unique_ptr<AudioCore::AudioOut> audio_core; void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index fac6785a5..945259c7d 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -12,8 +12,10 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/audio/audren_u.h" namespace Service::Audio { @@ -28,90 +30,116 @@ public: {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, {3, &IAudioRenderer::GetState, "GetState"}, - {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, + {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"}, {5, &IAudioRenderer::Start, "Start"}, {6, &IAudioRenderer::Stop, "Stop"}, {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, - {8, nullptr, "SetRenderingTimeLimit"}, - {9, nullptr, "GetRenderingTimeLimit"}, - {10, nullptr, "RequestUpdateAuto"}, + {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, + {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, + {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"}, {11, nullptr, "ExecuteAudioRendererRendering"}, }; // clang-format on RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - system_event = - Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent"); - renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event); + system_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "IAudioRenderer:SystemEvent"); + renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event.writable); } private: void UpdateAudioCallback() { - system_event->Signal(); + system_event.writable->Signal(); } void GetSampleRate(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(renderer->GetSampleRate()); - LOG_DEBUG(Service_Audio, "called"); } void GetSampleCount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(renderer->GetSampleCount()); - LOG_DEBUG(Service_Audio, "called"); } void GetState(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); - LOG_DEBUG(Service_Audio, "called"); } void GetMixBufferCount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(renderer->GetMixBufferCount()); - LOG_DEBUG(Service_Audio, "called"); } - void RequestUpdate(Kernel::HLERequestContext& ctx) { + void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_Audio, "(STUBBED) called"); } void Start(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_Audio, "(STUBBED) called"); } void Stop(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service_Audio, "(STUBBED) called"); } void QuerySystemEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(system_event); + rb.PushCopyObjects(system_event.readable); + } - LOG_WARNING(Service_Audio, "(STUBBED) called"); + void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rendering_time_limit_percent = rp.Pop<u32>(); + LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}", + rendering_time_limit_percent); + + ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); } - Kernel::SharedPtr<Kernel::Event> system_event; + void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(rendering_time_limit_percent); + } + + Kernel::EventPair system_event; std::unique_ptr<AudioCore::AudioRenderer> renderer; + u32 rendering_time_limit_percent = 100; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { @@ -136,8 +164,8 @@ public: RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - buffer_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "IAudioOutBufferReleasedEvent"); + buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IAudioOutBufferReleasedEvent"); } private: @@ -181,21 +209,22 @@ private: void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); - buffer_event->Signal(); + buffer_event.writable->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(buffer_event); + rb.PushCopyObjects(buffer_event.readable); } void GetActiveChannelCount(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(1); } - Kernel::SharedPtr<Kernel::Event> buffer_event; + Kernel::EventPair buffer_event; }; // namespace Audio @@ -214,19 +243,20 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") { AudRenU::~AudRenU() = default; void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::RequestParser rp{ctx}; auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<Audio::IAudioRenderer>(std::move(params)); - - LOG_DEBUG(Service_Audio, "called"); } void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); + LOG_DEBUG(Service_Audio, "called"); u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40); buffer_sz += params.unknown_c * 1024; @@ -280,26 +310,26 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push<u64>(output_sz); - LOG_DEBUG(Service_Audio, "called, buffer_size=0x{:X}", output_sz); + LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz); } void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<Audio::IAudioDevice>(); - - LOG_DEBUG(Service_Audio, "called"); } void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<Audio::IAudioDevice>(); - - LOG_WARNING(Service_Audio, "(STUBBED) called"); // TODO(ogniK): Figure out what is different - // based on the current revision + rb.PushIpcInterface<Audio::IAudioDevice>(); // TODO(ogniK): Figure out what is different + // based on the current revision } bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 7168c6a10..a850cadc8 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -46,10 +46,13 @@ public: private: void DecodeInterleaved(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Audio, "called"); + u32 consumed = 0; u32 sample_count = 0; std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) { + LOG_ERROR(Audio, "Failed to decode opus data"); IPC::ResponseBuilder rb{ctx, 2}; // TODO(ogniK): Use correct error code rb.Push(ResultCode(-1)); @@ -63,12 +66,15 @@ private: } void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Audio, "called"); + u32 consumed = 0; u32 sample_count = 0; u64 performance = 0; std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { + LOG_ERROR(Audio, "Failed to decode opus data"); IPC::ResponseBuilder rb{ctx, 2}; // TODO(ogniK): Use correct error code rb.Push(ResultCode(-1)); @@ -77,8 +83,8 @@ private: IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(consumed); - rb.Push<u64>(performance); rb.Push<u32>(sample_count); + rb.Push<u64>(performance); ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); } @@ -88,24 +94,39 @@ private: std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) { const auto start_time = std::chrono::high_resolution_clock::now(); std::size_t raw_output_sz = output.size() * sizeof(opus_int16); - if (sizeof(OpusHeader) > input.size()) + if (sizeof(OpusHeader) > input.size()) { + LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", + sizeof(OpusHeader), input.size()); return false; + } OpusHeader hdr{}; std::memcpy(&hdr, input.data(), sizeof(OpusHeader)); if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) { + LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}", + sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size()); return false; } auto frame = input.data() + sizeof(OpusHeader); auto decoded_sample_count = opus_packet_get_nb_samples( frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)), static_cast<opus_int32>(sample_rate)); - if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) + if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) { + LOG_ERROR( + Audio, + "Decoded data does not fit into the output data, decoded_sz={}, raw_output_sz={}", + decoded_sample_count * channel_count * sizeof(u16), raw_output_sz); return false; + } + const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); auto out_sample_count = - opus_decode(decoder.get(), frame, hdr.sz, output.data(), - (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0); - if (out_sample_count < 0) + opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0); + if (out_sample_count < 0) { + LOG_ERROR(Audio, + "Incorrect sample count received from opus_decode, " + "output_sample_count={}, frame_size={}, data_sz_from_hdr={}", + out_sample_count, frame_size, static_cast<u32>(hdr.sz)); return false; + } const auto end_time = std::chrono::high_resolution_clock::now() - start_time; sample_count = out_sample_count; consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); @@ -134,14 +155,17 @@ static std::size_t WorkerBufferSize(u32 channel_count) { void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto sample_rate = rp.Pop<u32>(); - auto channel_count = rp.Pop<u32>(); + const auto sample_rate = rp.Pop<u32>(); + const auto channel_count = rp.Pop<u32>(); + LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count); + ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || sample_rate == 12000 || sample_rate == 8000, "Invalid sample rate"); ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); - u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count)); - LOG_DEBUG(Audio, "called worker_buffer_sz={}", worker_buffer_sz); + + const u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count)); + LOG_DEBUG(Audio, "worker_buffer_sz={}", worker_buffer_sz); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -155,16 +179,18 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { auto buffer_sz = rp.Pop<u32>(); LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate, channel_count, buffer_sz); + ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || sample_rate == 12000 || sample_rate == 8000, "Invalid sample rate"); ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); std::size_t worker_sz = WorkerBufferSize(channel_count); - ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); + ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ static_cast<OpusDecoder*>(operator new(worker_sz))}; - if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) { + if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) { + LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err); IPC::ResponseBuilder rb{ctx, 2}; // TODO(ogniK): Use correct error code rb.Push(ResultCode(-1)); diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 6e7b795fb..b7bd738fc 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -33,10 +33,11 @@ public: }; void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IBcatService>(); - LOG_DEBUG(Service_BCAT, "called"); } Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index d0a15cc4c..5704ca0ab 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -2,12 +2,54 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/btdrv/btdrv.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" namespace Service::BtDrv { +class Bt final : public ServiceFramework<Bt> { +public: + explicit Bt() : ServiceFramework{"bt"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown0"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + {8, nullptr, "Unknown8"}, + {9, &Bt::RegisterEvent, "RegisterEvent"}, + }; + // clang-format on + RegisterHandlers(functions); + + auto& kernel = Core::System::GetInstance().Kernel(); + register_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "BT:RegisterEvent"); + } + +private: + void RegisterEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(register_event.readable); + } + + Kernel::EventPair register_event; +}; + class BtDrv final : public ServiceFramework<BtDrv> { public: explicit BtDrv() : ServiceFramework{"btdrv"} { @@ -67,6 +109,7 @@ public: void InstallInterfaces(SM::ServiceManager& sm) { std::make_shared<BtDrv>()->InstallAsService(sm); + std::make_shared<Bt>()->InstallAsService(sm); } } // namespace Service::BtDrv diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index b949bfabd..ef7398a23 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -7,12 +7,126 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/btm/btm.h" #include "core/hle/service/service.h" -#include "core/hle/service/sm/sm.h" namespace Service::BTM { +class IBtmUserCore final : public ServiceFramework<IBtmUserCore> { +public: + explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IBtmUserCore::GetScanEvent, "GetScanEvent"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + {8, nullptr, "Unknown8"}, + {9, nullptr, "Unknown9"}, + {10, nullptr, "Unknown10"}, + {17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"}, + {18, nullptr, "Unknown18"}, + {19, nullptr, "Unknown19"}, + {20, nullptr, "Unknown20"}, + {21, nullptr, "Unknown21"}, + {22, nullptr, "Unknown22"}, + {23, nullptr, "Unknown23"}, + {24, nullptr, "Unknown24"}, + {25, nullptr, "Unknown25"}, + {26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"}, + {27, nullptr, "Unknown27"}, + {28, nullptr, "Unknown28"}, + {29, nullptr, "Unknown29"}, + {30, nullptr, "Unknown30"}, + {31, nullptr, "Unknown31"}, + {32, nullptr, "Unknown32"}, + {33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"}, + {34, nullptr, "Unknown34"}, + {35, nullptr, "Unknown35"}, + {36, nullptr, "Unknown36"}, + {37, nullptr, "Unknown37"}, + }; + // clang-format on + RegisterHandlers(functions); + + auto& kernel = Core::System::GetInstance().Kernel(); + scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IBtmUserCore:ScanEvent"); + connection_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConnectionEvent"); + service_discovery = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery"); + config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IBtmUserCore:ConfigEvent"); + } + +private: + void GetScanEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(scan_event.readable); + } + + void GetConnectionEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(connection_event.readable); + } + + void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(service_discovery.readable); + } + + void GetConfigEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(config_event.readable); + } + + Kernel::EventPair scan_event; + Kernel::EventPair connection_event; + Kernel::EventPair service_discovery; + Kernel::EventPair config_event; +}; + +class BTM_USR final : public ServiceFramework<BTM_USR> { +public: + explicit BTM_USR() : ServiceFramework{"btm:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &BTM_USR::GetCoreImpl, "GetCoreImpl"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + void GetCoreImpl(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BTM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IBtmUserCore>(); + } +}; + class BTM final : public ServiceFramework<BTM> { public: explicit BTM() : ServiceFramework{"btm"} { @@ -104,11 +218,11 @@ public: private: void GetCoreImpl(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BTM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IBtmSystemCore>(); - - LOG_DEBUG(Service_BTM, "called"); } }; @@ -116,6 +230,7 @@ void InstallInterfaces(SM::ServiceManager& sm) { std::make_shared<BTM>()->InstallAsService(sm); std::make_shared<BTM_DBG>()->InstallAsService(sm); std::make_shared<BTM_SYS>()->InstallAsService(sm); + std::make_shared<BTM_USR>()->InstallAsService(sm); } } // namespace Service::BTM diff --git a/src/core/hle/service/erpt/erpt.cpp b/src/core/hle/service/erpt/erpt.cpp index ee11cd78e..d9b32954e 100644 --- a/src/core/hle/service/erpt/erpt.cpp +++ b/src/core/hle/service/erpt/erpt.cpp @@ -17,11 +17,13 @@ public: static const FunctionInfo functions[] = { {0, nullptr, "SubmitContext"}, {1, nullptr, "CreateReport"}, - {2, nullptr, "Unknown1"}, - {3, nullptr, "Unknown2"}, - {4, nullptr, "Unknown3"}, - {5, nullptr, "Unknown4"}, - {6, nullptr, "Unknown5"}, + {2, nullptr, "SetInitialLaunchSettingsCompletionTime"}, + {3, nullptr, "ClearInitialLaunchSettingsCompletionTime"}, + {4, nullptr, "UpdatePowerOnTime"}, + {5, nullptr, "UpdateAwakeTime"}, + {6, nullptr, "SubmitMultipleCategoryContext"}, + {7, nullptr, "UpdateApplicationLaunchTime"}, + {8, nullptr, "ClearApplicationLaunchTime"}, }; // clang-format on diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 2f15ac2a6..770590d0b 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -111,7 +111,8 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { } static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { - LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type)); + LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", + static_cast<u32>(fatal_type), error_code.raw); switch (fatal_type) { case FatalType::ErrorReportAndScreen: GenerateErrorReport(error_code, info); diff --git a/src/core/hle/service/fgm/fgm.cpp b/src/core/hle/service/fgm/fgm.cpp index 566fbf924..e461274c1 100644 --- a/src/core/hle/service/fgm/fgm.cpp +++ b/src/core/hle/service/fgm/fgm.cpp @@ -42,11 +42,11 @@ public: private: void Initialize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FGM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IRequest>(); - - LOG_DEBUG(Service_FGM, "called"); } }; diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index e32a7c48e..c6da2df43 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -8,18 +8,23 @@ #include "common/file_util.h" #include "core/core.h" #include "core/file_sys/bis_factory.h" +#include "core/file_sys/control_metadata.h" #include "core/file_sys/errors.h" #include "core/file_sys/mode.h" +#include "core/file_sys/partition_filesystem.h" +#include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/sdmc_factory.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_offset.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/fsp_ldr.h" #include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_srv.h" +#include "core/loader/loader.h" namespace Service::FileSystem { @@ -28,6 +33,10 @@ namespace Service::FileSystem { // TODO(DarkLordZach): Eventually make this configurable in settings. constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; +// A default size for normal/journal save data size if application control metadata cannot be found. +// This should be large enough to satisfy even the most extreme requirements (~4.2GB) +constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; + static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, std::string_view dir_name_) { std::string dir_name(FileUtil::SanitizePath(dir_name_)); @@ -113,6 +122,18 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str return RESULT_SUCCESS; } +ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { + const std::string sanitized_path(FileUtil::SanitizePath(path)); + auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path)); + + if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) { + // TODO(DarkLordZach): Find a better error code for this + return ResultCode(-1); + } + + return RESULT_SUCCESS; +} + ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, const std::string& dest_path_) const { std::string src_path(FileUtil::SanitizePath(src_path_)); @@ -303,25 +324,73 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, static_cast<u8>(space), save_struct.DebugInfo()); if (save_data_factory == nullptr) { - return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); + return FileSys::ERROR_ENTITY_NOT_FOUND; } return save_data_factory->Open(space, save_struct); } +ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { + LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); + + if (save_data_factory == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); +} + ResultVal<FileSys::VirtualDir> OpenSDMC() { LOG_TRACE(Service_FS, "Opening SDMC"); if (sdmc_factory == nullptr) { - return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound); + return FileSys::ERROR_SD_CARD_NOT_FOUND; } return sdmc_factory->Open(); } -std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { - return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{ - GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}); +FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) { + if (save_data_factory == nullptr) { + return {0, 0}; + } + + const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id); + + if (value.normal == 0 && value.journal == 0) { + FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE}; + + FileSys::NACP nacp; + const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp); + + if (res != Loader::ResultStatus::Success) { + FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()}; + auto [nacp_unique, discard] = pm.GetControlMetadata(); + + if (nacp_unique != nullptr) { + new_size = {nacp_unique->GetDefaultNormalSaveSize(), + nacp_unique->GetDefaultJournalSaveSize()}; + } + } else { + new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()}; + } + + WriteSaveDataSize(type, title_id, user_id, new_size); + return new_size; + } + + return value; +} + +void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value) { + if (save_data_factory != nullptr) + save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); +} + +FileSys::RegisteredCacheUnion GetUnionContents() { + return FileSys::RegisteredCacheUnion{ + {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}}; } FileSys::RegisteredCache* GetSystemNANDContents() { @@ -360,6 +429,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { return bis_factory->GetModificationLoadRoot(title_id); } +FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { + LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); + + if (bis_factory == nullptr) + return nullptr; + + return bis_factory->GetModificationDumpRoot(title_id); +} + void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { if (overwrite) { bis_factory = nullptr; @@ -373,13 +451,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { FileSys::Mode::ReadWrite); auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), FileSys::Mode::ReadWrite); + auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), + FileSys::Mode::ReadWrite); - if (bis_factory == nullptr) - bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); - if (save_data_factory == nullptr) + if (bis_factory == nullptr) { + bis_factory = + std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); + } + + if (save_data_factory == nullptr) { save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); - if (sdmc_factory == nullptr) + } + + if (sdmc_factory == nullptr) { sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); + } } void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) { diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 6ca5c5636..6fd5e7b23 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -21,9 +21,11 @@ class SDMCFactory; enum class ContentRecordType : u8; enum class Mode : u32; enum class SaveDataSpaceId : u8; +enum class SaveDataType : u8; enum class StorageId : u8; struct SaveDataDescriptor; +struct SaveDataSize; } // namespace FileSys namespace Service { @@ -45,15 +47,21 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora FileSys::ContentRecordType type); ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct); +ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); ResultVal<FileSys::VirtualDir> OpenSDMC(); -std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); +FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id); +void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value); + +FileSys::RegisteredCacheUnion GetUnionContents(); FileSys::RegisteredCache* GetSystemNANDContents(); FileSys::RegisteredCache* GetUserNANDContents(); FileSys::RegisteredCache* GetSDMCContents(); FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); +FileSys::VirtualDir GetModificationDumpRoot(u64 title_id); // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // above is called. @@ -111,6 +119,18 @@ public: ResultCode DeleteDirectoryRecursively(const std::string& path) const; /** + * Cleans the specified directory. This is similar to DeleteDirectoryRecursively, + * in that it deletes all the contents of the specified directory, however, this + * function does *not* delete the directory itself. It only deletes everything + * within it. + * + * @param path Path relative to the archive. + * + * @return Result of the operation. + */ + ResultCode CleanDirectoryRecursively(const std::string& path) const; + + /** * Rename a File specified by its path * @param src_path Source path relative to the archive * @param dest_path Destination path relative to the archive diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index c1c83a11d..74c4e583b 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -11,6 +11,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/hex_util.h" #include "common/logging/log.h" #include "common/string_util.h" #include "core/file_sys/directory.h" @@ -19,6 +20,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/savedata_factory.h" +#include "core/file_sys/system_archive/system_archive.h" #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/process.h" @@ -43,8 +45,12 @@ public: explicit IStorage(FileSys::VirtualFile backend_) : ServiceFramework("IStorage"), backend(std::move(backend_)) { static const FunctionInfo functions[] = { - {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, - {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, + {0, &IStorage::Read, "Read"}, + {1, nullptr, "Write"}, + {2, nullptr, "Flush"}, + {3, nullptr, "SetSize"}, + {4, &IStorage::GetSize, "GetSize"}, + {5, nullptr, "OperateRange"}, }; RegisterHandlers(functions); } @@ -61,13 +67,15 @@ private: // Error checking if (length < 0) { + LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + rb.Push(FileSys::ERROR_INVALID_SIZE); return; } if (offset < 0) { + LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + rb.Push(FileSys::ERROR_INVALID_OFFSET); return; } @@ -79,6 +87,15 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + + void GetSize(Kernel::HLERequestContext& ctx) { + const u64 size = backend->GetSize(); + LOG_DEBUG(Service_FS, "called, size={}", size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(size); + } }; class IFile final : public ServiceFramework<IFile> { @@ -106,13 +123,15 @@ private: // Error checking if (length < 0) { + LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + rb.Push(FileSys::ERROR_INVALID_SIZE); return; } if (offset < 0) { + LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + rb.Push(FileSys::ERROR_INVALID_OFFSET); return; } @@ -137,13 +156,15 @@ private: // Error checking if (length < 0) { + LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + rb.Push(FileSys::ERROR_INVALID_SIZE); return; } if (offset < 0) { + LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + rb.Push(FileSys::ERROR_INVALID_OFFSET); return; } @@ -179,9 +200,10 @@ private: void SetSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 size = rp.Pop<u64>(); - backend->Resize(size); LOG_DEBUG(Service_FS, "called, size={}", size); + backend->Resize(size); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -283,7 +305,7 @@ public: {10, &IFileSystem::Commit, "Commit"}, {11, nullptr, "GetFreeSpaceSize"}, {12, nullptr, "GetTotalSpaceSize"}, - {13, nullptr, "CleanDirectoryRecursively"}, + {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, {14, nullptr, "GetFileTimeStampRaw"}, {15, nullptr, "QueryEntry"}, }; @@ -353,6 +375,16 @@ public: rb.Push(backend.DeleteDirectoryRecursively(name)); } + void CleanDirectoryRecursively(Kernel::HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. Directory: {}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.CleanDirectoryRecursively(name)); + } + void RenameFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -451,7 +483,149 @@ private: VfsDirectoryServiceWrapper backend; }; +class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { +public: + explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space) + : ServiceFramework("ISaveDataInfoReader") { + static const FunctionInfo functions[] = { + {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, + }; + RegisterHandlers(functions); + + FindAllSaves(space); + } + + void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + // Calculate how many entries we can fit in the output buffer + const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo); + + // Cap at total number of entries. + const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index); + + // Determine data start and end + const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index); + const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries); + const auto range_size = static_cast<std::size_t>(std::distance(begin, end)); + + next_entry_index += actual_entries; + + // Write the data to memory + ctx.WriteBuffer(begin, range_size); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(actual_entries)); + } + +private: + static u64 stoull_be(std::string_view str) { + if (str.size() != 16) + return 0; + + const auto bytes = Common::HexStringToArray<0x8>(str); + u64 out{}; + std::memcpy(&out, bytes.data(), sizeof(u64)); + + return Common::swap64(out); + } + + void FindAllSaves(FileSys::SaveDataSpaceId space) { + const auto save_root = OpenSaveDataSpace(space); + ASSERT(save_root.Succeeded()); + + for (const auto& type : (*save_root)->GetSubdirectories()) { + if (type->GetName() == "save") { + for (const auto& save_id : type->GetSubdirectories()) { + for (const auto& user_id : save_id->GetSubdirectories()) { + const auto save_id_numeric = stoull_be(save_id->GetName()); + auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName()); + std::reverse(user_id_numeric.begin(), user_id_numeric.end()); + + if (save_id_numeric != 0) { + // System Save Data + info.emplace_back(SaveDataInfo{ + 0, + space, + FileSys::SaveDataType::SystemSaveData, + {}, + user_id_numeric, + save_id_numeric, + 0, + user_id->GetSize(), + {}, + }); + + continue; + } + + for (const auto& title_id : user_id->GetSubdirectories()) { + const auto device = + std::all_of(user_id_numeric.begin(), user_id_numeric.end(), + [](u8 val) { return val == 0; }); + info.emplace_back(SaveDataInfo{ + 0, + space, + device ? FileSys::SaveDataType::DeviceSaveData + : FileSys::SaveDataType::SaveData, + {}, + user_id_numeric, + save_id_numeric, + stoull_be(title_id->GetName()), + title_id->GetSize(), + {}, + }); + } + } + } + } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) { + // Temporary Storage + for (const auto& user_id : type->GetSubdirectories()) { + for (const auto& title_id : user_id->GetSubdirectories()) { + if (!title_id->GetFiles().empty() || + !title_id->GetSubdirectories().empty()) { + auto user_id_numeric = + Common::HexStringToArray<0x10>(user_id->GetName()); + std::reverse(user_id_numeric.begin(), user_id_numeric.end()); + + info.emplace_back(SaveDataInfo{ + 0, + space, + FileSys::SaveDataType::TemporaryStorage, + {}, + user_id_numeric, + stoull_be(type->GetName()), + stoull_be(title_id->GetName()), + title_id->GetSize(), + {}, + }); + } + } + } + } + } + } + + struct SaveDataInfo { + u64_le save_id_unknown; + FileSys::SaveDataSpaceId space; + FileSys::SaveDataType type; + INSERT_PADDING_BYTES(0x6); + std::array<u8, 0x10> user_id; + u64_le save_id; + u64_le title_id; + u64_le save_image_size; + INSERT_PADDING_BYTES(0x28); + }; + static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); + + std::vector<SaveDataInfo> info; + u64 next_entry_index = 0; +}; + FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "MountContent"}, {1, &FSP_SRV::Initialize, "Initialize"}, @@ -485,7 +659,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {58, nullptr, "ReadSaveDataFileSystemExtraData"}, {59, nullptr, "WriteSaveDataFileSystemExtraData"}, {60, nullptr, "OpenSaveDataInfoReader"}, - {61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, + {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, {62, nullptr, "OpenCacheStorageList"}, {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, {65, nullptr, "UpdateSaveDataMacForDebug"}, @@ -544,6 +718,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {1009, nullptr, "GetAndClearMemoryReportInfo"}, {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, }; + // clang-format on RegisterHandlers(functions); } @@ -561,6 +736,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) { const auto type = rp.PopRaw<FileSystemType>(); const auto title_id = rp.PopRaw<u64>(); + LOG_WARNING(Service_FS, "(STUBBED) called with type={}, title_id={:016X}", + static_cast<u8>(type), title_id); IPC::ResponseBuilder rb{ctx, 2, 0, 0}; rb.Push(ResultCode(-1)); @@ -596,13 +773,14 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>(); auto unk = rp.Pop<u32>(); LOG_INFO(Service_FS, "called with unknown={:08X}", unk); + auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); auto dir = OpenSaveData(space_id, save_struct); if (dir.Failed()) { IPC::ResponseBuilder rb{ctx, 2, 0, 0}; - rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); + rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); return; } @@ -618,12 +796,31 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) { MountSaveData(ctx); } +void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>(); + LOG_INFO(Service_FS, "called, space={}", static_cast<u8>(space)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space)); +} + void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_FS, "(STUBBED) called"); + enum class LogMode : u32 { + Off, + Log, + RedirectToSdCard, + LogToSdCard = Log | RedirectToSdCard, + }; + + // Given we always want to receive logging information, + // we always specify logging as enabled. IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(5); + rb.PushEnum(LogMode::Log); } void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { @@ -657,6 +854,15 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); if (data.Failed()) { + const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + + if (archive != nullptr) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(std::make_shared<IStorage>(archive)); + return; + } + // TODO(DarkLordZach): Find the right error code to use here LOG_ERROR(Service_FS, "could not open data storage with title_id={:016X}, storage_id={:02X}", title_id, @@ -685,7 +891,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { static_cast<u8>(storage_id), title_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); + rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); } } // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 4aa0358cb..e7abec0a3 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -25,6 +25,7 @@ private: void CreateSaveData(Kernel::HLERequestContext& ctx); void MountSaveData(Kernel::HLERequestContext& ctx); void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); + void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 3d100763f..c22357d8c 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -6,9 +6,14 @@ #include "common/common_types.h" #include "core/core_timing.h" #include "core/hle/service/hid/controllers/debug_pad.h" +#include "core/settings.h" namespace Service::HID { +constexpr s32 HID_JOYSTICK_MAX = 0x7fff; +constexpr s32 HID_JOYSTICK_MIN = -0x7fff; +enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; + Controller_DebugPad::Controller_DebugPad() = default; Controller_DebugPad::~Controller_DebugPad() = default; @@ -33,10 +38,44 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) { cur_entry.sampling_number = last_entry.sampling_number + 1; cur_entry.sampling_number2 = cur_entry.sampling_number; - // TODO(ogniK): Update debug pad states + cur_entry.attribute.connected.Assign(1); + auto& pad = cur_entry.pad_state; + + using namespace Settings::NativeButton; + pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); + pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); + pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); + pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); + pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); + pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); + pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); + pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); + pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); + pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); + pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); + pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); + pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); + pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); + + const auto [stick_l_x_f, stick_l_y_f] = + analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); + const auto [stick_r_x_f, stick_r_y_f] = + analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); + cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); + cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); + cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); + cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); std::memcpy(data, &shared_memory, sizeof(SharedMemory)); } -void Controller_DebugPad::OnLoadInputDevices() {} +void Controller_DebugPad::OnLoadInputDevices() { + std::transform(Settings::values.debug_pad_buttons.begin(), + Settings::values.debug_pad_buttons.begin() + + Settings::NativeButton::NUM_BUTTONS_HID, + buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); + std::transform(Settings::values.debug_pad_analogs.begin(), + Settings::values.debug_pad_analogs.end(), analogs.begin(), + Input::CreateDevice<Input::AnalogDevice>); +} } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 62b4f2682..68b734248 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -5,10 +5,13 @@ #pragma once #include <array> +#include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/frontend/input.h" #include "core/hle/service/hid/controllers/controller_base.h" +#include "core/settings.h" namespace Service::HID { class Controller_DebugPad final : public ControllerBase { @@ -35,11 +38,40 @@ private: }; static_assert(sizeof(AnalogStick) == 0x8); + struct PadState { + union { + u32_le raw{}; + BitField<0, 1, u32_le> a; + BitField<1, 1, u32_le> b; + BitField<2, 1, u32_le> x; + BitField<3, 1, u32_le> y; + BitField<4, 1, u32_le> l; + BitField<5, 1, u32_le> r; + BitField<6, 1, u32_le> zl; + BitField<7, 1, u32_le> zr; + BitField<8, 1, u32_le> plus; + BitField<9, 1, u32_le> minus; + BitField<10, 1, u32_le> d_left; + BitField<11, 1, u32_le> d_up; + BitField<12, 1, u32_le> d_right; + BitField<13, 1, u32_le> d_down; + }; + }; + static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size"); + + struct Attributes { + union { + u32_le raw{}; + BitField<0, 1, u32_le> connected; + }; + }; + static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); + struct PadStates { s64_le sampling_number; s64_le sampling_number2; - u32_le attribute; - u32_le button_state; + Attributes attribute; + PadState pad_state; AnalogStick r_stick; AnalogStick l_stick; }; @@ -52,5 +84,10 @@ private: }; static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); SharedMemory shared_memory{}; + + std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> + buttons; + std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> + analogs; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index ccfbce9ac..ca75adc2b 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -6,9 +6,11 @@ #include "common/common_types.h" #include "core/core_timing.h" #include "core/hle/service/hid/controllers/keyboard.h" +#include "core/settings.h" namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; +constexpr u8 KEYS_PER_BYTE = 8; Controller_Keyboard::Controller_Keyboard() = default; Controller_Keyboard::~Controller_Keyboard() = default; @@ -34,10 +36,24 @@ void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) { cur_entry.sampling_number = last_entry.sampling_number + 1; cur_entry.sampling_number2 = cur_entry.sampling_number; - // TODO(ogniK): Update keyboard states + + for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { + for (std::size_t k = 0; k < KEYS_PER_BYTE; ++k) { + cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << k); + } + } + + for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { + cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i); + } std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); } -void Controller_Keyboard::OnLoadInputDevices() {} +void Controller_Keyboard::OnLoadInputDevices() { + std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(), + keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>); + std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(), + keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>); +} } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index 493e68fce..f52775456 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -8,7 +8,9 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/frontend/input.h" #include "core/hle/service/hid/controllers/controller_base.h" +#include "core/settings.h" namespace Service::HID { class Controller_Keyboard final : public ControllerBase { @@ -46,5 +48,10 @@ private: }; static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); SharedMemory shared_memory{}; + + std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys> + keyboard_keys; + std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> + keyboard_mods; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 4e246a57d..63391dbe9 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -5,6 +5,7 @@ #include <cstring> #include "common/common_types.h" #include "core/core_timing.h" +#include "core/frontend/emu_window.h" #include "core/hle/service/hid/controllers/mouse.h" namespace Service::HID { @@ -14,7 +15,6 @@ Controller_Mouse::Controller_Mouse() = default; Controller_Mouse::~Controller_Mouse() = default; void Controller_Mouse::OnInit() {} - void Controller_Mouse::OnRelease() {} void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { @@ -34,10 +34,29 @@ void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { cur_entry.sampling_number = last_entry.sampling_number + 1; cur_entry.sampling_number2 = cur_entry.sampling_number; - // TODO(ogniK): Update mouse states + + if (Settings::values.mouse_enabled) { + const auto [px, py, sx, sy] = mouse_device->GetStatus(); + const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width); + const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height); + cur_entry.x = x; + cur_entry.y = y; + cur_entry.delta_x = x - last_entry.x; + cur_entry.delta_y = y - last_entry.y; + cur_entry.mouse_wheel_x = sx; + cur_entry.mouse_wheel_y = sy; + + for (std::size_t i = 0; i < mouse_button_devices.size(); ++i) { + cur_entry.button |= (mouse_button_devices[i]->GetStatus() << i); + } + } std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); } -void Controller_Mouse::OnLoadInputDevices() {} +void Controller_Mouse::OnLoadInputDevices() { + mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device); + std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(), + mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>); +} } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 543b0b71f..70b654d07 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -7,7 +7,9 @@ #include <array> #include "common/common_types.h" #include "common/swap.h" +#include "core/frontend/input.h" #include "core/hle/service/hid/controllers/controller_base.h" +#include "core/settings.h" namespace Service::HID { class Controller_Mouse final : public ControllerBase { @@ -35,7 +37,8 @@ private: s32_le y; s32_le delta_x; s32_le delta_y; - s32_le mouse_wheel; + s32_le mouse_wheel_x; + s32_le mouse_wheel_y; s32_le button; s32_le attribute; }; @@ -46,5 +49,9 @@ private: std::array<MouseState, 17> mouse_states; }; SharedMemory shared_memory{}; + + std::unique_ptr<Input::MouseDevice> mouse_device; + std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> + mouse_button_devices; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 4b4d1324f..75fdb861a 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -12,27 +12,20 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/input.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/hid/controllers/npad.h" #include "core/settings.h" namespace Service::HID { - -constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; -constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; -constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; -constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E; constexpr s32 HID_JOYSTICK_MAX = 0x7fff; constexpr s32 HID_JOYSTICK_MIN = -0x7fff; constexpr std::size_t NPAD_OFFSET = 0x9A00; constexpr u32 BATTERY_FULL = 2; -constexpr u32 NPAD_HANDHELD = 32; -constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? constexpr u32 MAX_NPAD_ID = 7; -constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER = - Controller_NPad::NPadControllerType::JoyDual; constexpr std::array<u32, 10> npad_id_list{ - 0, 1, 2, 3, 4, 5, 6, 7, 32, 16, + 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, }; enum class JoystickId : std::size_t { @@ -40,6 +33,66 @@ enum class JoystickId : std::size_t { Joystick_Right, }; +static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) { + switch (type) { + case Settings::ControllerType::ProController: + return Controller_NPad::NPadControllerType::ProController; + case Settings::ControllerType::DualJoycon: + return Controller_NPad::NPadControllerType::JoyDual; + case Settings::ControllerType::LeftJoycon: + return Controller_NPad::NPadControllerType::JoyLeft; + case Settings::ControllerType::RightJoycon: + return Controller_NPad::NPadControllerType::JoyRight; + default: + UNREACHABLE(); + return Controller_NPad::NPadControllerType::JoyDual; + } +} + +std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) { + switch (npad_id) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return npad_id; + case 8: + case NPAD_HANDHELD: + return 8; + case 9: + case NPAD_UNKNOWN: + return 9; + default: + UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id); + return 0; + } +} + +u32 Controller_NPad::IndexToNPad(std::size_t index) { + switch (index) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return static_cast<u32>(index); + case 8: + return NPAD_HANDHELD; + case 9: + return NPAD_UNKNOWN; + default: + UNIMPLEMENTED_MSG("Unknown npad index {}", index); + return 0; + }; +} + Controller_NPad::Controller_NPad() = default; Controller_NPad::~Controller_NPad() = default; @@ -56,22 +109,32 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { controller.joy_styles.handheld.Assign(1); controller.device_type.handheld.Assign(1); controller.pad_assignment = NPadAssignments::Dual; + controller.properties.is_vertical.Assign(1); + controller.properties.use_plus.Assign(1); + controller.properties.use_minus.Assign(1); break; case NPadControllerType::JoyDual: controller.joy_styles.joycon_dual.Assign(1); controller.device_type.joycon_left.Assign(1); controller.device_type.joycon_right.Assign(1); + controller.properties.is_vertical.Assign(1); + controller.properties.use_plus.Assign(1); + controller.properties.use_minus.Assign(1); controller.pad_assignment = NPadAssignments::Dual; break; case NPadControllerType::JoyLeft: controller.joy_styles.joycon_left.Assign(1); controller.device_type.joycon_left.Assign(1); - controller.pad_assignment = NPadAssignments::Dual; + controller.properties.is_horizontal.Assign(1); + controller.properties.use_minus.Assign(1); + controller.pad_assignment = NPadAssignments::Single; break; case NPadControllerType::JoyRight: controller.joy_styles.joycon_right.Assign(1); controller.device_type.joycon_right.Assign(1); - controller.pad_assignment = NPadAssignments::Dual; + controller.properties.is_horizontal.Assign(1); + controller.properties.use_plus.Assign(1); + controller.pad_assignment = NPadAssignments::Single; break; case NPadControllerType::Pokeball: controller.joy_styles.pokeball.Assign(1); @@ -81,6 +144,9 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { case NPadControllerType::ProController: controller.joy_styles.pro_controller.Assign(1); controller.device_type.pro_controller.Assign(1); + controller.properties.is_vertical.Assign(1); + controller.properties.use_plus.Assign(1); + controller.properties.use_minus.Assign(1); controller.pad_assignment = NPadAssignments::Single; break; } @@ -90,14 +156,12 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { controller.single_color.button_color = 0; controller.dual_color_error = ColorReadError::ReadOk; - controller.left_color.body_color = JOYCON_BODY_NEON_BLUE; - controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE; - controller.right_color.body_color = JOYCON_BODY_NEON_RED; - controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED; - - controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations - controller.properties.use_plus.Assign(1); - controller.properties.use_minus.Assign(1); + controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left; + controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left; + controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right; + controller.right_color.button_color = + Settings::values.players[controller_idx].button_color_right; + controller.battery_level[0] = BATTERY_FULL; controller.battery_level[1] = BATTERY_FULL; controller.battery_level[2] = BATTERY_FULL; @@ -105,8 +169,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { void Controller_NPad::OnInit() { auto& kernel = Core::System::GetInstance().Kernel(); - styleset_changed_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); + styleset_changed_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); if (!IsControllerActivated()) { return; @@ -121,26 +185,109 @@ void Controller_NPad::OnInit() { style.pro_controller.Assign(1); style.pokeball.Assign(1); } + + std::transform( + Settings::values.players.begin(), Settings::values.players.end(), + connected_controllers.begin(), [](const Settings::PlayerInput& player) { + return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected}; + }); + + std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8, + [](const ControllerHolder& holder) { return holder.is_connected; }); + + // Account for handheld + if (connected_controllers[8].is_connected) + connected_controllers[8].type = NPadControllerType::Handheld; + + supported_npad_id_types.resize(npad_id_list.size()); + std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), + npad_id_list.size() * sizeof(u32)); + + // Add a default dual joycon controller if none are present. if (std::none_of(connected_controllers.begin(), connected_controllers.end(), [](const ControllerHolder& controller) { return controller.is_connected; })) { supported_npad_id_types.resize(npad_id_list.size()); std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), npad_id_list.size() * sizeof(u32)); - AddNewController(PREFERRED_CONTROLLER); + AddNewController(NPadControllerType::JoyDual); + } + + for (std::size_t i = 0; i < connected_controllers.size(); ++i) { + const auto& controller = connected_controllers[i]; + if (controller.is_connected) { + AddNewControllerAt(controller.type, IndexToNPad(i)); + } } } void Controller_NPad::OnLoadInputDevices() { - std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, - Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, - buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); - std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, - Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END, - sticks.begin(), Input::CreateDevice<Input::AnalogDevice>); + const auto& players = Settings::values.players; + for (std::size_t i = 0; i < players.size(); ++i) { + std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, + players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END, + buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>); + std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, + players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, + sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); + } } void Controller_NPad::OnRelease() {} +void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { + const auto controller_idx = NPadIdToIndex(npad_id); + const auto controller_type = connected_controllers[controller_idx].type; + if (!connected_controllers[controller_idx].is_connected) { + return; + } + auto& pad_state = npad_pad_states[controller_idx].pad_states; + auto& lstick_entry = npad_pad_states[controller_idx].l_stick; + auto& rstick_entry = npad_pad_states[controller_idx].r_stick; + const auto& button_state = buttons[controller_idx]; + const auto& analog_state = sticks[controller_idx]; + + using namespace Settings::NativeButton; + pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus()); + + pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); + + pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); + + pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus()); + + pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); + + const auto [stick_l_x_f, stick_l_y_f] = + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); + const auto [stick_r_x_f, stick_r_y_f] = + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); + lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); + lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); + rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); + rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); +} + void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { if (!IsControllerActivated()) return; @@ -176,97 +323,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { continue; } - - // Pad states - ControllerPadState pad_state{}; - using namespace Settings::NativeButton; - pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); - - pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); - - pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); - - pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus()); - - pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus()); - - AnalogPosition lstick_entry{}; - AnalogPosition rstick_entry{}; - - const auto [stick_l_x_f, stick_l_y_f] = - sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); - const auto [stick_r_x_f, stick_r_y_f] = - sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); - lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); - lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); - rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); - rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); - - if (controller_type == NPadControllerType::JoyLeft || - controller_type == NPadControllerType::JoyRight) { - if (npad.properties.is_horizontal) { - ControllerPadState state{}; - AnalogPosition temp_lstick_entry{}; - AnalogPosition temp_rstick_entry{}; - if (controller_type == NPadControllerType::JoyLeft) { - state.d_down.Assign(pad_state.d_left.Value()); - state.d_left.Assign(pad_state.d_up.Value()); - state.d_right.Assign(pad_state.d_down.Value()); - state.d_up.Assign(pad_state.d_right.Value()); - state.l.Assign(pad_state.l.Value() | pad_state.sl.Value()); - state.r.Assign(pad_state.r.Value() | pad_state.sr.Value()); - - state.zl.Assign(pad_state.zl.Value()); - state.plus.Assign(pad_state.minus.Value()); - - temp_lstick_entry = lstick_entry; - temp_rstick_entry = rstick_entry; - std::swap(temp_lstick_entry.x, temp_lstick_entry.y); - std::swap(temp_rstick_entry.x, temp_rstick_entry.y); - temp_lstick_entry.y *= -1; - } else if (controller_type == NPadControllerType::JoyRight) { - state.x.Assign(pad_state.a.Value()); - state.a.Assign(pad_state.b.Value()); - state.b.Assign(pad_state.y.Value()); - state.y.Assign(pad_state.b.Value()); - - state.l.Assign(pad_state.l.Value() | pad_state.sl.Value()); - state.r.Assign(pad_state.r.Value() | pad_state.sr.Value()); - state.zr.Assign(pad_state.zr.Value()); - state.plus.Assign(pad_state.plus.Value()); - - temp_lstick_entry = lstick_entry; - temp_rstick_entry = rstick_entry; - std::swap(temp_lstick_entry.x, temp_lstick_entry.y); - std::swap(temp_rstick_entry.x, temp_rstick_entry.y); - temp_rstick_entry.x *= -1; - } - pad_state.raw = state.raw; - lstick_entry = temp_lstick_entry; - rstick_entry = temp_rstick_entry; - } - } + const u32 npad_index = static_cast<u32>(i); + RequestPadStateUpdate(npad_index); + auto& pad_state = npad_pad_states[npad_index]; auto& main_controller = npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; @@ -280,21 +339,19 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index]; auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; - if (hold_type == NpadHoldType::Horizontal) { - // TODO(ogniK): Remap buttons for different orientations - } libnx_entry.connection_status.raw = 0; switch (controller_type) { case NPadControllerType::Handheld: handheld_entry.connection_status.raw = 0; - handheld_entry.connection_status.IsConnected.Assign(1); - if (!Settings::values.use_docked_mode) { - handheld_entry.connection_status.IsWired.Assign(1); - } - handheld_entry.pad_states.raw = pad_state.raw; - handheld_entry.l_stick = lstick_entry; - handheld_entry.r_stick = rstick_entry; + handheld_entry.connection_status.IsWired.Assign(1); + handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); + handheld_entry.connection_status.IsRightJoyConnected.Assign(1); + handheld_entry.connection_status.IsLeftJoyWired.Assign(1); + handheld_entry.connection_status.IsRightJoyWired.Assign(1); + handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; + handheld_entry.pad.l_stick = pad_state.l_stick; + handheld_entry.pad.r_stick = pad_state.r_stick; break; case NPadControllerType::JoyDual: dual_entry.connection_status.raw = 0; @@ -307,24 +364,25 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { libnx_entry.connection_status.IsRightJoyConnected.Assign(1); libnx_entry.connection_status.IsConnected.Assign(1); - dual_entry.pad_states.raw = pad_state.raw; - dual_entry.l_stick = lstick_entry; - dual_entry.r_stick = rstick_entry; + dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; + dual_entry.pad.l_stick = pad_state.l_stick; + dual_entry.pad.r_stick = pad_state.r_stick; + break; case NPadControllerType::JoyLeft: left_entry.connection_status.raw = 0; left_entry.connection_status.IsConnected.Assign(1); - left_entry.pad_states.raw = pad_state.raw; - left_entry.l_stick = lstick_entry; - left_entry.r_stick = rstick_entry; + left_entry.pad.pad_states.raw = pad_state.pad_states.raw; + left_entry.pad.l_stick = pad_state.l_stick; + left_entry.pad.r_stick = pad_state.r_stick; break; case NPadControllerType::JoyRight: right_entry.connection_status.raw = 0; right_entry.connection_status.IsConnected.Assign(1); - right_entry.pad_states.raw = pad_state.raw; - right_entry.l_stick = lstick_entry; - right_entry.r_stick = rstick_entry; + right_entry.pad.pad_states.raw = pad_state.pad_states.raw; + right_entry.pad.l_stick = pad_state.l_stick; + right_entry.pad.r_stick = pad_state.r_stick; break; case NPadControllerType::Pokeball: pokeball_entry.connection_status.raw = 0; @@ -332,30 +390,30 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { pokeball_entry.connection_status.IsConnected.Assign(1); pokeball_entry.connection_status.IsWired.Assign(1); - pokeball_entry.pad_states.raw = pad_state.raw; - pokeball_entry.l_stick = lstick_entry; - pokeball_entry.r_stick = rstick_entry; + pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; + pokeball_entry.pad.l_stick = pad_state.l_stick; + pokeball_entry.pad.r_stick = pad_state.r_stick; break; case NPadControllerType::ProController: main_controller.connection_status.raw = 0; main_controller.connection_status.IsConnected.Assign(1); main_controller.connection_status.IsWired.Assign(1); - main_controller.pad_states.raw = pad_state.raw; - main_controller.l_stick = lstick_entry; - main_controller.r_stick = rstick_entry; + main_controller.pad.pad_states.raw = pad_state.pad_states.raw; + main_controller.pad.l_stick = pad_state.l_stick; + main_controller.pad.r_stick = pad_state.r_stick; break; } // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate // any controllers. - libnx_entry.pad_states.raw = pad_state.raw; - libnx_entry.l_stick = lstick_entry; - libnx_entry.r_stick = rstick_entry; + libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw; + libnx_entry.pad.l_stick = pad_state.l_stick; + libnx_entry.pad.r_stick = pad_state.r_stick; } std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), shared_memory_entries.size() * sizeof(NPadEntry)); -} // namespace Service::HID +} void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { style.raw = style_set.raw; @@ -370,14 +428,29 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { supported_npad_id_types.clear(); supported_npad_id_types.resize(length / sizeof(u32)); std::memcpy(supported_npad_id_types.data(), data, length); + bool had_controller_update = false; for (std::size_t i = 0; i < connected_controllers.size(); i++) { auto& controller = connected_controllers[i]; if (!controller.is_connected) { continue; } - if (!IsControllerSupported(PREFERRED_CONTROLLER)) { - controller.type = DecideBestController(PREFERRED_CONTROLLER); - InitNewlyAddedControler(i); + const auto requested_controller = + i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type) + : NPadControllerType::Handheld; + if (!IsControllerSupported(requested_controller)) { + const auto is_handheld = requested_controller == NPadControllerType::Handheld; + if (is_handheld) { + controller.type = NPadControllerType::None; + controller.is_connected = false; + AddNewController(requested_controller); + } else { + controller.type = requested_controller; + InitNewlyAddedControler(i); + } + had_controller_update = true; + } + if (had_controller_update) { + styleset_changed_event.writable->Signal(); } } } @@ -392,48 +465,49 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const { } void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { + styleset_changed_event.writable->Signal(); hold_type = joy_hold_type; } + Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { return hold_type; } void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { - ASSERT(npad_id < shared_memory_entries.size()); - shared_memory_entries[npad_id].pad_assignment = assignment_mode; + const std::size_t npad_index = NPadIdToIndex(npad_id); + ASSERT(npad_index < shared_memory_entries.size()); + shared_memory_entries[npad_index].pad_assignment = assignment_mode; } void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, const std::vector<Vibration>& vibrations) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + if (!can_controllers_vibrate) { return; } for (std::size_t i = 0; i < controller_ids.size(); i++) { - std::size_t controller_pos = i; - // Handheld controller conversion - if (controller_pos == NPAD_HANDHELD) { - controller_pos = 8; - } - // Unknown controller conversion - if (controller_pos == NPAD_UNKNOWN) { - controller_pos = 9; - } + std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i)); if (connected_controllers[controller_pos].is_connected) { // TODO(ogniK): Vibrate the physical controller } } - LOG_WARNING(Service_HID, "(STUBBED) called"); last_processed_vibration = vibrations.back(); } -Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const { - return styleset_changed_event; +Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent() const { + // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should + // be signalled at least once, and signaled after a new controller is connected? + styleset_changed_event.writable->Signal(); + return styleset_changed_event.readable; } Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { return last_processed_vibration; } + void Controller_NPad::AddNewController(NPadControllerType controller) { + controller = DecideBestController(controller); if (controller == NPadControllerType::Handheld) { connected_controllers[8] = {controller, true}; InitNewlyAddedControler(8); @@ -451,16 +525,54 @@ void Controller_NPad::AddNewController(NPadControllerType controller) { InitNewlyAddedControler(controller_id); } -void Controller_NPad::ConnectNPad(u32 npad_id) { - if (npad_id >= connected_controllers.size()) +void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) { + controller = DecideBestController(controller); + if (controller == NPadControllerType::Handheld) { + connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true}; + InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD)); return; - connected_controllers[npad_id].is_connected = true; + } + + connected_controllers[NPadIdToIndex(npad_id)] = {controller, true}; + InitNewlyAddedControler(NPadIdToIndex(npad_id)); +} + +void Controller_NPad::ConnectNPad(u32 npad_id) { + connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; } void Controller_NPad::DisconnectNPad(u32 npad_id) { - if (npad_id >= connected_controllers.size()) - return; - connected_controllers[npad_id].is_connected = false; + connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; +} + +bool Controller_NPad::IsControllerSupported(NPadControllerType controller) { + if (controller == NPadControllerType::Handheld) { + // Handheld is not even a supported type, lets stop here + if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), + NPAD_HANDHELD) == supported_npad_id_types.end()) { + return false; + } + // Handheld should not be supported in docked mode + if (Settings::values.use_docked_mode) { + return false; + } + } + switch (controller) { + case NPadControllerType::ProController: + return style.pro_controller; + case NPadControllerType::Handheld: + return style.handheld; + case NPadControllerType::JoyDual: + return style.joycon_dual; + case NPadControllerType::JoyLeft: + return style.joycon_left; + case NPadControllerType::JoyRight: + return style.joycon_right; + case NPadControllerType::Pokeball: + return style.pokeball; + default: + return false; + } } Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { @@ -494,6 +606,36 @@ void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { can_controllers_vibrate = can_vibrate; } +void Controller_NPad::ClearAllConnectedControllers() { + for (auto& controller : connected_controllers) { + if (controller.is_connected && controller.type != NPadControllerType::None) { + controller.type = NPadControllerType::None; + controller.is_connected = false; + } + } +} +void Controller_NPad::DisconnectAllConnectedControllers() { + std::for_each(connected_controllers.begin(), connected_controllers.end(), + [](ControllerHolder& controller) { controller.is_connected = false; }); +} + +void Controller_NPad::ConnectAllDisconnectedControllers() { + std::for_each(connected_controllers.begin(), connected_controllers.end(), + [](ControllerHolder& controller) { + if (controller.type != NPadControllerType::None && !controller.is_connected) { + controller.is_connected = false; + } + }); +} + +void Controller_NPad::ClearAllControllers() { + std::for_each(connected_controllers.begin(), connected_controllers.end(), + [](ControllerHolder& controller) { + controller.type = NPadControllerType::None; + controller.is_connected = false; + }); +} + bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { const bool support_handheld = std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) != diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index ac86985ff..29851f16a 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -5,13 +5,19 @@ #pragma once #include <array> +#include "common/bit_field.h" #include "common/common_types.h" #include "core/frontend/input.h" +#include "core/hle/kernel/object.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/hid/controllers/controller_base.h" #include "core/settings.h" namespace Service::HID { +constexpr u32 NPAD_HANDHELD = 32; +constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? + class Controller_NPad final : public ControllerBase { public: Controller_NPad(); @@ -75,9 +81,9 @@ public: struct LedPattern { explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { position1.Assign(light1); - position1.Assign(light2); - position1.Assign(light3); - position1.Assign(light4); + position2.Assign(light2); + position3.Assign(light3); + position4.Assign(light4); } union { u64 raw{}; @@ -103,15 +109,23 @@ public: void VibrateController(const std::vector<u32>& controller_ids, const std::vector<Vibration>& vibrations); - Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const; + Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent() const; Vibration GetLastVibration() const; void AddNewController(NPadControllerType controller); + void AddNewControllerAt(NPadControllerType controller, u32 npad_id); void ConnectNPad(u32 npad_id); void DisconnectNPad(u32 npad_id); LedPattern GetLedPattern(u32 npad_id); void SetVibrationEnabled(bool can_vibrate); + void ClearAllConnectedControllers(); + void DisconnectAllConnectedControllers(); + void ConnectAllDisconnectedControllers(); + void ClearAllControllers(); + + static std::size_t NPadIdToIndex(u32 npad_id); + static u32 IndexToNPad(std::size_t index); private: struct CommonHeader { @@ -164,8 +178,11 @@ private: BitField<23, 1, u64_le> r_stick_down; // Not always active? - BitField<24, 1, u64_le> sl; - BitField<25, 1, u64_le> sr; + BitField<24, 1, u64_le> left_sl; + BitField<25, 1, u64_le> left_sr; + + BitField<26, 1, u64_le> right_sl; + BitField<27, 1, u64_le> right_sr; }; }; static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); @@ -189,12 +206,17 @@ private: }; static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); - struct GenericStates { - s64_le timestamp; - s64_le timestamp2; + struct ControllerPad { ControllerPadState pad_states; AnalogPosition l_stick; AnalogPosition r_stick; + }; + static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size"); + + struct GenericStates { + s64_le timestamp; + s64_le timestamp2; + ControllerPad pad; ConnectionState connection_status; }; static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); @@ -266,18 +288,23 @@ private: static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); struct ControllerHolder { - Controller_NPad::NPadControllerType type; + NPadControllerType type; bool is_connected; }; NPadType style{}; std::array<NPadEntry, 10> shared_memory_entries{}; - std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> + std::array< + std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, + 10> buttons; - std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; + std::array< + std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, + 10> + sticks; std::vector<u32> supported_npad_id_types{}; NpadHoldType hold_type{NpadHoldType::Vertical}; - Kernel::SharedPtr<Kernel::Event> styleset_changed_event; + Kernel::EventPair styleset_changed_event; Vibration last_processed_vibration{}; std::array<ControllerHolder, 10> connected_controllers{}; bool can_controllers_vibrate{true}; @@ -285,5 +312,8 @@ private: void InitNewlyAddedControler(std::size_t controller_idx); bool IsControllerSupported(NPadControllerType controller) const; NPadControllerType DecideBestController(NPadControllerType priority) const; + void RequestPadStateUpdate(u32 npad_id); + std::array<ControllerPad, 10> npad_pad_states{}; + bool IsControllerSupported(NPadControllerType controller); }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 43efef803..f666b1bd8 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -41,16 +41,17 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { const auto [x, y, pressed] = touch_device->GetStatus(); auto& touch_entry = cur_entry.states[0]; - if (pressed) { + touch_entry.attribute.raw = 0; + if (pressed && Settings::values.touchscreen.enabled) { touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); - touch_entry.diameter_x = 15; - touch_entry.diameter_y = 15; - touch_entry.rotation_angle = 0; + touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; + touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; + touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; const u64 tick = CoreTiming::GetTicks(); touch_entry.delta_time = tick - last_touch; last_touch = tick; - touch_entry.finger = 0; + touch_entry.finger = Settings::values.touchscreen.finger; cur_entry.entry_count = 1; } else { cur_entry.entry_count = 0; @@ -60,6 +61,6 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { } void Controller_Touchscreen::OnLoadInputDevices() { - touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device); + touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device); } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index e5db6e6ba..94cd0eba9 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -4,6 +4,7 @@ #pragma once +#include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" @@ -29,9 +30,18 @@ public: void OnLoadInputDevices() override; private: + struct Attributes { + union { + u32 raw{}; + BitField<0, 1, u32_le> start_touch; + BitField<1, 1, u32_le> end_touch; + }; + }; + static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); + struct TouchState { u64_le delta_time; - u32_le attribute; + Attributes attribute; u32_le finger; u32_le x; u32_le y; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index a9aa9ec78..268409257 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -13,8 +13,9 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/shared_memory.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/xcd.h" @@ -34,8 +35,8 @@ namespace Service::HID { // Updating period for each HID device. -// TODO(shinyquagsire23): These need better values. -constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; +// TODO(ogniK): Find actual polling rate of hid +constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66; constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; @@ -96,6 +97,8 @@ public: // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); + + ReloadInputDevices(); } void ActivateController(HidController controller) { @@ -122,10 +125,11 @@ public: private: void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(shared_mem); - LOG_DEBUG(Service_HID, "called"); } void UpdateControllers(u64 userdata, int cycles_late) { @@ -161,9 +165,10 @@ public: private: void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); } }; @@ -284,10 +289,10 @@ public: {519, nullptr, "GetPalmaOperationResult"}, {520, nullptr, "ReadPalmaPlayLog"}, {521, nullptr, "ResetPalmaPlayLog"}, - {522, nullptr, "SetIsPalmaAllConnectable"}, + {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, {523, nullptr, "SetIsPalmaPairedConnectable"}, {524, nullptr, "PairPalma"}, - {525, nullptr, "SetPalmaBoostMode"}, + {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, {1000, nullptr, "SetNpadCommunicationMode"}, {1001, nullptr, "GetNpadCommunicationMode"}, }; @@ -301,6 +306,11 @@ private: std::shared_ptr<IAppletResource> applet_resource; void CreateAppletResource(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + if (applet_resource == nullptr) { applet_resource = std::make_shared<IAppletResource>(); } @@ -308,206 +318,306 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAppletResource>(applet_resource); - LOG_DEBUG(Service_HID, "called"); } void ActivateXpad(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto basic_xpad_id{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", + basic_xpad_id, applet_resource_user_id); + applet_resource->ActivateController(HidController::XPad); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void ActivateDebugPad(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + applet_resource->ActivateController(HidController::DebugPad); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + applet_resource->ActivateController(HidController::Touchscreen); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void ActivateMouse(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + applet_resource->ActivateController(HidController::Mouse); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void ActivateKeyboard(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + applet_resource->ActivateController(HidController::Keyboard); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void ActivateGesture(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto unknown{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, + applet_resource_user_id); + applet_resource->ActivateController(HidController::Gesture); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { // Should have no effect with how our npad sets up the data + IPC::RequestParser rp{ctx}; + const auto unknown{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, + applet_resource_user_id); + applet_resource->ActivateController(HidController::NPad); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto handle = rp.PopRaw<u32>(); + const auto handle{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); } void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop<u32>()}; + const auto drift_mode{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, handle={}, drift_mode={}, applet_resource_user_id={}", + handle, drift_mode, applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); } void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); // TODO (Hexagon12): Properly implement reading gyroscope values from controllers. rb.Push(true); - LOG_WARNING(Service_HID, "(STUBBED) called"); } void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto supported_styleset = rp.PopRaw<u32>(); + const auto supported_styleset{rp.Pop<u32>()}; + + LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset); + applet_resource->GetController<Controller_NPad>(HidController::NPad) .SetSupportedStyleSet({supported_styleset}); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_DEBUG(Service_HID, "called"); } void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(controller.GetSupportedStyleSet().raw); - LOG_DEBUG(Service_HID, "called"); } void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + applet_resource->GetController<Controller_NPad>(HidController::NPad) .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void ActivateNpad(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); applet_resource->ActivateController(HidController::NPad); - LOG_DEBUG(Service_HID, "called"); } void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto npad_id = rp.PopRaw<u32>(); + const auto npad_id{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto unknown{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", + npad_id, applet_resource_user_id, unknown); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) .GetStyleSetChangedEvent()); - LOG_DEBUG(Service_HID, "called"); } void DisconnectNpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto npad_id = rp.PopRaw<u32>(); + const auto npad_id{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, + applet_resource_user_id); + applet_resource->GetController<Controller_NPad>(HidController::NPad) .DisconnectNPad(npad_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto npad_id = rp.PopRaw<u32>(); + const auto npad_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad) .GetLedPattern(npad_id) .raw); - LOG_DEBUG(Service_HID, "called"); } void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { - auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); IPC::RequestParser rp{ctx}; - const auto hold_type = rp.PopRaw<u64>(); + const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto hold_type{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", + applet_resource_user_id, hold_type); + + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type}); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + const auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(static_cast<u64>(controller.GetHoldType())); - LOG_DEBUG(Service_HID, "called"); } void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto npad_id = rp.PopRaw<u32>(); + const auto npad_id{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + npad_id, applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); } void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + applet_resource->GetController<Controller_NPad>(HidController::NPad) .SetVibrationEnabled(true); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + applet_resource->GetController<Controller_NPad>(HidController::NPad) .SetVibrationEnabled(false); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void SendVibrationValue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto controller_id = rp.PopRaw<u32>(); - const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>(); + const auto controller_id{rp.Pop<u32>()}; + const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", + controller_id, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); applet_resource->GetController<Controller_NPad>(HidController::NPad) .VibrateController({controller_id}, {vibration_values}); - LOG_DEBUG(Service_HID, "called"); } void SendVibrationValues(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + const auto controllers = ctx.ReadBuffer(0); const auto vibrations = ctx.ReadBuffer(1); @@ -525,74 +635,134 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto controller_id{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", + controller_id, applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); rb.PushRaw<Controller_NPad::Vibration>( applet_resource->GetController<Controller_NPad>(HidController::NPad) .GetLastVibration()); - LOG_DEBUG(Service_HID, "called"); } void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id = rp.PopRaw<u32>(); + const auto npad_id{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, + applet_resource_user_id); + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HID, "called"); } void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto unknown_1{rp.Pop<u32>()}; + const auto unknown_2{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", + unknown_1, unknown_2, applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); } void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto mode = rp.PopRaw<u32>(); + const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto mode{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}", + applet_resource_user_id, mode); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); } void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(1); rb.Push<u32>(0); - LOG_DEBUG(Service_HID, "called"); } void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IActiveVibrationDeviceList>(); - LOG_DEBUG(Service_HID, "called"); } void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); } void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); } void StopSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop<u32>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}", handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + const auto unknown{rp.Pop<u32>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, unknown={}", + applet_resource_user_id, unknown); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto unknown{rp.Pop<u32>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}", unknown); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_HID, "(STUBBED) called"); } }; diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 872e3c344..3c7f8b1ee 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -44,115 +44,133 @@ IRS::IRS() : ServiceFramework{"irs"} { } void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_IRS, "called"); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(shared_mem); - LOG_DEBUG(Service_IRS, "called"); } void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 5}; rb.Push(RESULT_SUCCESS); rb.PushRaw<u64>(CoreTiming::GetTicks()); rb.PushRaw<u32>(0); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.PushRaw<u32>(device_handle); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IRS, "(STUBBED) called"); } IRS::~IRS() = default; diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index 164c57e18..e8f9f2d29 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp @@ -55,29 +55,29 @@ public: private: void EnableVrMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); vr_mode_enabled = true; - - LOG_DEBUG(Service_LBL, "called"); } void DisableVrMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); vr_mode_enabled = false; - - LOG_DEBUG(Service_LBL, "called"); } void IsVrModeEnabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(vr_mode_enabled); - - LOG_DEBUG(Service_LBL, "called"); } bool vr_mode_enabled = false; diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 167f2c66a..e250595e3 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -44,11 +44,11 @@ public: } void CreateMonitorService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LDN, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IMonitorService>(); - - LOG_DEBUG(Service_LDN, "called"); } }; @@ -104,11 +104,11 @@ public: } void CreateSystemLocalCommunicationService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LDN, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILocalCommunicationService>("ISystemLocalCommunicationService"); - - LOG_DEBUG(Service_LDN, "called"); } }; @@ -125,11 +125,11 @@ public: } void CreateUserLocalCommunicationService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LDN, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILocalCommunicationService>("IUserLocalCommunicationService"); - - LOG_DEBUG(Service_LDN, "called"); } }; diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index d607d985e..9df7ac50f 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -4,7 +4,10 @@ #include <memory> #include <fmt/format.h> +#include <mbedtls/sha256.h> +#include "common/alignment.h" +#include "common/hex_util.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/process.h" #include "core/hle/service/ldr/ldr.h" @@ -13,6 +16,21 @@ namespace Service::LDR { +constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51}; +constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52}; +constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53}; +constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54}; +constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55}; +constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56}; +constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57}; +constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81}; +constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82}; +constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84}; +constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85}; +constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; + +constexpr u64 MAXIMUM_LOADED_RO = 0x40; + class DebugMonitor final : public ServiceFramework<DebugMonitor> { public: explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} { @@ -64,9 +82,9 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, &RelocatableObject::LoadNro, "LoadNro"}, - {1, nullptr, "UnloadNro"}, + {1, &RelocatableObject::UnloadNro, "UnloadNro"}, {2, &RelocatableObject::LoadNrr, "LoadNrr"}, - {3, nullptr, "UnloadNrr"}, + {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, {4, &RelocatableObject::Initialize, "Initialize"}, }; // clang-format on @@ -75,9 +93,126 @@ public: } void LoadNrr(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rp.Skip(2, false); + const VAddr nrr_addr{rp.Pop<VAddr>()}; + const u64 nrr_size{rp.Pop<u64>()}; + LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}, nrr_size={:016X}", nrr_addr, + nrr_size); + + if (!initialized) { + LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NOT_INITIALIZED); + return; + } + + if (nrr.size() >= MAXIMUM_LOADED_RO) { + LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs " + "(0x40)! Failing..."); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_MAXIMUM_NRR); + return; + } + + // NRR Address does not fall on 0x1000 byte boundary + if (!Common::Is4KBAligned(nrr_addr)) { + LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ALIGNMENT); + return; + } + + // NRR Size is zero or causes overflow + if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) { + LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", + nrr_addr, nrr_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_SIZE); + return; + } + // Read NRR data from memory + std::vector<u8> nrr_data(nrr_size); + Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size); + NRRHeader header; + std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); + + if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) { + LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_NRR); + return; + } + + if (header.size != nrr_size) { + LOG_ERROR(Service_LDR, + "NRR header reported size did not match LoadNrr parameter size! " + "(header_size={:016X}, loadnrr_size={:016X})", + header.size, nrr_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_SIZE); + return; + } + + if (Core::CurrentProcess()->GetTitleID() != header.title_id) { + LOG_ERROR(Service_LDR, + "Attempting to load NRR with title ID other than current process. (actual " + "{:016X})!", + header.title_id); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_NRR); + return; + } + + std::vector<SHA256Hash> hashes; + + // Copy all hashes in the NRR (specified by hash count/hash offset) into vector. + for (std::size_t i = header.hash_offset; + i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) { + SHA256Hash hash; + std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash)); + hashes.emplace_back(hash); + } + + nrr.insert_or_assign(nrr_addr, std::move(hashes)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void UnloadNrr(Kernel::HLERequestContext& ctx) { + if (!initialized) { + LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NOT_INITIALIZED); + return; + } + + IPC::RequestParser rp{ctx}; + rp.Skip(2, false); + const auto nrr_addr{rp.Pop<VAddr>()}; + LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}", nrr_addr); + + if (!Common::Is4KBAligned(nrr_addr)) { + LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ALIGNMENT); + return; + } + + const auto iter = nrr.find(nrr_addr); + if (iter == nrr.end()) { + LOG_ERROR(Service_LDR, + "Attempting to unload NRR which has not been loaded! (addr={:016X})", + nrr_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_NRR_ADDRESS); + return; + } + + nrr.erase(iter); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_LDR, "(STUBBED) called"); } void LoadNro(Kernel::HLERequestContext& ctx) { @@ -87,33 +222,260 @@ public: const u64 nro_size{rp.Pop<u64>()}; const VAddr bss_addr{rp.Pop<VAddr>()}; const u64 bss_size{rp.Pop<u64>()}; + LOG_DEBUG( + Service_LDR, + "called with nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, bss_size={:016X}", + nro_addr, nro_size, bss_addr, bss_size); + + if (!initialized) { + LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NOT_INITIALIZED); + return; + } + + if (nro.size() >= MAXIMUM_LOADED_RO) { + LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs " + "(0x40)! Failing..."); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_MAXIMUM_NRO); + return; + } + + // NRO Address does not fall on 0x1000 byte boundary + if (!Common::Is4KBAligned(nro_addr)) { + LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ALIGNMENT); + return; + } + + // NRO Size or BSS Size is zero or causes overflow + const auto nro_size_valid = + nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size); + const auto bss_size_valid = + nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr); + + if (!nro_size_valid || !bss_size_valid) { + LOG_ERROR(Service_LDR, + "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " + "bss_address={:016X}, bss_size={:016X})", + nro_addr, nro_size, bss_addr, bss_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_SIZE); + return; + } // Read NRO data from memory std::vector<u8> nro_data(nro_size); Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); + SHA256Hash hash{}; + mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); + + // NRO Hash is already loaded + if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) { + return info.second.hash == hash; + })) { + LOG_ERROR(Service_LDR, "NRO is already loaded!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_ALREADY_LOADED); + return; + } + + // NRO Hash is not in any loaded NRR + if (!IsValidNROHash(hash)) { + LOG_ERROR(Service_LDR, + "NRO hash is not present in any currently loaded NRRs (hash={})!", + Common::HexArrayToString(hash)); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_MISSING_NRR_HASH); + return; + } + + NROHeader header; + std::memcpy(&header, nro_data.data(), sizeof(NROHeader)); + + if (!IsValidNRO(header, nro_size, bss_size)) { + LOG_ERROR(Service_LDR, "NRO was invalid!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_NRO); + return; + } + // Load NRO as new executable module - const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; - Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); + auto* process = Core::CurrentProcess(); + auto& vm_manager = process->VMManager(); + auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size); + + if (!map_address.Succeeded() || + *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) { + + LOG_ERROR(Service_LDR, + "General error while allocation memory or no available memory to allocate!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_MEMORY_STATE); + return; + } + + ASSERT(vm_manager + .MirrorMemory(*map_address, nro_addr, nro_size, + Kernel::MemoryState::ModuleCodeStatic) + .IsSuccess()); + ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); + + if (bss_size > 0) { + ASSERT(vm_manager + .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, + Kernel::MemoryState::ModuleCodeStatic) + .IsSuccess()); + ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); + } - // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. - // It is currently missing: - // - Signature checks with LoadNRR - // - Checking if a module has already been loaded - // - Using/validating BSS, etc. params (these are used from NRO header instead) - // - Error checking - // - ...Probably other things + vm_manager.ReprotectRange(*map_address, header.text_size, + Kernel::VMAPermission::ReadExecute); + vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size, + Kernel::VMAPermission::Read); + vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, + Kernel::VMAPermission::ReadWrite); + + Core::System::GetInstance().InvalidateCpuInstructionCaches(); + + nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size}); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(addr); - LOG_WARNING(Service_LDR, "(STUBBED) called"); + rb.Push(*map_address); } - void Initialize(Kernel::HLERequestContext& ctx) { + void UnloadNro(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rp.Skip(2, false); + const VAddr mapped_addr{rp.PopRaw<VAddr>()}; + const VAddr heap_addr{rp.PopRaw<VAddr>()}; + LOG_DEBUG(Service_LDR, "called with mapped_addr={:016X}, heap_addr={:016X}", mapped_addr, + heap_addr); + + if (!initialized) { + LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NOT_INITIALIZED); + return; + } + + if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) { + LOG_ERROR(Service_LDR, + "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, " + "bss_addr={:016X})!", + mapped_addr, heap_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ALIGNMENT); + return; + } + + const auto iter = nro.find(mapped_addr); + if (iter == nro.end()) { + LOG_ERROR(Service_LDR, + "The NRO attempting to unmap was not mapped or has an invalid address " + "(actual {:016X})!", + mapped_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_NRO_ADDRESS); + return; + } + + auto& vm_manager = Core::CurrentProcess()->VMManager(); + const auto& nro_size = iter->second.size; + + ASSERT(vm_manager + .MirrorMemory(heap_addr, mapped_addr, nro_size, + Kernel::MemoryState::ModuleCodeStatic) + .IsSuccess()); + ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess()); + + Core::System::GetInstance().InvalidateCpuInstructionCaches(); + + nro.erase(iter); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); + } + + void Initialize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_LDR, "(STUBBED) called"); + + initialized = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + +private: + using SHA256Hash = std::array<u8, 0x20>; + + struct NROHeader { + INSERT_PADDING_WORDS(1); + u32_le mod_offset; + INSERT_PADDING_WORDS(2); + u32_le magic; + u32_le version; + u32_le nro_size; + u32_le flags; + u32_le text_offset; + u32_le text_size; + u32_le ro_offset; + u32_le ro_size; + u32_le rw_offset; + u32_le rw_size; + u32_le bss_size; + INSERT_PADDING_WORDS(1); + std::array<u8, 0x20> build_id; + INSERT_PADDING_BYTES(0x20); + }; + static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); + + struct NRRHeader { + u32_le magic; + INSERT_PADDING_BYTES(12); + u64_le title_id_mask; + u64_le title_id_pattern; + INSERT_PADDING_BYTES(16); + std::array<u8, 0x100> modulus; + std::array<u8, 0x100> signature_1; + std::array<u8, 0x100> signature_2; + u64_le title_id; + u32_le size; + INSERT_PADDING_BYTES(4); + u32_le hash_offset; + u32_le hash_count; + INSERT_PADDING_BYTES(8); + }; + static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); + + struct NROInfo { + SHA256Hash hash; + u64 size; + }; + + bool initialized = false; + + std::map<VAddr, NROInfo> nro; + std::map<VAddr, std::vector<SHA256Hash>> nrr; + + bool IsValidNROHash(const SHA256Hash& hash) { + return std::any_of( + nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) { + return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); + }); + } + + static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { + return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && + header.nro_size == nro_size && header.bss_size == bss_size && + header.ro_offset == header.text_offset + header.text_size && + header.rw_offset == header.ro_offset + header.ro_size && + nro_size == header.rw_offset + header.rw_size && + Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && + Common::Is4KBAligned(header.rw_size); } }; diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index c89157a4d..1f462e087 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -18,7 +18,7 @@ public: ILogger() : ServiceFramework("ILogger") { static const FunctionInfo functions[] = { {0x00000000, &ILogger::Initialize, "Initialize"}, - {0x00000001, nullptr, "SetDestination"}, + {0x00000001, &ILogger::SetDestination, "SetDestination"}, }; RegisterHandlers(functions); } @@ -178,6 +178,17 @@ private: } } + // This service function is intended to be used as a way to + // redirect logging output to different destinations, however, + // given we always want to see the logging output, it's sufficient + // to do nothing and return success here. + void SetDestination(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LM, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + std::ostringstream log_stream; }; @@ -198,11 +209,11 @@ public: * 0: ResultCode */ void OpenLogger(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILogger>(); - - LOG_DEBUG(Service_LM, "called"); } }; diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp index e1f17a926..def63dc8a 100644 --- a/src/core/hle/service/mm/mm_u.cpp +++ b/src/core/hle/service/mm/mm_u.cpp @@ -31,12 +31,14 @@ public: private: void Initialize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void Finalize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -45,15 +47,16 @@ private: IPC::RequestParser rp{ctx}; min = rp.Pop<u32>(); max = rp.Pop<u32>(); - current = min; - LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max); + + current = min; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void Get(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(current); @@ -61,6 +64,7 @@ private: void InitializeWithId(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(id); // Any non zero value @@ -68,6 +72,7 @@ private: void FinalizeWithId(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -77,16 +82,17 @@ private: u32 input_id = rp.Pop<u32>(); min = rp.Pop<u32>(); max = rp.Pop<u32>(); - current = min; - LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", input_id, min, max); + + current = min; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void GetWithId(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_MM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(current); diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 30e542542..5c62d42ba 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -43,11 +43,11 @@ public: private: void CreateAmInterface(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAm>(); - - LOG_DEBUG(Service_NFC, "called"); } }; @@ -91,11 +91,11 @@ public: private: void CreateUserInterface(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<MFIUser>(); - - LOG_DEBUG(Service_NFC, "called"); } }; @@ -138,19 +138,19 @@ private: }; void InitializeOld(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0}; rb.Push(RESULT_SUCCESS); - // We don't deal with hardware initialization so we can just stub this. - LOG_DEBUG(Service_NFC, "called"); } void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "IsNfcEnabledOld"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.PushRaw<u8>(Settings::values.enable_nfc); - - LOG_DEBUG(Service_NFC, "IsNfcEnabledOld"); } void GetStateOld(Kernel::HLERequestContext& ctx) { @@ -183,11 +183,11 @@ public: private: void CreateUserInterface(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IUser>(); - - LOG_DEBUG(Service_NFC, "called"); } }; @@ -241,11 +241,11 @@ public: private: void CreateSystemInterface(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISystem>(); - - LOG_DEBUG(Service_NFC, "called"); } }; diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index c1af878fe..2254fb46b 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -7,9 +7,11 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/lock.h" -#include "core/hle/service/hid/hid.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nfp/nfp_user.h" @@ -23,8 +25,8 @@ constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) : ServiceFramework(name), module(std::move(module)) { auto& kernel = Core::System::GetInstance().Kernel(); - nfc_tag_load = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected"); + nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IUser:NFCTagDetected"); } Module::Interface::~Interface() = default; @@ -63,10 +65,10 @@ public: RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - deactivate_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); - availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "IUser:AvailabilityChangeEvent"); + deactivate_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); + availability_change_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent"); } private: @@ -108,30 +110,29 @@ private: static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); void Initialize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0}; rb.Push(RESULT_SUCCESS); state = State::Initialized; - - LOG_DEBUG(Service_NFC, "called"); } void GetState(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFC, "called"); + IPC::ResponseBuilder rb{ctx, 3, 0}; rb.Push(RESULT_SUCCESS); rb.PushRaw<u32>(static_cast<u32>(state)); - - LOG_DEBUG(Service_NFC, "called"); } void ListDevices(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 array_size = rp.Pop<u32>(); + LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); ctx.WriteBuffer(&device_handle, sizeof(device_handle)); - LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); - IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(1); @@ -141,6 +142,7 @@ private: IPC::RequestParser rp{ctx}; const u64 dev_handle = rp.Pop<u64>(); LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(npad_id); @@ -150,6 +152,7 @@ private: IPC::RequestParser rp{ctx}; const u64 dev_handle = rp.Pop<u64>(); LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(nfp_interface.GetNFCEvent()); @@ -163,15 +166,16 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(deactivate_event); + rb.PushCopyObjects(deactivate_event.readable); } void StopDetection(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); + switch (device_state) { case DeviceState::TagFound: case DeviceState::TagNearby: - deactivate_event->Signal(); + deactivate_event.writable->Signal(); device_state = DeviceState::Initialized; break; case DeviceState::SearchingForTag: @@ -185,6 +189,7 @@ private: void GetDeviceState(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); + auto nfc_event = nfp_interface.GetNFCEvent(); if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) { device_state = DeviceState::TagFound; @@ -212,7 +217,7 @@ private: IPC::ResponseBuilder rb{ctx, 2}; auto amiibo = nfp_interface.GetAmiiboBuffer(); TagInfo tag_info{}; - std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size())); + tag_info.uuid = amiibo.uuid; tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); tag_info.protocol = 1; // TODO(ogniK): Figure out actual values @@ -261,7 +266,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(availability_change_event); + rb.PushCopyObjects(availability_change_event.readable); } void GetRegisterInfo(Kernel::HLERequestContext& ctx) { @@ -312,17 +317,18 @@ private: } bool has_attached_handle{}; - const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')}; - const u32 npad_id{0}; // Player 1 controller + const u64 device_handle{0}; // Npad device 1 + const u32 npad_id{0}; // Player 1 controller State state{State::NonInitialized}; DeviceState device_state{DeviceState::Initialized}; - Kernel::SharedPtr<Kernel::Event> deactivate_event; - Kernel::SharedPtr<Kernel::Event> availability_change_event; + Kernel::EventPair deactivate_event; + Kernel::EventPair availability_change_event; const Module::Interface& nfp_interface; }; void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IUser>(*this); @@ -335,12 +341,14 @@ bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { } std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); - nfc_tag_load->Signal(); + nfc_tag_load.writable->Signal(); return true; } -const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const { - return nfc_tag_load; + +const Kernel::SharedPtr<Kernel::ReadableEvent>& Module::Interface::GetNFCEvent() const { + return nfc_tag_load.readable; } + const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { return amiibo; } diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 5c0ae8a54..a1817e991 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -6,7 +6,8 @@ #include <array> #include <vector> -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/service.h" namespace Service::NFP { @@ -33,11 +34,11 @@ public: void CreateUserInterface(Kernel::HLERequestContext& ctx); bool LoadAmiibo(const std::vector<u8>& buffer); - const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const; + const Kernel::SharedPtr<Kernel::ReadableEvent>& GetNFCEvent() const; const AmiiboFile& GetAmiiboBuffer() const; private: - Kernel::SharedPtr<Kernel::Event> nfc_tag_load{}; + Kernel::EventPair nfc_tag_load{}; AmiiboFile amiibo{}; protected: diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 75dcd94a3..60479bb45 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -4,7 +4,9 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/service.h" @@ -56,19 +58,23 @@ public: RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - event1 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event1"); - event2 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event2"); + event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IRequest:Event1"); + event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "IRequest:Event2"); } private: void Submit(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void GetRequestState(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); @@ -76,30 +82,34 @@ private: void GetResult(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 2}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(event1, event2); + rb.PushCopyObjects(event1.readable, event2.readable); } void Cancel(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } - Kernel::SharedPtr<Kernel::Event> event1, event2; + Kernel::EventPair event1, event2; }; class INetworkProfile final : public ServiceFramework<INetworkProfile> { @@ -122,32 +132,36 @@ private: void GetClientId(Kernel::HLERequestContext& ctx) { static constexpr u32 client_id = 1; LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid } void CreateScanRequest(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IScanRequest>(); - - LOG_DEBUG(Service_NIFM, "called"); } void CreateRequest(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IRequest>(); - - LOG_DEBUG(Service_NIFM, "called"); } void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); + ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "NetworkProfileData is not the correct size"); u128 uuid{}; auto buffer = ctx.ReadBuffer(); @@ -158,23 +172,24 @@ private: rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<INetworkProfile>(); rb.PushRaw<u128>(uuid); - - LOG_DEBUG(Service_NIFM, "called"); } void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u8>(0); } void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u8>(0); } void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u8>(0); @@ -235,17 +250,19 @@ public: } void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IGeneralService>(); - LOG_DEBUG(Service_NIFM, "called"); } void CreateGeneralService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIFM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IGeneralService>(); - LOG_DEBUG(Service_NIFM, "called"); } }; diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 18091c9bb..0dabcd23b 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -6,7 +6,9 @@ #include <ctime> #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -138,57 +140,61 @@ public: RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - finished_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "IEnsureNetworkClockAvailabilityService:FinishEvent"); + finished_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::OneShot, + "IEnsureNetworkClockAvailabilityService:FinishEvent"); } private: - Kernel::SharedPtr<Kernel::Event> finished_event; + Kernel::EventPair finished_event; void StartTask(Kernel::HLERequestContext& ctx) { // No need to connect to the internet, just finish the task straight away. - finished_event->Signal(); + LOG_DEBUG(Service_NIM, "called"); + finished_event.writable->Signal(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_NIM, "called"); } void GetFinishNotificationEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(finished_event); - LOG_DEBUG(Service_NIM, "called"); + rb.PushCopyObjects(finished_event.readable); } void GetResult(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIM, "called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_NIM, "called"); } void Cancel(Kernel::HLERequestContext& ctx) { - finished_event->Clear(); + LOG_DEBUG(Service_NIM, "called"); + finished_event.writable->Clear(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_NIM, "called"); } void IsProcessing(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIM, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.PushRaw<u32>(0); // We instantly process the request - LOG_DEBUG(Service_NIM, "called"); } void GetServerTime(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIM, "called"); + const s64 server_time{std::chrono::duration_cast<std::chrono::seconds>( std::chrono::system_clock::now().time_since_epoch()) .count()}; IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.PushRaw<s64>(server_time); - LOG_DEBUG(Service_NIM, "called"); } }; @@ -208,23 +214,26 @@ public: private: void OpenEnsureNetworkClockAvailabilityService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NIM, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(); - LOG_DEBUG(Service_NIM, "called"); } // TODO(ogniK): Do we need these? void SuspendAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_NIM, "(STUBBED) called"); } void ResumeAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_NIM, "(STUBBED) called"); } }; diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 07c1381fe..2663f56b1 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/file_sys/control_metadata.h" +#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/ns.h" @@ -118,7 +121,7 @@ public: {305, nullptr, "TerminateSystemApplet"}, {306, nullptr, "LaunchOverlayApplet"}, {307, nullptr, "TerminateOverlayApplet"}, - {400, nullptr, "GetApplicationControlData"}, + {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"}, {401, nullptr, "InvalidateAllApplicationControlCache"}, {402, nullptr, "RequestDownloadApplicationControlData"}, {403, nullptr, "GetMaxApplicationControlCacheCount"}, @@ -243,6 +246,65 @@ public: RegisterHandlers(functions); } + + 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)); + } + + 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); + } + + ctx.WriteBuffer(out); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(out.size())); + } }; class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { @@ -371,11 +433,11 @@ public: 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>(); - - LOG_DEBUG(Service_NS, "called"); } }; @@ -464,11 +526,11 @@ public: private: void OpenSystemUpdateControl(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NS, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISystemUpdateControl>(); - - LOG_DEBUG(Service_NS, "called"); } }; diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 44accecb7..ad176f89d 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -281,6 +281,7 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { const u32 shared_font_type{rp.Pop<u32>()}; // Games don't call this so all fonts should be loaded LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -288,8 +289,8 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; - LOG_DEBUG(Service_NS, "called, font_id={}", font_id); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(LoadState::Done)); @@ -298,8 +299,8 @@ void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { void PL_U::GetSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; - LOG_DEBUG(Service_NS, "called, font_id={}", font_id); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(impl->GetSharedFontRegion(font_id).size); @@ -308,8 +309,8 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) { void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 font_id{rp.Pop<u32>()}; - LOG_DEBUG(Service_NS, "called, font_id={}", font_id); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset); @@ -317,6 +318,7 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { // Map backing memory for the font data + LOG_DEBUG(Service_NS, "called"); Core::CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, SHARED_FONT_MEM_SIZE, Kernel::MemoryState::Shared); @@ -328,7 +330,6 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, "PL_U:shared_font_mem"); - LOG_DEBUG(Service_NS, "called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(impl->shared_font_mem); @@ -338,6 +339,7 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code); + IPC::ResponseBuilder rb{ctx, 4}; std::vector<u32> font_codes; std::vector<u32> font_offsets; @@ -351,6 +353,14 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { font_sizes.push_back(region.size); } + // Resize buffers if game requests smaller size output. + font_codes.resize( + std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32))); + font_offsets.resize( + std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32))); + font_sizes.resize( + std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32))); + ctx.WriteBuffer(font_codes, 0); ctx.WriteBuffer(font_offsets, 1); ctx.WriteBuffer(font_sizes, 2); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index c41ef7058..466db7ccd 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -54,6 +54,7 @@ u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& ou IoctlInitalizeEx params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); + return 0; } @@ -191,6 +192,7 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou IoctlBindChannel params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); + channel = params.fd; return 0; } 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 7a88ae029..d57a54ee8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -5,6 +5,8 @@ #include <cstring> #include "common/assert.h" #include "common/logging/log.h" +#include "core/core_timing.h" +#include "core/core_timing_util.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" namespace Service::Nvidia::Devices { @@ -33,6 +35,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec return ZBCQueryTable(input, output); case IoctlCommand::IocFlushL2: return FlushL2(input, output); + case IoctlCommand::IocGetGpuTime: + return GetGpuTime(input, output); } UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; @@ -99,6 +103,7 @@ u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); + IoctlActiveSlotMask params{}; if (input.size() > 0) { std::memcpy(¶ms, input.data(), input.size()); @@ -111,6 +116,7 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); + IoctlZcullGetCtxSize params{}; if (input.size() > 0) { std::memcpy(¶ms, input.data(), input.size()); @@ -122,6 +128,7 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); + IoctlNvgpuGpuZcullGetInfoArgs params{}; if (input.size() > 0) { @@ -144,6 +151,7 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IoctlZbcSetTable params{}; std::memcpy(¶ms, input.data(), input.size()); // TODO(ogniK): What does this even actually do? @@ -153,6 +161,7 @@ u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IoctlZbcQueryTable params{}; std::memcpy(¶ms, input.data(), input.size()); // TODO : To implement properly @@ -162,6 +171,7 @@ u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8> u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IoctlFlushL2 params{}; std::memcpy(¶ms, input.data(), input.size()); // TODO : To implement properly @@ -169,4 +179,14 @@ u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& outp return 0; } +u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + + IoctlGetGpuTime params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks()); + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 3bbf028ad..240435eea 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -156,6 +156,11 @@ private: }; static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size"); + struct IoctlGetGpuTime { + u64_le gpu_time; + }; + static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size"); + u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); @@ -164,6 +169,7 @@ private: u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 874d5e1c3..0a650f36c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -8,7 +8,6 @@ #include "core/core.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/memory.h" -#include "video_core/command_processor.h" #include "video_core/gpu.h" #include "video_core/memory_manager.h" @@ -61,12 +60,14 @@ u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); + nvmap_fd = params.nvmap_fd; return 0; } u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); + IoctlClientData params{}; std::memcpy(¶ms, input.data(), input.size()); user_data = params.data; @@ -75,6 +76,7 @@ u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& out u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); + IoctlClientData params{}; std::memcpy(¶ms, input.data(), input.size()); params.data = user_data; @@ -86,6 +88,7 @@ u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) std::memcpy(&zcull_params, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, zcull_params.mode); + std::memcpy(output.data(), &zcull_params, output.size()); return 0; } @@ -95,6 +98,7 @@ u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& std::memcpy(¶ms, input.data(), input.size()); LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, params.size, params.mem); + std::memcpy(output.data(), ¶ms, output.size()); return 0; } @@ -102,6 +106,7 @@ u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { std::memcpy(&channel_priority, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); + return 0; } @@ -113,6 +118,7 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou "unk1={:X}, unk2={:X}, unk3={:X}", params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, params.unk3); + params.fence_out.id = 0; params.fence_out.value = 0; std::memcpy(output.data(), ¶ms, output.size()); @@ -124,11 +130,22 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector< std::memcpy(¶ms, input.data(), input.size()); LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, params.flags); + params.obj_id = 0x0; std::memcpy(output.data(), ¶ms, output.size()); return 0; } +static void PushGPUEntries(Tegra::CommandList&& entries) { + if (entries.empty()) { + return; + } + + auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()}; + dma_pusher.Push(std::move(entries)); + dma_pusher.DispatchCalls(); +} + u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { if (input.size() < sizeof(IoctlSubmitGpfifo)) { UNIMPLEMENTED(); @@ -142,11 +159,11 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp params.num_entries * sizeof(Tegra::CommandListHeader), "Incorrect input size"); - std::vector<Tegra::CommandListHeader> entries(params.num_entries); + Tegra::CommandList entries(params.num_entries); std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], params.num_entries * sizeof(Tegra::CommandListHeader)); - Core::System::GetInstance().GPU().ProcessCommandLists(entries); + PushGPUEntries(std::move(entries)); params.fence_out.id = 0; params.fence_out.value = 0; @@ -163,11 +180,11 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, params.num_entries, params.flags); - std::vector<Tegra::CommandListHeader> entries(params.num_entries); + Tegra::CommandList entries(params.num_entries); Memory::ReadBlock(params.address, entries.data(), params.num_entries * sizeof(Tegra::CommandListHeader)); - Core::System::GetInstance().GPU().ProcessCommandLists(entries); + PushGPUEntries(std::move(entries)); params.fence_out.id = 0; params.fence_out.value = 0; @@ -179,6 +196,7 @@ u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& outpu IoctlGetWaitbase params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); + params.value = 0; // Seems to be hard coded at 0 std::memcpy(output.data(), ¶ms, output.size()); return 0; @@ -188,6 +206,7 @@ u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& IoctlChannelSetTimeout params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlChannelSetTimeout)); LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); + return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index 46dbbc37c..f5e8ea7c3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -30,6 +30,7 @@ u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& outp IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); + nvmap_fd = params.nvmap_fd; return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index c67f934f6..3e0951ab0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -30,6 +30,7 @@ u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& outp IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); + nvmap_fd = params.nvmap_fd; return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 727b9fee4..d544f0f31 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -30,6 +30,7 @@ u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output IoctlSetNvmapFD params{}; std::memcpy(¶ms, input.data(), input.size()); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); + nvmap_fd = params.nvmap_fd; return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 43651d8a6..1ec796fc6 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -54,6 +54,7 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); if (!params.size) { + LOG_ERROR(Service_NVDRV, "Size is 0"); return static_cast<u32>(NvErrCodes::InvalidValue); } // Create a new nvmap object and obtain a handle to it. @@ -78,10 +79,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); if (!params.handle) { + LOG_ERROR(Service_NVDRV, "Handle is 0"); return static_cast<u32>(NvErrCodes::InvalidValue); } if ((params.align - 1) & params.align) { + LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); return static_cast<u32>(NvErrCodes::InvalidValue); } @@ -92,10 +95,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { auto object = GetObject(params.handle); if (!object) { + LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); return static_cast<u32>(NvErrCodes::InvalidValue); } if (object->status == Object::Status::Allocated) { + LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); return static_cast<u32>(NvErrCodes::OperationNotPermitted); } @@ -116,11 +121,13 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { LOG_WARNING(Service_NVDRV, "called"); if (!params.handle) { + LOG_ERROR(Service_NVDRV, "Handle is zero"); return static_cast<u32>(NvErrCodes::InvalidValue); } auto object = GetObject(params.handle); if (!object) { + LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); return static_cast<u32>(NvErrCodes::OperationNotPermitted); } @@ -139,11 +146,13 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { auto itr = std::find_if(handles.begin(), handles.end(), [&](const auto& entry) { return entry.second->id == params.id; }); if (itr == handles.end()) { + LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); return static_cast<u32>(NvErrCodes::InvalidValue); } auto& object = itr->second; if (object->status != Object::Status::Allocated) { + LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); return static_cast<u32>(NvErrCodes::InvalidValue); } @@ -166,10 +175,12 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { auto object = GetObject(params.handle); if (!object) { + LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); return static_cast<u32>(NvErrCodes::InvalidValue); } if (object->status != Object::Status::Allocated) { + LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); return static_cast<u32>(NvErrCodes::OperationNotPermitted); } @@ -209,9 +220,14 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { auto itr = handles.find(params.handle); if (itr == handles.end()) { + LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); return static_cast<u32>(NvErrCodes::InvalidValue); } if (!itr->second->refcount) { + LOG_ERROR( + Service_NVDRV, + "There is no references to this object. The object is already freed. handle={:08X}", + params.handle); return static_cast<u32>(NvErrCodes::InvalidValue); } diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index ac3859353..3b9ab4b14 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -6,7 +6,9 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/interface.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -55,6 +57,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) { void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); @@ -68,15 +71,15 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(query_event); + rb.PushCopyObjects(query_event.readable); rb.Push<u32>(0); } void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; pid = rp.Pop<u64>(); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); @@ -84,6 +87,23 @@ void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) { void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) { + // According to SwitchBrew, this has no inputs and no outputs, so effectively does nothing on + // retail hardware. + LOG_DEBUG(Service_NVDRV, "called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -97,10 +117,10 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) {3, &NVDRV::Initialize, "Initialize"}, {4, &NVDRV::QueryEvent, "QueryEvent"}, {5, nullptr, "MapSharedMem"}, - {6, nullptr, "GetStatus"}, + {6, &NVDRV::GetStatus, "GetStatus"}, {7, nullptr, "ForceSetClientPID"}, {8, &NVDRV::SetClientPID, "SetClientPID"}, - {9, nullptr, "DumpGraphicsMemoryInfo"}, + {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"}, {10, nullptr, "InitializeDevtools"}, {11, &NVDRV::Ioctl, "Ioctl2"}, {12, nullptr, "Ioctl3"}, @@ -109,7 +129,8 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - query_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "NVDRV::query_event"); + query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot, + "NVDRV::query_event"); } NVDRV::~NVDRV() = default; diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index d340893c2..fe311b069 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -5,10 +5,13 @@ #pragma once #include <memory> -#include "core/hle/kernel/event.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/service.h" +namespace Kernel { +class WritableEvent; +} + namespace Service::Nvidia { class NVDRV final : public ServiceFramework<NVDRV> { @@ -24,12 +27,14 @@ private: void QueryEvent(Kernel::HLERequestContext& ctx); void SetClientPID(Kernel::HLERequestContext& ctx); void FinishInitialize(Kernel::HLERequestContext& ctx); + void GetStatus(Kernel::HLERequestContext& ctx); + void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx); std::shared_ptr<Module> nvdrv; u64 pid{}; - Kernel::SharedPtr<Kernel::Event> query_event; + Kernel::EventPair query_event; }; } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index fd98d541d..fc07d9bb8 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -7,31 +7,34 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvflinger/buffer_queue.h" namespace Service::NVFlinger { BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { auto& kernel = Core::System::GetInstance().Kernel(); - buffer_wait_event = - Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "BufferQueue NativeHandle"); + buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + "BufferQueue NativeHandle"); } BufferQueue::~BufferQueue() = default; void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { + LOG_WARNING(Service, "Adding graphics buffer {}", slot); + Buffer buffer{}; buffer.slot = slot; buffer.igbp_buffer = igbp_buffer; buffer.status = Buffer::Status::Free; - LOG_WARNING(Service, "Adding graphics buffer {}", slot); - queue.emplace_back(buffer); - buffer_wait_event->Signal(); + buffer_wait_event.writable->Signal(); } -boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { +std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { // Only consider free buffers. Buffers become free once again after they've been Acquired // and Released by the compositor, see the NVFlinger::Compose method. @@ -44,7 +47,7 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { }); if (itr == queue.end()) { - return boost::none; + return {}; } itr->status = Buffer::Status::Dequeued; @@ -70,12 +73,12 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, itr->crop_rect = crop_rect; } -boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() { +std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { return buffer.status == Buffer::Status::Queued; }); if (itr == queue.end()) - return boost::none; + return {}; itr->status = Buffer::Status::Acquired; return *itr; } @@ -87,11 +90,12 @@ void BufferQueue::ReleaseBuffer(u32 slot) { ASSERT(itr->status == Buffer::Status::Acquired); itr->status = Buffer::Status::Free; - buffer_wait_event->Signal(); + buffer_wait_event.writable->Signal(); } u32 BufferQueue::Query(QueryType type) { LOG_WARNING(Service, "(STUBBED) called type={}", static_cast<u32>(type)); + switch (type) { case QueryType::NativeWindowFormat: // TODO(Subv): Use an enum for this @@ -103,4 +107,12 @@ u32 BufferQueue::Query(QueryType type) { return 0; } +Kernel::SharedPtr<Kernel::WritableEvent> BufferQueue::GetWritableBufferWaitEvent() const { + return buffer_wait_event.writable; +} + +Kernel::SharedPtr<Kernel::ReadableEvent> BufferQueue::GetBufferWaitEvent() const { + return buffer_wait_event.readable; +} + } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 50b767732..b171f256c 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -4,12 +4,14 @@ #pragma once +#include <optional> #include <vector> -#include <boost/optional.hpp> + #include "common/common_funcs.h" #include "common/math_util.h" #include "common/swap.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/object.h" +#include "core/hle/kernel/writable_event.h" namespace CoreTiming { struct EventType; @@ -57,9 +59,9 @@ public: /// Rotate source image 90 degrees clockwise Rotate90 = 0x04, /// Rotate source image 180 degrees - Roate180 = 0x03, + Rotate180 = 0x03, /// Rotate source image 270 degrees clockwise - Roate270 = 0x07, + Rotate270 = 0x07, }; struct Buffer { @@ -73,11 +75,11 @@ public: }; void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); - boost::optional<u32> DequeueBuffer(u32 width, u32 height); + std::optional<u32> DequeueBuffer(u32 width, u32 height); const IGBPBuffer& RequestBuffer(u32 slot) const; void QueueBuffer(u32 slot, BufferTransformFlags transform, const MathUtil::Rectangle<int>& crop_rect); - boost::optional<const Buffer&> AcquireBuffer(); + std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); void ReleaseBuffer(u32 slot); u32 Query(QueryType type); @@ -85,16 +87,16 @@ public: return id; } - Kernel::SharedPtr<Kernel::Event> GetBufferWaitEvent() const { - return buffer_wait_event; - } + Kernel::SharedPtr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const; + + Kernel::SharedPtr<Kernel::ReadableEvent> GetBufferWaitEvent() const; private: u32 id; u64 layer_id; std::vector<Buffer> queue; - Kernel::SharedPtr<Kernel::Event> buffer_wait_event; + Kernel::EventPair buffer_wait_event; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index d47b6f659..05af2d593 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> -#include <boost/optional.hpp> +#include <optional> #include "common/alignment.h" #include "common/assert.h" @@ -13,6 +13,9 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" @@ -83,9 +86,8 @@ u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) { return layer.buffer_queue->GetId(); } -Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) { - const auto& display = GetDisplay(display_id); - return display.vsync_event; +Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::GetVsyncEvent(u64 display_id) { + return GetDisplay(display_id).vsync_event.readable; } std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const { @@ -117,7 +119,7 @@ Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) { void NVFlinger::Compose() { for (auto& display : displays) { // Trigger vsync for this display at the end of drawing - SCOPE_EXIT({ display.vsync_event->Signal(); }); + SCOPE_EXIT({ display.vsync_event.writable->Signal(); }); // Don't do anything for displays without layers. if (display.layers.empty()) @@ -134,7 +136,7 @@ void NVFlinger::Compose() { MicroProfileFlip(); - if (buffer == boost::none) { + if (!buffer) { auto& system_instance = Core::System::GetInstance(); // There was no queued buffer to draw, render previous frame @@ -143,7 +145,7 @@ void NVFlinger::Compose() { continue; } - auto& igbp_buffer = buffer->igbp_buffer; + auto& igbp_buffer = buffer->get().igbp_buffer; // Now send the buffer to the GPU for drawing. // TODO(Subv): Support more than just disp0. The display device selection is probably based @@ -152,10 +154,10 @@ void NVFlinger::Compose() { ASSERT(nvdisp); nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, - igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform, - buffer->crop_rect); + igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, + buffer->get().transform, buffer->get().crop_rect); - buffer_queue->ReleaseBuffer(buffer->slot); + buffer_queue->ReleaseBuffer(buffer->get().slot); } } @@ -164,7 +166,8 @@ Layer::~Layer() = default; Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { auto& kernel = Core::System::GetInstance().Kernel(); - vsync_event = Kernel::Event::Create(kernel, Kernel::ResetType::Pulse, "Display VSync Event"); + vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Pulse, + fmt::format("Display VSync Event {}", id)); } Display::~Display() = default; diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 3dc69e69b..9abba555b 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -10,12 +10,17 @@ #include <vector> #include "common/common_types.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/object.h" namespace CoreTiming { struct EventType; } +namespace Kernel { +class ReadableEvent; +class WritableEvent; +} // namespace Kernel + namespace Service::Nvidia { class Module; } @@ -40,7 +45,7 @@ struct Display { std::string name; std::vector<Layer> layers; - Kernel::SharedPtr<Kernel::Event> vsync_event; + Kernel::EventPair vsync_event; }; class NVFlinger final { @@ -61,7 +66,7 @@ public: u32 GetBufferQueueId(u64 display_id, u64 layer_id); /// Gets the vsync event for the specified display. - Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id); + Kernel::SharedPtr<Kernel::ReadableEvent> GetVsyncEvent(u64 display_id); /// Obtains a buffer queue identified by the id. std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const; diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp index 4fd185f69..6081f41e1 100644 --- a/src/core/hle/service/pctl/module.cpp +++ b/src/core/hle/service/pctl/module.cpp @@ -114,29 +114,33 @@ public: private: void Initialize(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_PCTL, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 0}; rb.Push(RESULT_SUCCESS); } void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_PCTL, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } }; void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IParentalControlService>(); - LOG_DEBUG(Service_PCTL, "called"); } void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IParentalControlService>(); - LOG_DEBUG(Service_PCTL, "called"); } Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index 6ec35ca60..53e7da9c3 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -20,11 +20,11 @@ public: private: void GetBootMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PM, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(SystemBootMode::Normal)); // Normal boot mode - - LOG_DEBUG(Service_PM, "called"); } }; diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp index bbad870a2..0ba0a4076 100644 --- a/src/core/hle/service/psc/psc.cpp +++ b/src/core/hle/service/psc/psc.cpp @@ -61,11 +61,11 @@ public: private: void GetPmModule(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PSC, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IPmModule>(); - - LOG_DEBUG(Service_PSC, "called"); } }; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index a4cf45267..d25b80ab0 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -70,18 +70,14 @@ #include "core/hle/service/vi/vi.h" #include "core/hle/service/wlan/wlan.h" -using Kernel::ClientPort; -using Kernel::ServerPort; -using Kernel::SharedPtr; - namespace Service { /** * Creates a function string for logging, complete with the name (or header code, depending * on what's passed in) the port name, and all the cmd_buff arguments. */ -static std::string MakeFunctionString(const char* name, const char* port_name, - const u32* cmd_buff) { +[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name, + const u32* cmd_buff) { // Number of params == bits 0-5 + bits 6-11 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); @@ -101,33 +97,33 @@ ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_ses ServiceFrameworkBase::~ServiceFrameworkBase() = default; void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { - ASSERT(port == nullptr); - port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); + ASSERT(!port_installed); + + auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); port->SetHleHandler(shared_from_this()); + port_installed = true; } void ServiceFrameworkBase::InstallAsNamedPort() { - ASSERT(port == nullptr); + ASSERT(!port_installed); auto& kernel = Core::System::GetInstance().Kernel(); - SharedPtr<ServerPort> server_port; - SharedPtr<ClientPort> client_port; - std::tie(server_port, client_port) = - ServerPort::CreatePortPair(kernel, max_sessions, service_name); + auto [server_port, client_port] = + Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); server_port->SetHleHandler(shared_from_this()); kernel.AddNamedPort(service_name, std::move(client_port)); + port_installed = true; } Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { - ASSERT(port == nullptr); + ASSERT(!port_installed); auto& kernel = Core::System::GetInstance().Kernel(); - Kernel::SharedPtr<Kernel::ServerPort> server_port; - Kernel::SharedPtr<Kernel::ClientPort> client_port; - std::tie(server_port, client_port) = + auto [server_port, client_port] = Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); - port = MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)).Unwrap(); + auto port = MakeResult(std::move(server_port)).Unwrap(); port->SetHleHandler(shared_from_this()); + port_installed = true; return client_port; } @@ -152,8 +148,7 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext } buf.push_back('}'); - LOG_ERROR(Service, "unknown / unimplemented {}", fmt::to_string(buf)); - UNIMPLEMENTED(); + UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf)); } void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 98483ecf1..029533628 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -96,11 +96,9 @@ private: /// Maximum number of concurrent sessions that this service can handle. u32 max_sessions; - /** - * Port where incoming connections will be received. Only created when InstallAsService() or - * InstallAsNamedPort() are called. - */ - Kernel::SharedPtr<Kernel::ServerPort> port; + /// Flag to store if a port was already create/installed to detect multiple install attempts, + /// which is not supported. + bool port_installed = false; /// Function used to safely up-cast pointers to the derived class before invoking a handler. InvokerFn* handler_invoker; diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 9e5af7839..1afc43f75 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -35,6 +35,8 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{ constexpr std::size_t pre4_0_0_max_entries = 0xF; constexpr std::size_t post4_0_0_max_entries = 0x40; +constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; + LanguageCode GetLanguageCodeFromIndex(std::size_t index) { return available_language_codes.at(index); } @@ -49,38 +51,54 @@ static std::array<LanguageCode, size> MakeLanguageCodeSubset() { static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - if (available_language_codes.size() > max_size) + if (available_language_codes.size() > max_size) { rb.Push(static_cast<u32>(max_size)); - else + } else { rb.Push(static_cast<u32>(available_language_codes.size())); + } } void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { - if (available_language_codes.size() > pre4_0_0_max_entries) + LOG_DEBUG(Service_SET, "called"); + + if (available_language_codes.size() > pre4_0_0_max_entries) { ctx.WriteBuffer(MakeLanguageCodeSubset<pre4_0_0_max_entries>()); - else + } else { ctx.WriteBuffer(available_language_codes); - + } PushResponseLanguageCode(ctx, pre4_0_0_max_entries); +} - LOG_DEBUG(Service_SET, "called"); +void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto index = rp.Pop<u32>(); + + if (index >= available_language_codes.size()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_INVALID_LANGUAGE); + return; + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(available_language_codes[index]); } void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) { - if (available_language_codes.size() > post4_0_0_max_entries) + LOG_DEBUG(Service_SET, "called"); + + if (available_language_codes.size() > post4_0_0_max_entries) { ctx.WriteBuffer(MakeLanguageCodeSubset<post4_0_0_max_entries>()); - else + } else { ctx.WriteBuffer(available_language_codes); - + } PushResponseLanguageCode(ctx, post4_0_0_max_entries); - - LOG_DEBUG(Service_SET, "called"); } void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { - PushResponseLanguageCode(ctx, pre4_0_0_max_entries); - LOG_DEBUG(Service_SET, "called"); + + PushResponseLanguageCode(ctx, pre4_0_0_max_entries); } void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) { @@ -90,18 +108,18 @@ void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) { } void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u64>(available_language_codes[Settings::values.language_index])); - - LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index); + rb.PushEnum(available_language_codes[Settings::values.language_index]); } SET::SET() : ServiceFramework("set") { static const FunctionInfo functions[] = { {0, &SET::GetLanguageCode, "GetLanguageCode"}, {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, - {2, nullptr, "MakeLanguageCode"}, + {2, &SET::MakeLanguageCode, "MakeLanguageCode"}, {3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"}, {4, nullptr, "GetRegionCode"}, {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 266f13e46..31f9cb296 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -38,6 +38,7 @@ public: private: void GetLanguageCode(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx); + void MakeLanguageCode(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 41efca31c..c9b4da5b0 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -10,22 +10,22 @@ namespace Service::Set { void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.PushEnum(color_set); - - LOG_DEBUG(Service_SET, "called"); } void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + IPC::RequestParser rp{ctx}; color_set = rp.PopEnum<ColorSet>(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - - LOG_DEBUG(Service_SET, "called"); } SET_SYS::SET_SYS() : ServiceFramework("set:sys") { diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index 98f6e4111..74da4d5e6 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp @@ -14,25 +14,26 @@ namespace Service::SM { void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) { ASSERT_MSG(ctx.Session()->IsSession(), "Session is already a domain"); + LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId()); ctx.Session()->ConvertToDomain(); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(1); // Converted sessions start with 1 request handler - - LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId()); } void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) { // TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong // and that we probably want to actually make an entirely new Session, but we still need to // verify this on hardware. + LOG_DEBUG(Service, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(RESULT_SUCCESS); Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client}; rb.PushMoveObjects(session); - LOG_DEBUG(Service, "called, session={}", session->GetObjectId()); + LOG_DEBUG(Service, "session={}", session->GetObjectId()); } void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) { @@ -42,11 +43,11 @@ void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) { } void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u16>(0x500); - - LOG_WARNING(Service, "(STUBBED) called"); } Controller::Controller() : ServiceFramework("IpcController") { diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 464e79d01..142929124 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -54,13 +54,22 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService return ERR_ALREADY_REGISTERED; auto& kernel = Core::System::GetInstance().Kernel(); - Kernel::SharedPtr<Kernel::ServerPort> server_port; - Kernel::SharedPtr<Kernel::ClientPort> client_port; - std::tie(server_port, client_port) = + auto [server_port, client_port] = Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name); registered_services.emplace(std::move(name), std::move(client_port)); - return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)); + return MakeResult(std::move(server_port)); +} + +ResultCode ServiceManager::UnregisterService(const std::string& name) { + CASCADE_CODE(ValidateServiceName(name)); + + const auto iter = registered_services.find(name); + if (iter == registered_services.end()) + return ERR_SERVICE_NOT_REGISTERED; + + registered_services.erase(iter); + return RESULT_SUCCESS; } ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort( @@ -72,7 +81,7 @@ ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort( return ERR_SERVICE_NOT_REGISTERED; } - return MakeResult<Kernel::SharedPtr<Kernel::ClientPort>>(it->second); + return MakeResult(it->second); } ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToService( @@ -92,9 +101,10 @@ SM::~SM() = default; * 0: ResultCode */ void SM::Initialize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SM, "called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_SM, "called"); } void SM::GetService(Kernel::HLERequestContext& ctx) { @@ -127,13 +137,54 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { } } +void SM::RegisterService(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto name_buf = rp.PopRaw<std::array<char, 8>>(); + const auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); + + const std::string name(name_buf.begin(), end); + + const auto is_light = static_cast<bool>(rp.PopRaw<u32>()); + const auto max_session_count = rp.PopRaw<u32>(); + + LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name, + max_session_count, is_light); + + auto handle = service_manager->RegisterService(name, max_session_count); + if (handle.Failed()) { + LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", + handle.Code().raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(handle.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; + rb.Push(handle.Code()); + rb.PushMoveObjects(std::move(handle).Unwrap()); +} + +void SM::UnregisterService(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto name_buf = rp.PopRaw<std::array<char, 8>>(); + const auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); + + const std::string name(name_buf.begin(), end); + LOG_DEBUG(Service_SM, "called with name={}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(service_manager->UnregisterService(name)); +} + SM::SM(std::shared_ptr<ServiceManager> service_manager) : ServiceFramework("sm:", 4), service_manager(std::move(service_manager)) { static const FunctionInfo functions[] = { {0x00000000, &SM::Initialize, "Initialize"}, {0x00000001, &SM::GetService, "GetService"}, - {0x00000002, nullptr, "RegisterService"}, - {0x00000003, nullptr, "UnregisterService"}, + {0x00000002, &SM::RegisterService, "RegisterService"}, + {0x00000003, &SM::UnregisterService, "UnregisterService"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 4f8145dda..bef25433e 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -35,6 +35,8 @@ public: private: void Initialize(Kernel::HLERequestContext& ctx); void GetService(Kernel::HLERequestContext& ctx); + void RegisterService(Kernel::HLERequestContext& ctx); + void UnregisterService(Kernel::HLERequestContext& ctx); std::shared_ptr<ServiceManager> service_manager; }; @@ -48,6 +50,7 @@ public: ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name, unsigned int max_sessions); + ResultCode UnregisterService(const std::string& name); ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name); ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name); diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp index 44a6717d0..8db0c2f13 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp @@ -3,34 +3,41 @@ // Refer to the license.txt file included. #include <algorithm> +#include <chrono> #include <cstdlib> +#include <ctime> +#include <functional> #include <vector> #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/spl/csrng.h" #include "core/hle/service/spl/module.h" #include "core/hle/service/spl/spl.h" +#include "core/settings.h" namespace Service::SPL { Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) - : ServiceFramework(name), module(std::move(module)) {} + : ServiceFramework(name), module(std::move(module)), + rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {} Module::Interface::~Interface() = default; void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SPL, "called"); + IPC::RequestParser rp{ctx}; std::size_t size = ctx.GetWriteBufferSize(); + std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max()); std::vector<u8> data(size); - std::generate(data.begin(), data.end(), std::rand); + std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); }); ctx.WriteBuffer(data); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_SPL, "called"); } void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h index 48fda6099..afa1f0295 100644 --- a/src/core/hle/service/spl/module.h +++ b/src/core/hle/service/spl/module.h @@ -4,6 +4,7 @@ #pragma once +#include <random> #include "core/hle/service/service.h" namespace Service::SPL { @@ -19,6 +20,9 @@ public: protected: std::shared_ptr<Module> module; + + private: + std::mt19937 rng; }; }; diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index bc4f7a437..af40a1815 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -69,6 +69,7 @@ public: private: void SetOption(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_SSL, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; IPC::ResponseBuilder rb{ctx, 2}; @@ -114,6 +115,7 @@ private: void SetInterfaceVersion(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SSL, "called"); + IPC::RequestParser rp{ctx}; ssl_version = rp.Pop<u32>(); diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index 18a5d71d5..b3a196f65 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp @@ -21,9 +21,10 @@ Time::Time(std::shared_ptr<Module> time, const char* name) {102, nullptr, "GetStandardUserSystemClockInitialYear"}, {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, - {400, nullptr, "GetClockSnapshot"}, + {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, - {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, + {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, + "CalculateStandardUserSystemClockDifferenceByUser"}, {501, nullptr, "CalculateSpanBetween"}, }; RegisterHandlers(functions); diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 28fd8debc..16564de24 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -15,6 +15,44 @@ namespace Service::Time { +static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time, + CalendarAdditionalInfo& additional_info, + [[maybe_unused]] const TimeZoneRule& /*rule*/) { + const std::time_t time(posix_time); + const std::tm* tm = std::localtime(&time); + if (tm == nullptr) { + calendar_time = {}; + additional_info = {}; + return; + } + calendar_time.year = tm->tm_year + 1900; + calendar_time.month = tm->tm_mon + 1; + calendar_time.day = tm->tm_mday; + calendar_time.hour = tm->tm_hour; + calendar_time.minute = tm->tm_min; + calendar_time.second = tm->tm_sec; + + additional_info.day_of_week = tm->tm_wday; + additional_info.day_of_year = tm->tm_yday; + std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC")); + additional_info.utc_offset = 0; +} + +static u64 CalendarToPosix(const CalendarTime& calendar_time, + [[maybe_unused]] const TimeZoneRule& /*rule*/) { + std::tm time{}; + time.tm_year = calendar_time.year - 1900; + time.tm_mon = calendar_time.month - 1; + time.tm_mday = calendar_time.day; + + time.tm_hour = calendar_time.hour; + time.tm_min = calendar_time.minute; + time.tm_sec = calendar_time.second; + + std::time_t epoch_time = std::mktime(&time); + return static_cast<u64>(epoch_time); +} + class ISystemClock final : public ServiceFramework<ISystemClock> { public: ISystemClock() : ServiceFramework("ISystemClock") { @@ -34,6 +72,7 @@ private: std::chrono::system_clock::now().time_since_epoch()) .count()}; LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(time_since_epoch); @@ -41,6 +80,7 @@ private: void GetSystemClockContext(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Time, "(STUBBED) called"); + SystemClockContext system_clock_ontext{}; IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2}; rb.Push(RESULT_SUCCESS); @@ -60,6 +100,7 @@ public: private: void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); + SteadyClockTimePoint steady_clock_time_point{ CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000}; IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; @@ -80,8 +121,8 @@ public: {5, nullptr, "GetTimeZoneRuleVersion"}, {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, - {201, nullptr, "ToPosixTime"}, - {202, nullptr, "ToPosixTimeWithMyRule"}, + {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"}, + {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, }; RegisterHandlers(functions); } @@ -92,6 +133,7 @@ private: void GetDeviceLocationName(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2}; rb.Push(RESULT_SUCCESS); rb.PushRaw(location_name); @@ -99,6 +141,7 @@ private: void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Time, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); @@ -116,7 +159,6 @@ private: void ToCalendarTime(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 posix_time = rp.Pop<u64>(); - LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time); TimeZoneRule time_zone_rule{}; @@ -137,7 +179,6 @@ private: void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 posix_time = rp.Pop<u64>(); - LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time); CalendarTime calendar_time{2018, 1, 1, 0, 0, 0}; @@ -151,60 +192,139 @@ private: rb.PushRaw(additional_info); } - void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time, - CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) { - std::time_t t(posix_time); - std::tm* tm = std::localtime(&t); - if (!tm) { - return; - } - calendar_time.year = tm->tm_year + 1900; - calendar_time.month = tm->tm_mon + 1; - calendar_time.day = tm->tm_mday; - calendar_time.hour = tm->tm_hour; - calendar_time.minute = tm->tm_min; - calendar_time.second = tm->tm_sec; - - additional_info.day_of_week = tm->tm_wday; - additional_info.day_of_year = tm->tm_yday; - std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC")); - additional_info.utc_offset = 0; + void ToPosixTime(Kernel::HLERequestContext& ctx) { + // TODO(ogniK): Figure out how to handle multiple times + LOG_WARNING(Service_Time, "(STUBBED) called"); + + IPC::RequestParser rp{ctx}; + auto calendar_time = rp.PopRaw<CalendarTime>(); + auto posix_time = CalendarToPosix(calendar_time, {}); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u32>(1); // Amount of times we're returning + ctx.WriteBuffer(&posix_time, sizeof(u64)); + } + + void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Time, "(STUBBED) called"); + + IPC::RequestParser rp{ctx}; + auto calendar_time = rp.PopRaw<CalendarTime>(); + auto posix_time = CalendarToPosix(calendar_time, {}); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u32>(1); // Amount of times we're returning + ctx.WriteBuffer(&posix_time, sizeof(u64)); } }; void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISystemClock>(); - LOG_DEBUG(Service_Time, "called"); } void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISystemClock>(); - LOG_DEBUG(Service_Time, "called"); } void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISteadyClock>(); - LOG_DEBUG(Service_Time, "called"); } void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ITimeZoneService>(); - LOG_DEBUG(Service_Time, "called"); } void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISystemClock>(); +} + +void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); + + IPC::RequestParser rp{ctx}; + const auto initial_type = rp.PopRaw<u8>(); + + const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count()}; + + const std::time_t time(time_since_epoch); + const std::tm* tm = std::localtime(&time); + if (tm == nullptr) { + LOG_ERROR(Service_Time, "tm is a nullptr"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code + return; + } + + const SteadyClockTimePoint steady_clock_time_point{ + CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000, {}}; + + CalendarTime calendar_time{}; + calendar_time.year = tm->tm_year + 1900; + calendar_time.month = tm->tm_mon + 1; + calendar_time.day = tm->tm_mday; + calendar_time.hour = tm->tm_hour; + calendar_time.minute = tm->tm_min; + calendar_time.second = tm->tm_sec; + + ClockSnapshot clock_snapshot{}; + clock_snapshot.system_posix_time = time_since_epoch; + clock_snapshot.network_posix_time = time_since_epoch; + clock_snapshot.system_calendar_time = calendar_time; + clock_snapshot.network_calendar_time = calendar_time; + + CalendarAdditionalInfo additional_info{}; + PosixToCalendar(time_since_epoch, calendar_time, additional_info, {}); + + clock_snapshot.system_calendar_info = additional_info; + clock_snapshot.network_calendar_info = additional_info; + + clock_snapshot.steady_clock_timepoint = steady_clock_time_point; + clock_snapshot.location_name = LocationName{"UTC"}; + clock_snapshot.clock_auto_adjustment_enabled = 1; + clock_snapshot.type = initial_type; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot)); +} + +void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( + Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + + IPC::RequestParser rp{ctx}; + const auto snapshot_a = rp.PopRaw<ClockSnapshot>(); + const auto snapshot_b = rp.PopRaw<ClockSnapshot>(); + const u64 difference = + snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset; + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u64>(difference); } Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 5659ecad3..f11affe95 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -5,6 +5,7 @@ #pragma once #include <array> +#include "common/common_funcs.h" #include "core/hle/service/service.h" namespace Service::Time { @@ -21,7 +22,6 @@ struct CalendarTime { u8 hour; u8 minute; u8 second; - INSERT_PADDING_BYTES(1); }; static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size"); @@ -29,7 +29,7 @@ struct CalendarAdditionalInfo { u32_le day_of_week; u32_le day_of_year; std::array<u8, 8> name; - INSERT_PADDING_BYTES(1); + u8 is_dst; s32_le utc_offset; }; static_assert(sizeof(CalendarAdditionalInfo) == 0x18, @@ -41,8 +41,10 @@ struct TimeZoneRule { }; struct SteadyClockTimePoint { + using SourceID = std::array<u8, 16>; + u64_le value; - INSERT_PADDING_WORDS(4); + SourceID source_id; }; static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size"); @@ -53,6 +55,24 @@ struct SystemClockContext { static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext structure has incorrect size"); +struct ClockSnapshot { + SystemClockContext user_clock_context; + SystemClockContext network_clock_context; + s64_le system_posix_time; + s64_le network_posix_time; + CalendarTime system_calendar_time; + CalendarTime network_calendar_time; + CalendarAdditionalInfo system_calendar_info; + CalendarAdditionalInfo network_calendar_info; + SteadyClockTimePoint steady_clock_timepoint; + LocationName location_name; + u8 clock_auto_adjustment_enabled; + u8 type; + u8 version; + INSERT_PADDING_BYTES(1); +}; +static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size"); + class Module final { public: class Interface : public ServiceFramework<Interface> { @@ -65,6 +85,8 @@ public: void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); void GetTimeZoneService(Kernel::HLERequestContext& ctx); void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); + void GetClockSnapshot(Kernel::HLERequestContext& ctx); + void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx); protected: std::shared_ptr<Module> time; diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index c489da071..58a9845fc 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp @@ -73,7 +73,7 @@ public: {3, nullptr, "Populate"}, {4, nullptr, "PostBufferAsync"}, {5, nullptr, "GetXferReport"}, - {6, nullptr, "Unknown2"}, + {6, nullptr, "PostBufferMultiAsync"}, {7, nullptr, "Unknown3"}, {8, nullptr, "Unknown4"}, }; @@ -132,11 +132,11 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "BindNoticeEvent"}, - {1, nullptr, "Unknown1"}, + {1, nullptr, "UnbindNoticeEvent"}, {2, nullptr, "GetStatus"}, {3, nullptr, "GetNotice"}, - {4, nullptr, "Unknown2"}, - {5, nullptr, "Unknown3"}, + {4, nullptr, "EnablePowerRequestNotice"}, + {5, nullptr, "DisablePowerRequestNotice"}, {6, nullptr, "ReplyPowerRequest"}, }; // clang-format on @@ -159,11 +159,11 @@ public: private: void GetPdSession(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_USB, "called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IPdSession>(); - - LOG_DEBUG(Service_USB, "called"); } }; diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 184537daa..a3b8cec72 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -6,9 +6,10 @@ #include <array> #include <cstring> #include <memory> +#include <optional> #include <type_traits> #include <utility> -#include <boost/optional.hpp> + #include "common/alignment.h" #include "common/assert.h" #include "common/common_funcs.h" @@ -17,7 +18,9 @@ #include "common/swap.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -236,6 +239,22 @@ private: Data data{}; }; +/// Represents a parcel containing one int '0' as its data +/// Used by DetachBuffer and Disconnect +class IGBPEmptyResponseParcel : public Parcel { +protected: + void SerializeData() override { + Write(data); + } + +private: + struct Data { + u32_le unk_0; + }; + + Data data{}; +}; + class IGBPSetPreallocatedBufferRequestParcel : public Parcel { public: explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer) @@ -484,16 +503,22 @@ private: void TransactParcel(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - u32 id = rp.Pop<u32>(); - auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); - u32 flags = rp.Pop<u32>(); - auto buffer_queue = nv_flinger->GetBufferQueue(id); + const u32 id = rp.Pop<u32>(); + const auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); + const u32 flags = rp.Pop<u32>(); - LOG_DEBUG(Service_VI, "called, transaction={:X}", static_cast<u32>(transaction)); + LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, + static_cast<u32>(transaction), flags); + + auto buffer_queue = nv_flinger->GetBufferQueue(id); if (transaction == TransactionId::Connect) { IGBPConnectRequestParcel request{ctx.ReadBuffer()}; - IGBPConnectResponseParcel response{1280, 720}; + IGBPConnectResponseParcel response{ + static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) * + Settings::values.resolution_factor), + static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) * + Settings::values.resolution_factor)}; ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::SetPreallocatedBuffer) { IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; @@ -506,9 +531,9 @@ private: IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; const u32 width{request.data.width}; const u32 height{request.data.height}; - boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); - if (slot != boost::none) { + if (slot) { // Buffer is available IGBPDequeueBufferResponseParcel response{*slot}; ctx.WriteBuffer(response.Serialize()); @@ -520,13 +545,15 @@ private: Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available auto buffer_queue = nv_flinger->GetBufferQueue(id); - boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer."); + IGBPDequeueBufferResponseParcel response{*slot}; ctx.WriteBuffer(response.Serialize()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); }, - buffer_queue->GetBufferWaitEvent()); + buffer_queue->GetWritableBufferWaitEvent()); } } else if (transaction == TransactionId::RequestBuffer) { IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; @@ -553,6 +580,12 @@ private: ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::CancelBuffer) { LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); + } else if (transaction == TransactionId::Disconnect || + transaction == TransactionId::DetachBuffer) { + const auto buffer = ctx.ReadBuffer(); + + IGBPEmptyResponseParcel response{}; + ctx.WriteBuffer(response.Serialize()); } else { ASSERT_MSG(false, "Unimplemented"); } @@ -563,26 +596,27 @@ private: void AdjustRefcount(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - u32 id = rp.Pop<u32>(); - s32 addval = rp.PopRaw<s32>(); - u32 type = rp.Pop<u32>(); + const u32 id = rp.Pop<u32>(); + const s32 addval = rp.PopRaw<s32>(); + const u32 type = rp.Pop<u32>(); LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval, type); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void GetNativeHandle(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - u32 id = rp.Pop<u32>(); - u32 unknown = rp.Pop<u32>(); + const u32 id = rp.Pop<u32>(); + const u32 unknown = rp.Pop<u32>(); - auto buffer_queue = nv_flinger->GetBufferQueue(id); + LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); - // TODO(Subv): Find out what this actually is. + const auto buffer_queue = nv_flinger->GetBufferQueue(id); - LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); + // TODO(Subv): Find out what this actually is. IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(buffer_queue->GetBufferWaitEvent()); @@ -645,10 +679,12 @@ public: private: void SetLayerZ(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; - u64 layer_id = rp.Pop<u64>(); - u64 z_value = rp.Pop<u64>(); + const u64 layer_id = rp.Pop<u64>(); + const u64 z_value = rp.Pop<u64>(); + + LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}, z_value=0x{:016X}", layer_id, + z_value); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -656,30 +692,36 @@ private: void SetLayerVisibility(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - u64 layer_id = rp.Pop<u64>(); - bool visibility = rp.Pop<bool>(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + const u64 layer_id = rp.Pop<u64>(); + const bool visibility = rp.Pop<bool>(); + LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id, visibility); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); } void GetDisplayMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); if (Settings::values.use_docked_mode) { - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * + static_cast<u32>(Settings::values.resolution_factor)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * + static_cast<u32>(Settings::values.resolution_factor)); } else { - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); - rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * + static_cast<u32>(Settings::values.resolution_factor)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * + static_cast<u32>(Settings::values.resolution_factor)); } - rb.PushRaw<float>(60.0f); + rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games. rb.Push<u32>(0); - - LOG_DEBUG(Service_VI, "called"); } }; @@ -761,23 +803,27 @@ public: private: void CloseDisplay(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; - u64 display = rp.Pop<u64>(); + const u64 display = rp.Pop<u64>(); + + LOG_WARNING(Service_VI, "(STUBBED) called. display=0x{:016X}", display); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void CreateManagedLayer(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; - u32 unknown = rp.Pop<u32>(); + const u32 unknown = rp.Pop<u32>(); rp.Skip(1, false); - u64 display = rp.Pop<u64>(); - u64 aruid = rp.Pop<u64>(); + const u64 display = rp.Pop<u64>(); + const u64 aruid = rp.Pop<u64>(); + + LOG_WARNING(Service_VI, + "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}", + unknown, display, aruid); - u64 layer_id = nv_flinger->CreateLayer(display); + const u64 layer_id = nv_flinger->CreateLayer(display); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -785,10 +831,12 @@ private: } void AddToLayerStack(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; - u32 stack = rp.Pop<u32>(); - u64 layer_id = rp.Pop<u64>(); + const u32 stack = rp.Pop<u32>(); + const u64 layer_id = rp.Pop<u64>(); + + LOG_WARNING(Service_VI, "(STUBBED) called. stack=0x{:08X}, layer_id=0x{:016X}", stack, + layer_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -796,12 +844,14 @@ private: void SetLayerVisibility(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - u64 layer_id = rp.Pop<u64>(); - bool visibility = rp.Pop<bool>(); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + const u64 layer_id = rp.Pop<u64>(); + const bool visibility = rp.Pop<bool>(); + LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id, visibility); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); } std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; @@ -847,11 +897,25 @@ private: void OpenDisplay(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; - auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); - auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); + const auto name_buf = rp.PopRaw<std::array<char, 0x40>>(); - std::string name(name_buf.begin(), end); + OpenDisplayImpl(ctx, std::string_view{name_buf.data(), name_buf.size()}); + } + + void OpenDefaultDisplay(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_VI, "called"); + + OpenDisplayImpl(ctx, "Default"); + } + + void OpenDisplayImpl(Kernel::HLERequestContext& ctx, std::string_view name) { + const auto trim_pos = name.find('\0'); + + if (trim_pos != std::string_view::npos) { + name.remove_suffix(name.size() - trim_pos); + } ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); @@ -861,64 +925,85 @@ private: } void CloseDisplay(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; - u64 display_id = rp.Pop<u64>(); + const u64 display_id = rp.Pop<u64>(); + + LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + // This literally does nothing internally in the actual service itself, + // and just returns a successful result code regardless of the input. + void SetDisplayEnabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_VI, "called."); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void GetDisplayResolution(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; - u64 display_id = rp.Pop<u64>(); + const u64 display_id = rp.Pop<u64>(); + + LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); if (Settings::values.use_docked_mode) { - rb.Push(static_cast<u64>(DisplayResolution::DockedWidth)); - rb.Push(static_cast<u64>(DisplayResolution::DockedHeight)); + rb.Push(static_cast<u64>(DisplayResolution::DockedWidth) * + static_cast<u32>(Settings::values.resolution_factor)); + rb.Push(static_cast<u64>(DisplayResolution::DockedHeight) * + static_cast<u32>(Settings::values.resolution_factor)); } else { - rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth)); - rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight)); + rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) * + static_cast<u32>(Settings::values.resolution_factor)); + rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) * + static_cast<u32>(Settings::values.resolution_factor)); } } void SetLayerScalingMode(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; - u32 scaling_mode = rp.Pop<u32>(); - u64 unknown = rp.Pop<u64>(); + const u32 scaling_mode = rp.Pop<u32>(); + const u64 unknown = rp.Pop<u64>(); + + LOG_WARNING(Service_VI, "(STUBBED) called. scaling_mode=0x{:08X}, unknown=0x{:016X}", + scaling_mode, unknown); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void ListDisplays(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; DisplayInfo display_info; + display_info.width *= static_cast<u64>(Settings::values.resolution_factor); + display_info.height *= static_cast<u64>(Settings::values.resolution_factor); ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(1); - LOG_WARNING(Service_VI, "(STUBBED) called"); } void OpenLayer(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_VI, "called"); IPC::RequestParser rp{ctx}; - auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); - auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); + const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); + const auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); + + const std::string display_name(name_buf.begin(), end); - std::string display_name(name_buf.begin(), end); + const u64 layer_id = rp.Pop<u64>(); + const u64 aruid = rp.Pop<u64>(); - u64 layer_id = rp.Pop<u64>(); - u64 aruid = rp.Pop<u64>(); + LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid); - u64 display_id = nv_flinger->OpenDisplay(display_name); - u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); + const u64 display_id = nv_flinger->OpenDisplay(display_name); + const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); NativeWindow native_window{buffer_queue_id}; IPC::ResponseBuilder rb{ctx, 4}; @@ -927,17 +1012,17 @@ private: } void CreateStrayLayer(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_VI, "called"); - IPC::RequestParser rp{ctx}; - u32 flags = rp.Pop<u32>(); + const u32 flags = rp.Pop<u32>(); rp.Pop<u32>(); // padding - u64 display_id = rp.Pop<u64>(); + const u64 display_id = rp.Pop<u64>(); + + LOG_DEBUG(Service_VI, "called. flags=0x{:08X}, display_id=0x{:016X}", flags, display_id); // TODO(Subv): What's the difference between a Stray and a Managed layer? - u64 layer_id = nv_flinger->CreateLayer(display_id); - u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); + const u64 layer_id = nv_flinger->CreateLayer(display_id); + const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); NativeWindow native_window{buffer_queue_id}; IPC::ResponseBuilder rb{ctx, 6}; @@ -947,21 +1032,22 @@ private: } void DestroyStrayLayer(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - u64 layer_id = rp.Pop<u64>(); + const u64 layer_id = rp.Pop<u64>(); + + LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; - u64 display_id = rp.Pop<u64>(); + const u64 display_id = rp.Pop<u64>(); + + LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); - auto vsync_event = nv_flinger->GetVsyncEvent(display_id); + const auto vsync_event = nv_flinger->GetVsyncEvent(display_id); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); @@ -1030,9 +1116,9 @@ IApplicationDisplayService::IApplicationDisplayService( "GetIndirectDisplayTransactionService"}, {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, - {1011, nullptr, "OpenDefaultDisplay"}, + {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"}, {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, - {1101, nullptr, "SetDisplayEnabled"}, + {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"}, {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"}, {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"}, {2021, nullptr, "CloseLayer"}, |