diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/core.cpp | 12 | ||||
-rw-r--r-- | src/core/core.h | 7 | ||||
-rw-r--r-- | src/core/file_sys/vfs_real.cpp | 14 | ||||
-rw-r--r-- | src/core/frontend/emu_window.cpp | 43 | ||||
-rw-r--r-- | src/core/frontend/emu_window.h | 13 | ||||
-rw-r--r-- | src/core/frontend/input.h | 7 | ||||
-rw-r--r-- | src/core/hle/service/acc/acc.cpp | 61 | ||||
-rw-r--r-- | src/core/hle/service/acc/acc.h | 5 | ||||
-rw-r--r-- | src/core/hle/service/acc/acc_su.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/acc/acc_u0.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/acc/acc_u1.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/am/am.cpp | 9 | ||||
-rw-r--r-- | src/core/hle/service/hid/controllers/touchscreen.cpp | 127 | ||||
-rw-r--r-- | src/core/hle/service/hid/controllers/touchscreen.h | 32 | ||||
-rw-r--r-- | src/core/hle/service/lbl/lbl.cpp | 283 | ||||
-rw-r--r-- | src/core/hle/service/lm/lm.cpp | 319 | ||||
-rw-r--r-- | src/core/hle/service/lm/manager.cpp | 134 | ||||
-rw-r--r-- | src/core/hle/service/lm/manager.h | 106 | ||||
-rw-r--r-- | src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | 8 | ||||
-rw-r--r-- | src/core/reporter.cpp | 50 | ||||
-rw-r--r-- | src/core/reporter.h | 3 |
22 files changed, 807 insertions, 434 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2f6b22747..9c9b3195b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -400,8 +400,6 @@ add_library(core STATIC hle/service/ldr/ldr.h hle/service/lm/lm.cpp hle/service/lm/lm.h - hle/service/lm/manager.cpp - hle/service/lm/manager.h hle/service/mig/mig.cpp hle/service/mig/mig.h hle/service/mii/manager.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 1a2002dec..86bdc7f6b 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -36,7 +36,6 @@ #include "core/hle/service/apm/controller.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/manager.h" -#include "core/hle/service/lm/manager.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/time/time_manager.h" @@ -293,8 +292,6 @@ struct System::Impl { perf_stats->GetMeanFrametime()); } - lm_manager.Flush(); - is_powered_on = false; exit_lock = false; @@ -398,7 +395,6 @@ struct System::Impl { /// Service State Service::Glue::ARPManager arp_manager; - Service::LM::Manager lm_manager{reporter}; Service::Time::TimeManager time_manager; /// Service manager @@ -720,14 +716,6 @@ const Service::APM::Controller& System::GetAPMController() const { return impl->apm_controller; } -Service::LM::Manager& System::GetLogManager() { - return impl->lm_manager; -} - -const Service::LM::Manager& System::GetLogManager() const { - return impl->lm_manager; -} - Service::Time::TimeManager& System::GetTimeManager() { return impl->time_manager; } diff --git a/src/core/core.h b/src/core/core.h index 579a774e4..3a8e040c1 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -62,10 +62,6 @@ namespace Glue { class ARPManager; } -namespace LM { -class Manager; -} // namespace LM - namespace SM { class ServiceManager; } // namespace SM @@ -351,9 +347,6 @@ public: [[nodiscard]] Service::APM::Controller& GetAPMController(); [[nodiscard]] const Service::APM::Controller& GetAPMController() const; - [[nodiscard]] Service::LM::Manager& GetLogManager(); - [[nodiscard]] const Service::LM::Manager& GetLogManager() const; - [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index a287eebe3..a44ce6288 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -133,8 +133,11 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ } cache.erase(old_path); - file->Open(new_path, "r+b"); - cache.insert_or_assign(new_path, std::move(file)); + if (file->Open(new_path, "r+b")) { + cache.insert_or_assign(new_path, std::move(file)); + } else { + LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path); + } } else { UNREACHABLE(); return nullptr; @@ -214,9 +217,12 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, } auto file = cached.lock(); - file->Open(file_new_path, "r+b"); cache.erase(file_old_path); - cache.insert_or_assign(std::move(file_new_path), std::move(file)); + if (file->Open(file_new_path, "r+b")) { + cache.insert_or_assign(std::move(file_new_path), std::move(file)); + } else { + LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path); + } } return OpenDirectory(new_path, Mode::ReadWrite); diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 8c1193894..ee7a58b1c 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -21,21 +21,18 @@ public: std::mutex mutex; - bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false - - float touch_x = 0.0f; ///< Touchpad X-position - float touch_y = 0.0f; ///< Touchpad Y-position + Input::TouchStatus status; private: class Device : public Input::TouchDevice { public: explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {} - std::tuple<float, float, bool> GetStatus() const override { + Input::TouchStatus GetStatus() const override { if (auto state = touch_state.lock()) { std::lock_guard guard{state->mutex}; - return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed); + return state->status; } - return std::make_tuple(0.0f, 0.0f, false); + return {}; } private: @@ -79,36 +76,44 @@ std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsi return std::make_tuple(new_x, new_y); } -void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { - if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) +void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) { + if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) { return; + } + if (id >= touch_state->status.size()) { + return; + } std::lock_guard guard{touch_state->mutex}; - touch_state->touch_x = + const float x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); - touch_state->touch_y = + const float y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); - touch_state->touch_pressed = true; + touch_state->status[id] = std::make_tuple(x, y, true); } -void EmuWindow::TouchReleased() { +void EmuWindow::TouchReleased(std::size_t id) { + if (id >= touch_state->status.size()) { + return; + } std::lock_guard guard{touch_state->mutex}; - touch_state->touch_pressed = false; - touch_state->touch_x = 0; - touch_state->touch_y = 0; + touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false); } -void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { - if (!touch_state->touch_pressed) +void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) { + if (id >= touch_state->status.size()) { + return; + } + if (!std::get<2>(touch_state->status[id])) return; if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); - TouchPressed(framebuffer_x, framebuffer_y); + TouchPressed(framebuffer_x, framebuffer_y, id); } void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 276d2b906..2436c6580 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -117,18 +117,23 @@ public: * Signal that a touch pressed event has occurred (e.g. mouse click pressed) * @param framebuffer_x Framebuffer x-coordinate that was pressed * @param framebuffer_y Framebuffer y-coordinate that was pressed + * @param id Touch event ID */ - void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y); + void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id); - /// Signal that a touch released event has occurred (e.g. mouse click released) - void TouchReleased(); + /** + * Signal that a touch released event has occurred (e.g. mouse click released) + * @param id Touch event ID + */ + void TouchReleased(std::size_t id); /** * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window) * @param framebuffer_x Framebuffer x-coordinate * @param framebuffer_y Framebuffer y-coordinate + * @param id Touch event ID */ - void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); + void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id); /** * Returns currently active configuration. diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index de51a754e..f014dfea3 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -163,10 +163,11 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common using MotionDevice = InputDevice<MotionStatus>; /** - * A touch status is an object that returns a tuple of two floats and a bool. The floats are - * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed. + * A touch status is an object that returns an array of 16 tuple elements of two floats and a bool. + * The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is + * pressed. */ -using TouchStatus = std::tuple<float, float, bool>; +using TouchStatus = std::array<std::tuple<float, float, bool>, 16>; /** * A touch device is an input device that returns a touch status object diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index bb07f6ccc..3ec0e1eca 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -32,9 +32,15 @@ namespace Service::Account { -constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30}; +constexpr ResultCode ERR_INVALID_USER_ID{ErrorModule::Account, 20}; +constexpr ResultCode ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22}; +constexpr ResultCode ERR_INVALID_BUFFER{ErrorModule::Account, 30}; +constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31}; constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; +// Thumbnails are hard coded to be at least this size +constexpr std::size_t THUMBNAIL_SIZE = 0x24000; + static std::string GetImagePath(Common::UUID uuid) { return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; @@ -369,7 +375,7 @@ protected: if (user_data.size() < sizeof(ProfileData)) { LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_INVALID_BUFFER_SIZE); + rb.Push(ERR_INVALID_BUFFER); return; } @@ -402,7 +408,7 @@ protected: if (user_data.size() < sizeof(ProfileData)) { LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERR_INVALID_BUFFER_SIZE); + rb.Push(ERR_INVALID_BUFFER); return; } @@ -811,6 +817,55 @@ void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ct rb.Push(RESULT_SUCCESS); } +void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid = rp.PopRaw<Common::UUID>(); + + LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}", uuid.Format()); + + // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable + // way of confirming things like the TID, we're going to assume a non zero value for the time + // being. + constexpr u64 tid{1}; + StoreSaveDataThumbnail(ctx, uuid, tid); +} + +void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid = rp.PopRaw<Common::UUID>(); + const auto tid = rp.Pop<u64_le>(); + + LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}, tid={:016X}", uuid.Format(), tid); + StoreSaveDataThumbnail(ctx, uuid, tid); +} + +void Module::Interface::StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, + const Common::UUID& uuid, const u64 tid) { + IPC::ResponseBuilder rb{ctx, 2}; + + if (tid == 0) { + LOG_ERROR(Service_ACC, "TitleID is not valid!"); + rb.Push(ERR_INVALID_APPLICATION_ID); + return; + } + + if (!uuid) { + LOG_ERROR(Service_ACC, "User ID is not valid!"); + rb.Push(ERR_INVALID_USER_ID); + return; + } + const auto thumbnail_size = ctx.GetReadBufferSize(); + if (thumbnail_size != THUMBNAIL_SIZE) { + LOG_ERROR(Service_ACC, "Buffer size is empty! size={:X} expecting {:X}", thumbnail_size, + THUMBNAIL_SIZE); + rb.Push(ERR_INVALID_BUFFER_SIZE); + return; + } + + // TODO(ogniK): Construct save data thumbnail + rb.Push(RESULT_SUCCESS); +} + 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 diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index ab8edc049..0e3ad8ec6 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -4,6 +4,7 @@ #pragma once +#include "common/uuid.h" #include "core/hle/service/glue/manager.h" #include "core/hle/service/service.h" @@ -36,9 +37,13 @@ public: void ListQualifiedUsers(Kernel::HLERequestContext& ctx); void LoadOpenContext(Kernel::HLERequestContext& ctx); void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); + void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx); + void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx); private: ResultCode InitializeApplicationInfoBase(); + void StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, const Common::UUID& uuid, + const u64 tid); enum class ApplicationType : u32_le { GameCard = 0, diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index d2bb8c2c8..49b22583e 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -29,7 +29,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {104, nullptr, "GetProfileUpdateNotifier"}, {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ - {110, nullptr, "StoreSaveDataThumbnail"}, + {110, &ACC_SU::StoreSaveDataThumbnailSystem, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 75a24f8f5..8d66d180d 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -26,7 +26,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, {102, nullptr, "AuthenticateApplicationAsync"}, {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ - {110, nullptr, "StoreSaveDataThumbnail"}, + {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {120, nullptr, "CreateGuestLoginRequest"}, {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+ diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index a4aa5316a..951081cd0 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -29,7 +29,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {104, nullptr, "GetProfileUpdateNotifier"}, {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+ {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+ - {110, nullptr, "StoreSaveDataThumbnail"}, + {110, &ACC_U1::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+ diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index c9808060a..1743bcb2b 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -856,7 +856,7 @@ public: {25, nullptr, "Terminate"}, {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, - {60, nullptr, "PresetLibraryAppletGpuTimeSliceZero"}, + {60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"}, {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, {102, nullptr, "PushExtraStorage"}, @@ -900,6 +900,13 @@ private: rb.Push(applet->GetStatus()); } + void PresetLibraryAppletGpuTimeSliceZero(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + void Start(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 0df395e85..5219f2dad 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cstring> #include "common/common_types.h" #include "core/core_timing.h" @@ -16,7 +17,13 @@ constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {} Controller_Touchscreen::~Controller_Touchscreen() = default; -void Controller_Touchscreen::OnInit() {} +void Controller_Touchscreen::OnInit() { + for (std::size_t id = 0; id < MAX_FINGERS; ++id) { + mouse_finger_id[id] = MAX_FINGERS; + keyboard_finger_id[id] = MAX_FINGERS; + udp_finger_id[id] = MAX_FINGERS; + } +} void Controller_Touchscreen::OnRelease() {} @@ -40,38 +47,106 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin cur_entry.sampling_number = last_entry.sampling_number + 1; cur_entry.sampling_number2 = cur_entry.sampling_number; - bool pressed = false; - float x, y; - std::tie(x, y, pressed) = touch_device->GetStatus(); - auto& touch_entry = cur_entry.states[0]; - touch_entry.attribute.raw = 0; - if (!pressed && touch_btn_device) { - std::tie(x, y, pressed) = touch_btn_device->GetStatus(); + const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); + const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); + for (std::size_t id = 0; id < mouse_status.size(); ++id) { + mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]); + udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]); } - 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 = 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 = core_timing.GetCPUTicks(); - touch_entry.delta_time = tick - last_touch; - last_touch = tick; - touch_entry.finger = Settings::values.touchscreen.finger; - cur_entry.entry_count = 1; - } else { - cur_entry.entry_count = 0; + + if (Settings::values.use_touch_from_button) { + const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus(); + for (std::size_t id = 0; id < mouse_status.size(); ++id) { + keyboard_finger_id[id] = + UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]); + } } + std::array<Finger, 16> active_fingers; + const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), + [](const auto& finger) { return finger.pressed; }); + const auto active_fingers_count = + static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); + + const u64 tick = core_timing.GetCPUTicks(); + cur_entry.entry_count = static_cast<s32_le>(active_fingers_count); + for (std::size_t id = 0; id < MAX_FINGERS; ++id) { + auto& touch_entry = cur_entry.states[id]; + if (id < active_fingers_count) { + touch_entry.x = static_cast<u16>(active_fingers[id].x * Layout::ScreenUndocked::Width); + touch_entry.y = static_cast<u16>(active_fingers[id].y * Layout::ScreenUndocked::Height); + 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; + touch_entry.delta_time = tick - active_fingers[id].last_touch; + fingers[active_fingers[id].id].last_touch = tick; + touch_entry.finger = active_fingers[id].id; + touch_entry.attribute.raw = active_fingers[id].attribute.raw; + } else { + // Clear touch entry + touch_entry.attribute.raw = 0; + touch_entry.x = 0; + touch_entry.y = 0; + touch_entry.diameter_x = 0; + touch_entry.diameter_y = 0; + touch_entry.rotation_angle = 0; + touch_entry.delta_time = 0; + touch_entry.finger = 0; + } + } std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory)); } void Controller_Touchscreen::OnLoadInputDevices() { - touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device); - if (Settings::values.use_touch_from_button) { - touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button"); - } else { - touch_btn_device.reset(); + touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window"); + touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp"); + touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button"); +} + +std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const { + std::size_t first_free_id = 0; + while (first_free_id < MAX_FINGERS) { + if (!fingers[first_free_id].pressed) { + return first_free_id; + } else { + first_free_id++; + } + } + return std::nullopt; +} + +std::size_t Controller_Touchscreen::UpdateTouchInputEvent( + const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) { + const auto& [x, y, pressed] = touch_input; + if (pressed) { + Attributes attribute{}; + if (finger_id == MAX_FINGERS) { + const auto first_free_id = GetUnusedFingerID(); + if (!first_free_id) { + // Invalid finger id do nothing + return MAX_FINGERS; + } + finger_id = first_free_id.value(); + fingers[finger_id].pressed = true; + fingers[finger_id].id = static_cast<u32_le>(finger_id); + attribute.start_touch.Assign(1); + } + fingers[finger_id].x = x; + fingers[finger_id].y = y; + fingers[finger_id].attribute = attribute; + return finger_id; } + + if (finger_id != MAX_FINGERS) { + if (!fingers[finger_id].attribute.end_touch) { + fingers[finger_id].attribute.end_touch.Assign(1); + fingers[finger_id].attribute.start_touch.Assign(0); + return finger_id; + } + fingers[finger_id].pressed = false; + } + + return MAX_FINGERS; } + } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 4d9042adc..784124e25 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -30,6 +30,18 @@ public: void OnLoadInputDevices() override; private: + static constexpr std::size_t MAX_FINGERS = 16; + + // Returns an unused finger id, if there is no fingers available std::nullopt will be returned + std::optional<std::size_t> GetUnusedFingerID() const; + + // If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no + // changes will be made. Updates the coordinates if the finger id it's already set. If the touch + // ends delays the output by one frame to set the end_touch flag before finally freeing the + // finger id + std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input, + std::size_t finger_id); + struct Attributes { union { u32 raw{}; @@ -55,7 +67,7 @@ private: s64_le sampling_number; s64_le sampling_number2; s32_le entry_count; - std::array<TouchState, 16> states; + std::array<TouchState, MAX_FINGERS> states; }; static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size"); @@ -66,9 +78,23 @@ private: }; static_assert(sizeof(TouchScreenSharedMemory) == 0x3000, "TouchScreenSharedMemory is an invalid size"); + + struct Finger { + u64_le last_touch{}; + float x{}; + float y{}; + u32_le id{}; + bool pressed{}; + Attributes attribute; + }; + TouchScreenSharedMemory shared_memory{}; - std::unique_ptr<Input::TouchDevice> touch_device; + std::unique_ptr<Input::TouchDevice> touch_mouse_device; + std::unique_ptr<Input::TouchDevice> touch_udp_device; std::unique_ptr<Input::TouchDevice> touch_btn_device; - s64_le last_touch{}; + std::array<std::size_t, MAX_FINGERS> mouse_finger_id; + std::array<std::size_t, MAX_FINGERS> keyboard_finger_id; + std::array<std::size_t, MAX_FINGERS> udp_finger_id; + std::array<Finger, MAX_FINGERS> fingers; }; } // namespace Service::HID diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index 6ad3a2877..f4490f3d9 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp @@ -20,30 +20,30 @@ public: static const FunctionInfo functions[] = { {0, nullptr, "SaveCurrentSetting"}, {1, nullptr, "LoadCurrentSetting"}, - {2, nullptr, "SetCurrentBrightnessSetting"}, - {3, nullptr, "GetCurrentBrightnessSetting"}, + {2, &LBL::SetCurrentBrightnessSetting, "SetCurrentBrightnessSetting"}, + {3, &LBL::GetCurrentBrightnessSetting, "GetCurrentBrightnessSetting"}, {4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"}, {5, nullptr, "GetBrightnessSettingAppliedToBacklight"}, - {6, nullptr, "SwitchBacklightOn"}, - {7, nullptr, "SwitchBacklightOff"}, - {8, nullptr, "GetBacklightSwitchStatus"}, - {9, nullptr, "EnableDimming"}, - {10, nullptr, "DisableDimming"}, - {11, nullptr, "IsDimmingEnabled"}, - {12, nullptr, "EnableAutoBrightnessControl"}, - {13, nullptr, "DisableAutoBrightnessControl"}, - {14, nullptr, "IsAutoBrightnessControlEnabled"}, - {15, nullptr, "SetAmbientLightSensorValue"}, - {16, nullptr, "GetAmbientLightSensorValue"}, - {17, nullptr, "SetBrightnessReflectionDelayLevel"}, - {18, nullptr, "GetBrightnessReflectionDelayLevel"}, - {19, nullptr, "SetCurrentBrightnessMapping"}, - {20, nullptr, "GetCurrentBrightnessMapping"}, - {21, nullptr, "SetCurrentAmbientLightSensorMapping"}, - {22, nullptr, "GetCurrentAmbientLightSensorMapping"}, - {23, nullptr, "IsAmbientLightSensorAvailable"}, - {24, nullptr, "SetCurrentBrightnessSettingForVrMode"}, - {25, nullptr, "GetCurrentBrightnessSettingForVrMode"}, + {6, &LBL::SwitchBacklightOn, "SwitchBacklightOn"}, + {7, &LBL::SwitchBacklightOff, "SwitchBacklightOff"}, + {8, &LBL::GetBacklightSwitchStatus, "GetBacklightSwitchStatus"}, + {9, &LBL::EnableDimming, "EnableDimming"}, + {10, &LBL::DisableDimming, "DisableDimming"}, + {11, &LBL::IsDimmingEnabled, "IsDimmingEnabled"}, + {12, &LBL::EnableAutoBrightnessControl, "EnableAutoBrightnessControl"}, + {13, &LBL::DisableAutoBrightnessControl, "DisableAutoBrightnessControl"}, + {14, &LBL::IsAutoBrightnessControlEnabled, "IsAutoBrightnessControlEnabled"}, + {15, &LBL::SetAmbientLightSensorValue, "SetAmbientLightSensorValue"}, + {16, &LBL::GetAmbientLightSensorValue, "GetAmbientLightSensorValue"}, + {17, &LBL::SetBrightnessReflectionDelayLevel, "SetBrightnessReflectionDelayLevel"}, + {18, &LBL::GetBrightnessReflectionDelayLevel, "GetBrightnessReflectionDelayLevel"}, + {19, &LBL::SetCurrentBrightnessMapping, "SetCurrentBrightnessMapping"}, + {20, &LBL::GetCurrentBrightnessMapping, "GetCurrentBrightnessMapping"}, + {21, &LBL::SetCurrentAmbientLightSensorMapping, "SetCurrentAmbientLightSensorMapping"}, + {22, &LBL::GetCurrentAmbientLightSensorMapping, "GetCurrentAmbientLightSensorMapping"}, + {23, &LBL::IsAmbientLightSensorAvailable, "IsAmbientLightSensorAvailable"}, + {24, &LBL::SetCurrentBrightnessSettingForVrMode, "SetCurrentBrightnessSettingForVrMode"}, + {25, &LBL::GetCurrentBrightnessSettingForVrMode, "GetCurrentBrightnessSettingForVrMode"}, {26, &LBL::EnableVrMode, "EnableVrMode"}, {27, &LBL::DisableVrMode, "DisableVrMode"}, {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, @@ -55,6 +55,237 @@ public: } private: + enum class BacklightSwitchStatus : u32 { + Off = 0, + On = 1, + }; + + void SetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto brightness = rp.Pop<float>(); + + if (!std::isfinite(brightness)) { + LOG_ERROR(Service_LBL, "Brightness is infinite!"); + brightness = 0.0f; + } + + LOG_DEBUG(Service_LBL, "called brightness={}", brightness); + + current_brightness = brightness; + update_instantly = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto brightness = current_brightness; + if (!std::isfinite(brightness)) { + LOG_ERROR(Service_LBL, "Brightness is infinite!"); + brightness = 0.0f; + } + + LOG_DEBUG(Service_LBL, "called brightness={}", brightness); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(brightness); + } + + void SwitchBacklightOn(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto fade_time = rp.Pop<u64_le>(); + LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time); + + backlight_enabled = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SwitchBacklightOff(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto fade_time = rp.Pop<u64_le>(); + LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time); + + backlight_enabled = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetBacklightSwitchStatus(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum<BacklightSwitchStatus>(backlight_enabled ? BacklightSwitchStatus::On + : BacklightSwitchStatus::Off); + } + + void EnableDimming(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + dimming = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void DisableDimming(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + dimming = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void IsDimmingEnabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(dimming); + } + + void EnableAutoBrightnessControl(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + auto_brightness = true; + update_instantly = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void DisableAutoBrightnessControl(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + auto_brightness = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void IsAutoBrightnessControlEnabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(auto_brightness); + } + + void SetAmbientLightSensorValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto light_value = rp.Pop<float>(); + + LOG_DEBUG(Service_LBL, "called light_value={}", light_value); + + ambient_light_value = light_value; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetAmbientLightSensorValue(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(ambient_light_value); + } + + void SetBrightnessReflectionDelayLevel(Kernel::HLERequestContext& ctx) { + // This is Intentional, this function does absolutely nothing + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetBrightnessReflectionDelayLevel(Kernel::HLERequestContext& ctx) { + // This is intentional, the function is hard coded to return 0.0f on hardware + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(0.0f); + } + + void SetCurrentBrightnessMapping(Kernel::HLERequestContext& ctx) { + // This is Intentional, this function does absolutely nothing + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetCurrentBrightnessMapping(Kernel::HLERequestContext& ctx) { + // This is Intentional, this function does absolutely nothing + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + // This function is suppose to return something but it seems like it doesn't + } + + void SetCurrentAmbientLightSensorMapping(Kernel::HLERequestContext& ctx) { + // This is Intentional, this function does absolutely nothing + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetCurrentAmbientLightSensorMapping(Kernel::HLERequestContext& ctx) { + // This is Intentional, this function does absolutely nothing + LOG_DEBUG(Service_LBL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + // This function is suppose to return something but it seems like it doesn't + } + + void IsAmbientLightSensorAvailable(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_LBL, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + // TODO(ogniK): Only return true if there's no device error + rb.Push(true); + } + + void SetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto brightness = rp.Pop<float>(); + + if (!std::isfinite(brightness)) { + LOG_ERROR(Service_LBL, "Brightness is infinite!"); + brightness = 0.0f; + } + + LOG_DEBUG(Service_LBL, "called brightness={}", brightness); + + current_vr_brightness = brightness; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto brightness = current_vr_brightness; + if (!std::isfinite(brightness)) { + LOG_ERROR(Service_LBL, "Brightness is infinite!"); + brightness = 0.0f; + } + + LOG_DEBUG(Service_LBL, "called brightness={}", brightness); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(brightness); + } + void EnableVrMode(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_LBL, "called"); @@ -82,6 +313,14 @@ private: } bool vr_mode_enabled = false; + float current_brightness = 1.0f; + float backlight_brightness = 1.0f; + float ambient_light_value = 0.0f; + float current_vr_brightness = 1.0f; + bool dimming = true; + bool backlight_enabled = true; + bool update_instantly = false; + bool auto_brightness = false; // TODO(ogniK): Move to system settings }; void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 8e49b068c..2a6d43d2a 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -5,22 +5,71 @@ #include <sstream> #include <string> +#include <optional> +#include <unordered_map> +#include <boost/container_hash/hash.hpp> #include "common/logging/log.h" #include "common/scope_exit.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/lm/lm.h" -#include "core/hle/service/lm/manager.h" #include "core/hle/service/service.h" #include "core/memory.h" namespace Service::LM { +enum class LogSeverity : u8 { + Trace = 0, + Info = 1, + Warning = 2, + Error = 3, + Fatal = 4, +}; + +// To keep flags out of hashing as well as the payload size +struct LogPacketHeaderEntry { + u64_le pid{}; + u64_le tid{}; + LogSeverity severity{}; + u8 verbosity{}; + + auto operator<=>(const LogPacketHeaderEntry&) const = default; +}; +} // namespace Service::LM + +namespace std { +template <> +struct hash<Service::LM::LogPacketHeaderEntry> { + std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const noexcept { + std::size_t seed{}; + boost::hash_combine(seed, k.pid); + boost::hash_combine(seed, k.tid); + boost::hash_combine(seed, k.severity); + boost::hash_combine(seed, k.verbosity); + return seed; + }; +}; +} // namespace std + +namespace Service::LM { + +enum class LogDestination : u32 { + TargetManager = 1 << 0, + Uart = 1 << 1, + UartSleep = 1 << 2, + All = 0xffff, +}; +DECLARE_ENUM_FLAG_OPERATORS(LogDestination); + +enum class LogPacketFlags : u8 { + Head = 1 << 0, + Tail = 1 << 1, + LittleEndian = 1 << 2, +}; +DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags); class ILogger final : public ServiceFramework<ILogger> { public: - explicit ILogger(Core::System& system_) - : ServiceFramework{system_, "ILogger"}, manager{system_.GetLogManager()}, - memory{system_.Memory()} { + explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} { static const FunctionInfo functions[] = { {0, &ILogger::Log, "Log"}, {1, &ILogger::SetDestination, "SetDestination"}, @@ -30,54 +79,262 @@ public: private: void Log(Kernel::HLERequestContext& ctx) { + std::size_t offset{}; + const auto data = ctx.ReadBuffer(); + // This function only succeeds - Get that out of the way IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - // Read MessageHeader, despite not doing anything with it right now - MessageHeader header{}; - VAddr addr{ctx.BufferDescriptorX()[0].Address()}; - const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size}; - memory.ReadBlock(addr, &header, sizeof(MessageHeader)); - addr += sizeof(MessageHeader); - - FieldMap fields; - while (addr < end_addr) { - const auto field = static_cast<Field>(memory.Read8(addr++)); - const auto length = memory.Read8(addr++); + if (data.size() < sizeof(LogPacketHeader)) { + LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size()); + return; + } - if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) { - ++addr; - } + LogPacketHeader header{}; + std::memcpy(&header, data.data(), sizeof(LogPacketHeader)); + offset += sizeof(LogPacketHeader); - SCOPE_EXIT({ addr += length; }); + LogPacketHeaderEntry entry{ + .pid = header.pid, + .tid = header.tid, + .severity = header.severity, + .verbosity = header.verbosity, + }; - if (field == Field::Skip) { - continue; + if (True(header.flags & LogPacketFlags::Head)) { + std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader)); + std::memcpy(tmp.data(), data.data() + offset, tmp.size()); + entries[entry] = std::move(tmp); + } else { + // Append to existing entry + if (!entries.contains(entry)) { + LOG_ERROR(Service_LM, "Log entry does not exist!"); + return; } + std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader)); - std::vector<u8> data(length); - memory.ReadBlock(addr, data.data(), length); - fields.emplace(field, std::move(data)); + auto& existing_entry = entries[entry]; + const auto base = existing_entry.size(); + existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader))); + std::memcpy(existing_entry.data() + base, data.data() + offset, + (data.size() - sizeof(LogPacketHeader))); } - manager.Log({header, std::move(fields)}); + if (True(header.flags & LogPacketFlags::Tail)) { + auto it = entries.find(entry); + if (it == entries.end()) { + LOG_ERROR(Service_LM, "Log entry does not exist!"); + return; + } + ParseLog(it->first, it->second); + entries.erase(it); + } } void SetDestination(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto destination = rp.PopEnum<DestinationFlag>(); - - LOG_DEBUG(Service_LM, "called, destination={:08X}", destination); + const auto log_destination = rp.PopEnum<LogDestination>(); - manager.SetDestination(destination); + LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination)); + destination = log_destination; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } - Manager& manager; - Core::Memory::Memory& memory; + u32 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) { + u32 result{}; + u32 shift{}; + do { + result |= (data[offset] & 0x7f) << shift; + shift += 7; + offset++; + if (offset >= data.size()) { + break; + } + } while ((data[offset] & 0x80) != 0); + return result; + } + + std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset, + std::size_t length) { + if (length == 0) { + return std::nullopt; + } + const auto length_to_read = std::min(length, data.size() - offset); + + std::string output(length_to_read, '\0'); + std::memcpy(output.data(), data.data() + offset, length_to_read); + offset += length_to_read; + return output; + } + + u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) { + ASSERT(length == sizeof(u32)); + u32_le output{}; + std::memcpy(&output, data.data() + offset, sizeof(u32)); + offset += length; + return output; + } + + u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) { + ASSERT(length == sizeof(u64)); + u64_le output{}; + std::memcpy(&output, data.data() + offset, sizeof(u64)); + offset += length; + return output; + } + + void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) { + // Possible entries + std::optional<std::string> text_log; + std::optional<u32> line_number; + std::optional<std::string> file_name; + std::optional<std::string> function_name; + std::optional<std::string> module_name; + std::optional<std::string> thread_name; + std::optional<u64> log_pack_drop_count; + std::optional<s64> user_system_clock; + std::optional<std::string> process_name; + + std::size_t offset{}; + while (offset < log_data.size()) { + const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset)); + const auto chunk_size = ReadLeb128(log_data, offset); + + switch (key) { + case LogDataChunkKey::LogSessionBegin: + case LogDataChunkKey::LogSessionEnd: + break; + case LogDataChunkKey::TextLog: + text_log = ReadString(log_data, offset, chunk_size); + break; + case LogDataChunkKey::LineNumber: + line_number = ReadAsU32(log_data, offset, chunk_size); + break; + case LogDataChunkKey::FileName: + file_name = ReadString(log_data, offset, chunk_size); + break; + case LogDataChunkKey::FunctionName: + function_name = ReadString(log_data, offset, chunk_size); + break; + case LogDataChunkKey::ModuleName: + module_name = ReadString(log_data, offset, chunk_size); + break; + case LogDataChunkKey::ThreadName: + thread_name = ReadString(log_data, offset, chunk_size); + break; + case LogDataChunkKey::LogPacketDropCount: + log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size); + break; + case LogDataChunkKey::UserSystemClock: + user_system_clock = ReadAsU64(log_data, offset, chunk_size); + break; + case LogDataChunkKey::ProcessName: + process_name = ReadString(log_data, offset, chunk_size); + break; + } + } + + std::string output_log{}; + if (process_name) { + output_log += fmt::format("Process: {}\n", *process_name); + } + if (module_name) { + output_log += fmt::format("Module: {}\n", *module_name); + } + if (file_name) { + output_log += fmt::format("File: {}\n", *file_name); + } + if (function_name) { + output_log += fmt::format("Function: {}\n", *function_name); + } + if (line_number && *line_number != 0) { + output_log += fmt::format("Line: {}\n", *line_number); + } + output_log += fmt::format("ProcessID: {:X}\n", entry.pid); + output_log += fmt::format("ThreadID: {:X}\n", entry.tid); + + if (text_log) { + output_log += fmt::format("Log Text: {}\n", *text_log); + } + + switch (entry.severity) { + case LogSeverity::Trace: + LOG_DEBUG(Service_LM, "LogManager DEBUG ({}):\n{}", DestinationToString(destination), + output_log); + break; + case LogSeverity::Info: + LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination), + output_log); + break; + case LogSeverity::Warning: + LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}", + DestinationToString(destination), output_log); + break; + case LogSeverity::Error: + LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination), + output_log); + break; + case LogSeverity::Fatal: + LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination), + output_log); + break; + default: + LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}", + DestinationToString(destination), output_log); + break; + } + } + + static std::string DestinationToString(LogDestination destination) { + if (True(destination & LogDestination::All)) { + return "TargetManager | Uart | UartSleep"; + } + std::string output{}; + if (True(destination & LogDestination::TargetManager)) { + output += "| TargetManager"; + } + if (True(destination & LogDestination::Uart)) { + output += "| Uart"; + } + if (True(destination & LogDestination::UartSleep)) { + output += "| UartSleep"; + } + if (output.length() > 0) { + return output.substr(2); + } + return "No Destination"; + } + + enum class LogDataChunkKey : u32 { + LogSessionBegin = 0, + LogSessionEnd = 1, + TextLog = 2, + LineNumber = 3, + FileName = 4, + FunctionName = 5, + ModuleName = 6, + ThreadName = 7, + LogPacketDropCount = 8, + UserSystemClock = 9, + ProcessName = 10, + }; + + struct LogPacketHeader { + u64_le pid{}; + u64_le tid{}; + LogPacketFlags flags{}; + INSERT_PADDING_BYTES(1); + LogSeverity severity{}; + u8 verbosity{}; + u32_le payload_size{}; + }; + static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size"); + + std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{}; + LogDestination destination{LogDestination::All}; }; class LM final : public ServiceFramework<LM> { diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp deleted file mode 100644 index 3ee2374e7..000000000 --- a/src/core/hle/service/lm/manager.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/string_util.h" -#include "core/hle/service/lm/manager.h" -#include "core/reporter.h" - -namespace Service::LM { - -std::ostream& operator<<(std::ostream& os, DestinationFlag dest) { - std::vector<std::string> array; - const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) { - if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) { - array.emplace_back(std::move(name)); - } - }; - - check_single_flag(DestinationFlag::Default, "Default"); - check_single_flag(DestinationFlag::UART, "UART"); - check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)"); - - os << "["; - for (const auto& entry : array) { - os << entry << ", "; - } - return os << "]"; -} - -std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) { - switch (severity) { - case MessageHeader::Severity::Trace: - return os << "Trace"; - case MessageHeader::Severity::Info: - return os << "Info"; - case MessageHeader::Severity::Warning: - return os << "Warning"; - case MessageHeader::Severity::Error: - return os << "Error"; - case MessageHeader::Severity::Critical: - return os << "Critical"; - default: - return os << fmt::format("{:08X}", static_cast<u32>(severity)); - } -} - -std::ostream& operator<<(std::ostream& os, Field field) { - switch (field) { - case Field::Skip: - return os << "Skip"; - case Field::Message: - return os << "Message"; - case Field::Line: - return os << "Line"; - case Field::Filename: - return os << "Filename"; - case Field::Function: - return os << "Function"; - case Field::Module: - return os << "Module"; - case Field::Thread: - return os << "Thread"; - default: - return os << fmt::format("{:08X}", static_cast<u32>(field)); - } -} - -std::string FormatField(Field type, const std::vector<u8>& data) { - switch (type) { - case Field::Skip: - return ""; - case Field::Line: - if (data.size() >= sizeof(u32)) { - u32 line; - std::memcpy(&line, data.data(), sizeof(u32)); - return fmt::format("{}", line); - } - return "[ERROR DECODING LINE NUMBER]"; - case Field::Message: - case Field::Filename: - case Field::Function: - case Field::Module: - case Field::Thread: - return Common::StringFromFixedZeroTerminatedBuffer( - reinterpret_cast<const char*>(data.data()), data.size()); - default: - UNIMPLEMENTED_MSG("Unimplemented field type={}", type); - return ""; - } -} - -Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {} - -Manager::~Manager() = default; - -void Manager::SetEnabled(bool enabled) { - this->enabled = enabled; -} - -void Manager::SetDestination(DestinationFlag destination) { - this->destination = destination; -} - -void Manager::Log(LogMessage message) { - if (message.header.IsHeadLog()) { - InitializeLog(); - } - - current_log.emplace_back(std::move(message)); - - if (current_log.back().header.IsTailLog()) { - FinalizeLog(); - } -} - -void Manager::Flush() { - FinalizeLog(); -} - -void Manager::InitializeLog() { - current_log.clear(); - - LOG_INFO(Service_LM, "Initialized new log session"); -} - -void Manager::FinalizeLog() { - reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log)); - - LOG_INFO(Service_LM, "Finalized current log session"); -} - -} // namespace Service::LM diff --git a/src/core/hle/service/lm/manager.h b/src/core/hle/service/lm/manager.h deleted file mode 100644 index 544e636ba..000000000 --- a/src/core/hle/service/lm/manager.h +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <map> -#include <ostream> -#include <vector> -#include "common/bit_field.h" -#include "common/common_types.h" -#include "common/swap.h" - -namespace Core { -class Reporter; -} - -namespace Service::LM { - -enum class DestinationFlag : u32 { - Default = 1, - UART = 2, - UARTSleeping = 4, - - All = 0xFFFF, -}; - -struct MessageHeader { - enum Flags : u32_le { - IsHead = 1, - IsTail = 2, - }; - enum Severity : u32_le { - Trace, - Info, - Warning, - Error, - Critical, - }; - - u64_le pid; - u64_le thread_context; - union { - BitField<0, 16, Flags> flags; - BitField<16, 8, Severity> severity; - BitField<24, 8, u32> verbosity; - }; - u32_le payload_size; - - bool IsHeadLog() const { - return flags & IsHead; - } - bool IsTailLog() const { - return flags & IsTail; - } -}; -static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size"); - -enum class Field : u8 { - Skip = 1, - Message = 2, - Line = 3, - Filename = 4, - Function = 5, - Module = 6, - Thread = 7, -}; - -std::ostream& operator<<(std::ostream& os, DestinationFlag dest); -std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity); -std::ostream& operator<<(std::ostream& os, Field field); - -using FieldMap = std::map<Field, std::vector<u8>>; - -struct LogMessage { - MessageHeader header; - FieldMap fields; -}; - -std::string FormatField(Field type, const std::vector<u8>& data); - -class Manager { -public: - explicit Manager(Core::Reporter& reporter); - ~Manager(); - - void SetEnabled(bool enabled); - void SetDestination(DestinationFlag destination); - - void Log(LogMessage message); - - void Flush(); - -private: - void InitializeLog(); - void FinalizeLog(); - - bool enabled = true; - DestinationFlag destination = DestinationFlag::All; - - std::vector<LogMessage> current_log; - - Core::Reporter& reporter; -}; - -} // namespace Service::LM diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index fea3b7b9f..060599bab 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -155,7 +155,13 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::ve return NvResult::BadParameter; } if (events_interface.registered[event_id]) { - return NvResult::BadParameter; + const auto event_state = events_interface.status[event_id]; + if (event_state != EventState::Free) { + LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event"); + events_interface.UnregisterEvent(event_id); + } else { + return NvResult::BadParameter; + } } events_interface.RegisterEvent(event_id); return NvResult::Success; diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 0becdf642..f199c3362 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -20,7 +20,6 @@ #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" #include "core/hle/result.h" -#include "core/hle/service/lm/manager.h" #include "core/memory.h" #include "core/reporter.h" #include "core/settings.h" @@ -360,55 +359,6 @@ void Reporter::SaveErrorReport(u64 title_id, ResultCode result, SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp)); } -void Reporter::SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const { - if (!IsReportingEnabled()) { - return; - } - - const auto timestamp = GetTimestamp(); - json out; - - out["yuzu_version"] = GetYuzuVersionData(); - out["report_common"] = - GetReportCommonData(system.CurrentProcess()->GetTitleID(), RESULT_SUCCESS, timestamp); - - out["log_destination"] = - fmt::format("{}", static_cast<Service::LM::DestinationFlag>(destination)); - - auto json_messages = json::array(); - std::transform(messages.begin(), messages.end(), std::back_inserter(json_messages), - [](const Service::LM::LogMessage& message) { - json out; - out["is_head"] = fmt::format("{}", message.header.IsHeadLog()); - out["is_tail"] = fmt::format("{}", message.header.IsTailLog()); - out["pid"] = fmt::format("{:016X}", message.header.pid); - out["thread_context"] = - fmt::format("{:016X}", message.header.thread_context); - out["payload_size"] = fmt::format("{:016X}", message.header.payload_size); - out["flags"] = fmt::format("{:04X}", message.header.flags.Value()); - out["severity"] = fmt::format("{}", message.header.severity.Value()); - out["verbosity"] = fmt::format("{:02X}", message.header.verbosity); - - auto fields = json::array(); - std::transform(message.fields.begin(), message.fields.end(), - std::back_inserter(fields), [](const auto& kv) { - json out; - out["type"] = fmt::format("{}", kv.first); - out["data"] = - Service::LM::FormatField(kv.first, kv.second); - return out; - }); - - out["fields"] = std::move(fields); - return out; - }); - - out["log_messages"] = std::move(json_messages); - - SaveToFile(std::move(out), - GetPath("log_report", system.CurrentProcess()->GetTitleID(), timestamp)); -} - void Reporter::SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, std::string log_message) const { if (!IsReportingEnabled()) diff --git a/src/core/reporter.h b/src/core/reporter.h index 86d760cf0..b2c2d9a2e 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h @@ -72,9 +72,6 @@ public: void SaveFilesystemAccessReport(Service::FileSystem::LogMode log_mode, std::string log_message) const; - // Used by lm services - void SaveLogReport(u32 destination, std::vector<Service::LM::LogMessage> messages) const; - // Can be used anywhere to generate a backtrace and general info report at any point during // execution. Not intended to be used for anything other than debugging or testing. void SaveUserReport() const; |