From 301702e548844e3df5099382e90766966940d0a9 Mon Sep 17 00:00:00 2001
From: Narr the Reg <juangerman-13@hotmail.com>
Date: Fri, 5 Aug 2022 22:33:49 -0500
Subject: [PATCH] hid: core: Properly emulate controller color and battery
 level

---
 src/core/hid/emulated_controller.cpp          | 26 +++++--
 src/core/hid/emulated_controller.h            |  7 ++
 src/core/hid/hid_types.h                      | 12 ++-
 src/core/hle/service/hid/controllers/npad.cpp | 77 +++++++++++++++----
 4 files changed, 97 insertions(+), 25 deletions(-)

diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 8c3895937d..085ff3fdac 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -84,18 +84,19 @@ void EmulatedController::ReloadFromSettings() {
         motion_params[index] = Common::ParamPackage(player.motions[index]);
     }
 
+    controller.colors_state.fullkey = {
+        .body = GetNpadColor(player.body_color_left),
+        .button = GetNpadColor(player.button_color_left),
+    };
     controller.colors_state.left = {
-        .body = player.body_color_left,
-        .button = player.button_color_left,
+        .body = GetNpadColor(player.body_color_left),
+        .button = GetNpadColor(player.button_color_left),
     };
-
-    controller.colors_state.right = {
-        .body = player.body_color_right,
-        .button = player.button_color_right,
+    controller.colors_state.left = {
+        .body = GetNpadColor(player.body_color_right),
+        .button = GetNpadColor(player.button_color_right),
     };
 
-    controller.colors_state.fullkey = controller.colors_state.left;
-
     // Other or debug controller should always be a pro controller
     if (npad_id_type != NpadIdType::Other) {
         SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
@@ -1310,6 +1311,15 @@ const CameraState& EmulatedController::GetCamera() const {
     return controller.camera_state;
 }
 
+NpadColor EmulatedController::GetNpadColor(u32 color) {
+    return {
+        .r = static_cast<u8>((color >> 16) & 0xFF),
+        .g = static_cast<u8>((color >> 8) & 0xFF),
+        .b = static_cast<u8>(color & 0xFF),
+        .a = 0xff,
+    };
+}
+
 void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
     std::scoped_lock lock{callback_mutex};
     for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 823c1700c6..cbd7c26d38 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -424,6 +424,13 @@ private:
      */
     void SetCamera(const Common::Input::CallbackStatus& callback);
 
+    /**
+     * Converts a color format from bgra to rgba
+     * @param color in bgra format
+     * @return NpadColor in rgba format
+     */
+    NpadColor GetNpadColor(u32 color);
+
     /**
      * Triggers a callback that something has changed on the controller status
      * @param type Input type of the event to trigger
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index e49223016a..e3b1cfbc64 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -327,10 +327,18 @@ struct TouchState {
 };
 static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
 
+struct NpadColor {
+    u8 r{};
+    u8 g{};
+    u8 b{};
+    u8 a{};
+};
+static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size");
+
 // This is nn::hid::NpadControllerColor
 struct NpadControllerColor {
-    u32 body{};
-    u32 button{};
+    NpadColor body{};
+    NpadColor button{};
 };
 static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
 
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 3c28dee76e..cb29004e87 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -163,28 +163,51 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
     }
     LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
     const auto controller_type = controller.device->GetNpadStyleIndex();
+    const auto& body_colors = controller.device->GetColors();
+    const auto& battery_level = controller.device->GetBattery();
     auto* shared_memory = controller.shared_memory;
     if (controller_type == Core::HID::NpadStyleIndex::None) {
         controller.styleset_changed_event->GetWritableEvent().Signal();
         return;
     }
+
+    // Reset memory values
     shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None;
     shared_memory->device_type.raw = 0;
     shared_memory->system_properties.raw = 0;
+    shared_memory->joycon_color.attribute = ColorAttribute::NoController;
+    shared_memory->joycon_color.attribute = ColorAttribute::NoController;
+    shared_memory->fullkey_color = {};
+    shared_memory->joycon_color.left = {};
+    shared_memory->joycon_color.right = {};
+    shared_memory->battery_level_dual = {};
+    shared_memory->battery_level_left = {};
+    shared_memory->battery_level_right = {};
+
     switch (controller_type) {
     case Core::HID::NpadStyleIndex::None:
         ASSERT(false);
         break;
     case Core::HID::NpadStyleIndex::ProController:
+        shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+        shared_memory->fullkey_color.fullkey = body_colors.fullkey;
+        shared_memory->battery_level_dual = battery_level.dual.battery_level;
         shared_memory->style_tag.fullkey.Assign(1);
         shared_memory->device_type.fullkey.Assign(1);
         shared_memory->system_properties.is_vertical.Assign(1);
         shared_memory->system_properties.use_plus.Assign(1);
         shared_memory->system_properties.use_minus.Assign(1);
+        shared_memory->system_properties.is_charging_joy_dual.Assign(
+            battery_level.dual.is_charging);
         shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
         shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
         break;
     case Core::HID::NpadStyleIndex::Handheld:
+        shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+        shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+        shared_memory->fullkey_color.fullkey = body_colors.fullkey;
+        shared_memory->joycon_color.left = body_colors.left;
+        shared_memory->joycon_color.right = body_colors.right;
         shared_memory->style_tag.handheld.Assign(1);
         shared_memory->device_type.handheld_left.Assign(1);
         shared_memory->device_type.handheld_right.Assign(1);
@@ -192,47 +215,86 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
         shared_memory->system_properties.use_plus.Assign(1);
         shared_memory->system_properties.use_minus.Assign(1);
         shared_memory->system_properties.use_directional_buttons.Assign(1);
+        shared_memory->system_properties.is_charging_joy_dual.Assign(
+            battery_level.left.is_charging);
+        shared_memory->system_properties.is_charging_joy_left.Assign(
+            battery_level.left.is_charging);
+        shared_memory->system_properties.is_charging_joy_right.Assign(
+            battery_level.right.is_charging);
         shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
         shared_memory->applet_nfc_xcd.applet_footer.type =
             AppletFooterUiType::HandheldJoyConLeftJoyConRight;
         shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
         break;
     case Core::HID::NpadStyleIndex::JoyconDual:
+        shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+        shared_memory->joycon_color.attribute = ColorAttribute::Ok;
         shared_memory->style_tag.joycon_dual.Assign(1);
         if (controller.is_dual_left_connected) {
+            shared_memory->joycon_color.left = body_colors.left;
+            shared_memory->battery_level_left = battery_level.left.battery_level;
             shared_memory->device_type.joycon_left.Assign(1);
             shared_memory->system_properties.use_minus.Assign(1);
+            shared_memory->system_properties.is_charging_joy_left.Assign(
+                battery_level.left.is_charging);
             shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
         }
         if (controller.is_dual_right_connected) {
+            shared_memory->joycon_color.right = body_colors.right;
+            shared_memory->battery_level_right = battery_level.right.battery_level;
             shared_memory->device_type.joycon_right.Assign(1);
             shared_memory->system_properties.use_plus.Assign(1);
+            shared_memory->system_properties.is_charging_joy_right.Assign(
+                battery_level.right.is_charging);
             shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
         }
         shared_memory->system_properties.use_directional_buttons.Assign(1);
         shared_memory->system_properties.is_vertical.Assign(1);
         shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
+
         if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
             shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual;
+            shared_memory->fullkey_color.fullkey = body_colors.left;
+            shared_memory->battery_level_dual = battery_level.left.battery_level;
+            shared_memory->system_properties.is_charging_joy_dual.Assign(
+                battery_level.left.is_charging);
         } else if (controller.is_dual_left_connected) {
             shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
+            shared_memory->fullkey_color.fullkey = body_colors.left;
+            shared_memory->battery_level_dual = battery_level.left.battery_level;
+            shared_memory->system_properties.is_charging_joy_dual.Assign(
+                battery_level.left.is_charging);
         } else {
             shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
+            shared_memory->fullkey_color.fullkey = body_colors.right;
+            shared_memory->battery_level_dual = battery_level.right.battery_level;
+            shared_memory->system_properties.is_charging_joy_dual.Assign(
+                battery_level.right.is_charging);
         }
         break;
     case Core::HID::NpadStyleIndex::JoyconLeft:
+        shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+        shared_memory->joycon_color.left = body_colors.left;
+        shared_memory->battery_level_dual = battery_level.left.battery_level;
         shared_memory->style_tag.joycon_left.Assign(1);
         shared_memory->device_type.joycon_left.Assign(1);
         shared_memory->system_properties.is_horizontal.Assign(1);
         shared_memory->system_properties.use_minus.Assign(1);
+        shared_memory->system_properties.is_charging_joy_left.Assign(
+            battery_level.left.is_charging);
         shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
         shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
         break;
     case Core::HID::NpadStyleIndex::JoyconRight:
+        shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+        shared_memory->joycon_color.right = body_colors.right;
+        shared_memory->battery_level_right = battery_level.right.battery_level;
         shared_memory->style_tag.joycon_right.Assign(1);
         shared_memory->device_type.joycon_right.Assign(1);
         shared_memory->system_properties.is_horizontal.Assign(1);
         shared_memory->system_properties.use_plus.Assign(1);
+        shared_memory->system_properties.is_charging_joy_right.Assign(
+            battery_level.right.is_charging);
         shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
         shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
         break;
@@ -269,21 +331,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
         break;
     }
 
-    const auto& body_colors = controller.device->GetColors();
-
-    shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
-    shared_memory->fullkey_color.fullkey = body_colors.fullkey;
-
-    shared_memory->joycon_color.attribute = ColorAttribute::Ok;
-    shared_memory->joycon_color.left = body_colors.left;
-    shared_memory->joycon_color.right = body_colors.right;
-
-    // TODO: Investigate when we should report all batery types
-    const auto& battery_level = controller.device->GetBattery();
-    shared_memory->battery_level_dual = battery_level.dual.battery_level;
-    shared_memory->battery_level_left = battery_level.left.battery_level;
-    shared_memory->battery_level_right = battery_level.right.battery_level;
-
     controller.is_connected = true;
     controller.device->Connect();
     SignalStyleSetChangedEvent(npad_id);