summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp12
-rw-r--r--src/core/core.h7
-rw-r--r--src/core/file_sys/vfs_real.cpp14
-rw-r--r--src/core/frontend/emu_window.cpp43
-rw-r--r--src/core/frontend/emu_window.h13
-rw-r--r--src/core/frontend/input.h7
-rw-r--r--src/core/hle/service/acc/acc.cpp61
-rw-r--r--src/core/hle/service/acc/acc.h5
-rw-r--r--src/core/hle/service/acc/acc_su.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp2
-rw-r--r--src/core/hle/service/am/am.cpp9
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp127
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h32
-rw-r--r--src/core/hle/service/lbl/lbl.cpp283
-rw-r--r--src/core/hle/service/lm/lm.cpp319
-rw-r--r--src/core/hle/service/lm/manager.cpp134
-rw-r--r--src/core/hle/service/lm/manager.h106
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp8
-rw-r--r--src/core/reporter.cpp50
-rw-r--r--src/core/reporter.h3
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;