diff options
32 files changed, 731 insertions, 531 deletions
| diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml index e5f8ea91e..cca3189fa 100644 --- a/.ci/templates/build-msvc.yml +++ b/.ci/templates/build-msvc.yml @@ -8,7 +8,7 @@ steps:    displayName: 'Install vulkan-sdk'  - script: python -m pip install --upgrade pip conan    displayName: 'Install conan' -- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release .. && cd .. +- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release .. && cd ..    displayName: 'Configure CMake'  - task: MSBuild@1    displayName: 'Build' diff --git a/.ci/yuzu-mainline-step2.yml b/.ci/yuzu-mainline-step2.yml index 91e21a126..b37eab6cd 100644 --- a/.ci/yuzu-mainline-step2.yml +++ b/.ci/yuzu-mainline-step2.yml @@ -47,7 +47,7 @@ stages:      timeoutInMinutes: 120      displayName: 'msvc'      pool: -      vmImage: windows-2019 +      vmImage: windows-2022      steps:      - template: ./templates/sync-source.yml        parameters: diff --git a/.ci/yuzu-patreon-step2.yml b/.ci/yuzu-patreon-step2.yml index ad61ac74e..119123a63 100644 --- a/.ci/yuzu-patreon-step2.yml +++ b/.ci/yuzu-patreon-step2.yml @@ -12,7 +12,7 @@ stages:      timeoutInMinutes: 120      displayName: 'windows-msvc'      pool: -      vmImage: windows-2019 +      vmImage: windows-2022      steps:      - template: ./templates/sync-source.yml        parameters: diff --git a/CMakeLists.txt b/CMakeLists.txt index caeb2ab8e..dcd4011a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -405,13 +405,11 @@ if (CONAN_REQUIRED_LIBS)      # Download conan.cmake automatically, you can also just copy the conan.cmake file      if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")          message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") -        # TODO: Use a tagged release. The latest tagged release does not support VS2022 as of this writing. -        file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/43e385830ee35377dbd2dcbe8d5a9e750301ea00/conan.cmake" -                        "${CMAKE_BINARY_DIR}/conan.cmake") +        file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/release/0.18/conan.cmake" "${CMAKE_BINARY_DIR}/conan.cmake")      endif()      include(${CMAKE_BINARY_DIR}/conan.cmake) -    conan_check(VERSION 1.41.0 REQUIRED) +    conan_check(VERSION 1.45.0 REQUIRED)      # Manually add iconv to fix a dep conflict between qt and sdl2      # We don't need to add it through find_package or anything since the other two can find it just fine diff --git a/externals/libressl b/externals/libressl -Subproject 8289d0d07de6553bf4b900bf60e808ea3f7f59d +Subproject 8929f818fd748fd31a34fec7c04558399e13014 diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index df2b1dfc5..26ec1091b 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h @@ -316,27 +316,27 @@ static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"  // This is nn::hid::TouchState  struct TouchState { -    u64 delta_time; -    TouchAttribute attribute; -    u32 finger; -    Common::Point<u32> position; -    u32 diameter_x; -    u32 diameter_y; -    u32 rotation_angle; +    u64 delta_time{}; +    TouchAttribute attribute{}; +    u32 finger{}; +    Common::Point<u32> position{}; +    u32 diameter_x{}; +    u32 diameter_y{}; +    u32 rotation_angle{};  };  static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");  // This is nn::hid::NpadControllerColor  struct NpadControllerColor { -    u32 body; -    u32 button; +    u32 body{}; +    u32 button{};  };  static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");  // This is nn::hid::AnalogStickState  struct AnalogStickState { -    s32 x; -    s32 y; +    s32 x{}; +    s32 y{};  };  static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size"); @@ -354,10 +354,10 @@ static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid s  // This is nn::hid::system::NpadPowerInfo  struct NpadPowerInfo { -    bool is_powered; -    bool is_charging; +    bool is_powered{}; +    bool is_charging{};      INSERT_PADDING_BYTES(0x6); -    NpadBatteryLevel battery_level; +    NpadBatteryLevel battery_level{8};  };  static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size"); @@ -474,8 +474,8 @@ static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size"  // This is nn::hid::ConsoleSixAxisSensorHandle  struct ConsoleSixAxisSensorHandle { -    u8 unknown_1; -    u8 unknown_2; +    u8 unknown_1{}; +    u8 unknown_2{};      INSERT_PADDING_BYTES_NOINIT(2);  };  static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4, @@ -483,9 +483,9 @@ static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,  // This is nn::hid::SixAxisSensorHandle  struct SixAxisSensorHandle { -    NpadStyleIndex npad_type; -    u8 npad_id; -    DeviceIndex device_index; +    NpadStyleIndex npad_type{NpadStyleIndex::None}; +    u8 npad_id{}; +    DeviceIndex device_index{DeviceIndex::None};      INSERT_PADDING_BYTES_NOINIT(1);  };  static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size"); @@ -500,19 +500,19 @@ static_assert(sizeof(SixAxisSensorFusionParameters) == 8,  // This is nn::hid::VibrationDeviceHandle  struct VibrationDeviceHandle { -    NpadStyleIndex npad_type; -    u8 npad_id; -    DeviceIndex device_index; +    NpadStyleIndex npad_type{NpadStyleIndex::None}; +    u8 npad_id{}; +    DeviceIndex device_index{DeviceIndex::None};      INSERT_PADDING_BYTES_NOINIT(1);  };  static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size");  // This is nn::hid::VibrationValue  struct VibrationValue { -    f32 low_amplitude; -    f32 low_frequency; -    f32 high_amplitude; -    f32 high_frequency; +    f32 low_amplitude{}; +    f32 low_frequency{}; +    f32 high_amplitude{}; +    f32 high_frequency{};  };  static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size."); @@ -561,7 +561,7 @@ static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid  // This is nn::hid::KeyboardKey  struct KeyboardKey {      // This should be a 256 bit flag -    std::array<u8, 32> key; +    std::array<u8, 32> key{};  };  static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size"); @@ -590,16 +590,16 @@ static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size"  // This is nn::hid::detail::MouseState  struct MouseState { -    s64 sampling_number; -    s32 x; -    s32 y; -    s32 delta_x; -    s32 delta_y; +    s64 sampling_number{}; +    s32 x{}; +    s32 y{}; +    s32 delta_x{}; +    s32 delta_y{};      // Axis Order in HW is switched for the wheel -    s32 delta_wheel_y; -    s32 delta_wheel_x; -    MouseButton button; -    MouseAttribute attribute; +    s32 delta_wheel_y{}; +    s32 delta_wheel_x{}; +    MouseButton button{}; +    MouseAttribute attribute{};  };  static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size"); diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp index f5a0b94dd..bb3cba910 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp +++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp @@ -9,9 +9,14 @@  namespace Service::HID {  constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; -Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_) +Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_, +                                                     u8* raw_shared_memory_)      : ControllerBase{hid_core_} {      console = hid_core.GetEmulatedConsole(); +    static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size, +                  "ConsoleSharedMemory is bigger than the shared memory"); +    shared_memory = std::construct_at( +        reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));  }  Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; @@ -20,8 +25,7 @@ void Controller_ConsoleSixAxis::OnInit() {}  void Controller_ConsoleSixAxis::OnRelease() {} -void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                                         std::size_t size) { +void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {      if (!IsControllerActivated() || !is_transfer_memory_set) {          seven_sixaxis_lifo.buffer_count = 0;          seven_sixaxis_lifo.buffer_tail = 0; @@ -48,13 +52,11 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti          -motion_status.quaternion.xyz.z,      }; -    console_six_axis.sampling_number++; -    console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; -    console_six_axis.verticalization_error = motion_status.verticalization_error; -    console_six_axis.gyro_bias = motion_status.gyro_bias; +    shared_memory->sampling_number++; +    shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; +    shared_memory->verticalization_error = motion_status.verticalization_error; +    shared_memory->gyro_bias = motion_status.gyro_bias; -    // Update console six axis shared memory -    std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis));      // Update seven six axis transfer memory      seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);      std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo)); diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h index 6da5e9454..2fd11538f 100644 --- a/src/core/hle/service/hid/controllers/console_sixaxis.h +++ b/src/core/hle/service/hid/controllers/console_sixaxis.h @@ -17,7 +17,7 @@ class EmulatedConsole;  namespace Service::HID {  class Controller_ConsoleSixAxis final : public ControllerBase {  public: -    explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_); +    explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);      ~Controller_ConsoleSixAxis() override;      // Called when the controller is initialized @@ -27,7 +27,7 @@ public:      void OnRelease() override;      // When the controller is requesting an update for the shared memory -    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; +    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;      // Called on InitializeSevenSixAxisSensor      void SetTransferMemoryPointer(u8* t_mem); @@ -61,12 +61,13 @@ private:      Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};      static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size"); -    Core::HID::EmulatedConsole* console; +    SevenSixAxisState next_seven_sixaxis_state{};      u8* transfer_memory = nullptr; +    ConsoleSharedMemory* shared_memory = nullptr; +    Core::HID::EmulatedConsole* console = nullptr; +      bool is_transfer_memory_set = false;      u64 last_saved_timestamp{};      u64 last_global_timestamp{}; -    ConsoleSharedMemory console_six_axis{}; -    SevenSixAxisState next_seven_sixaxis_state{};  };  } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index bb01ea643..d6f7a5073 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h @@ -26,12 +26,10 @@ public:      virtual void OnRelease() = 0;      // When the controller is requesting an update for the shared memory -    virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                          std::size_t size) = 0; +    virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing) = 0;      // When the controller is requesting a motion update for the shared memory -    virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                                std::size_t size) {} +    virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}      void ActivateController(); @@ -40,6 +38,7 @@ public:      bool IsControllerActivated() const;      static const std::size_t hid_entry_count = 17; +    static const std::size_t shared_memory_size = 0x40000;  protected:      bool is_activated{false}; diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 73309d64e..8ec9f4a95 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -13,8 +13,12 @@  namespace Service::HID {  constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000; -Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_) +Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)      : ControllerBase{hid_core_} { +    static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size, +                  "DebugPadSharedMemory is bigger than the shared memory"); +    shared_memory = std::construct_at( +        reinterpret_cast<DebugPadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));      controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);  } @@ -24,16 +28,14 @@ void Controller_DebugPad::OnInit() {}  void Controller_DebugPad::OnRelease() {} -void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                                   std::size_t size) { +void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {      if (!IsControllerActivated()) { -        debug_pad_lifo.buffer_count = 0; -        debug_pad_lifo.buffer_tail = 0; -        std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo)); +        shared_memory->debug_pad_lifo.buffer_count = 0; +        shared_memory->debug_pad_lifo.buffer_tail = 0;          return;      } -    const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state; +    const auto& last_entry = shared_memory->debug_pad_lifo.ReadCurrentEntry().state;      next_state.sampling_number = last_entry.sampling_number + 1;      if (Settings::values.debug_pad_enabled) { @@ -47,8 +49,7 @@ void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing,          next_state.r_stick = stick_state.right;      } -    debug_pad_lifo.WriteNextEntry(next_state); -    std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo)); +    shared_memory->debug_pad_lifo.WriteNextEntry(next_state);  }  } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 388d25b3c..68ff0ea79 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -17,7 +17,7 @@ struct AnalogStickState;  namespace Service::HID {  class Controller_DebugPad final : public ControllerBase {  public: -    explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_); +    explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);      ~Controller_DebugPad() override;      // Called when the controller is initialized @@ -27,7 +27,7 @@ public:      void OnRelease() override;      // When the controller is requesting an update for the shared memory -    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; +    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;  private:      // This is nn::hid::DebugPadAttribute @@ -41,19 +41,24 @@ private:      // This is nn::hid::DebugPadState      struct DebugPadState { -        s64 sampling_number; -        DebugPadAttribute attribute; -        Core::HID::DebugPadButton pad_state; -        Core::HID::AnalogStickState r_stick; -        Core::HID::AnalogStickState l_stick; +        s64 sampling_number{}; +        DebugPadAttribute attribute{}; +        Core::HID::DebugPadButton pad_state{}; +        Core::HID::AnalogStickState r_stick{}; +        Core::HID::AnalogStickState l_stick{};      };      static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state"); -    // This is nn::hid::detail::DebugPadLifo -    Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{}; -    static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size"); -    DebugPadState next_state{}; +    struct DebugPadSharedMemory { +        // This is nn::hid::detail::DebugPadLifo +        Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{}; +        static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size"); +        INSERT_PADDING_WORDS(0x4E); +    }; +    static_assert(sizeof(DebugPadSharedMemory) == 0x400, "DebugPadSharedMemory is an invalid size"); -    Core::HID::EmulatedController* controller; +    DebugPadState next_state{}; +    DebugPadSharedMemory* shared_memory = nullptr; +    Core::HID::EmulatedController* controller = nullptr;  };  } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index c8dd131dd..3eae1ae35 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -23,25 +23,28 @@ constexpr f32 Square(s32 num) {      return static_cast<f32>(num * num);  } -Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) { +Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) +    : ControllerBase(hid_core_) { +    static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size, +                  "GestureSharedMemory is bigger than the shared memory"); +    shared_memory = std::construct_at( +        reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));      console = hid_core.GetEmulatedConsole();  }  Controller_Gesture::~Controller_Gesture() = default;  void Controller_Gesture::OnInit() { -    gesture_lifo.buffer_count = 0; -    gesture_lifo.buffer_tail = 0; +    shared_memory->gesture_lifo.buffer_count = 0; +    shared_memory->gesture_lifo.buffer_tail = 0;      force_update = true;  }  void Controller_Gesture::OnRelease() {} -void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                                  std::size_t size) { +void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {      if (!IsControllerActivated()) { -        gesture_lifo.buffer_count = 0; -        gesture_lifo.buffer_tail = 0; -        std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo)); +        shared_memory->gesture_lifo.buffer_count = 0; +        shared_memory->gesture_lifo.buffer_tail = 0;          return;      } @@ -49,15 +52,15 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u      GestureProperties gesture = GetGestureProperties();      f32 time_difference = -        static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000); +        static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) / +        (1000 * 1000 * 1000);      // Only update if necesary      if (!ShouldUpdateGesture(gesture, time_difference)) {          return;      } -    last_update_timestamp = gesture_lifo.timestamp; -    UpdateGestureSharedMemory(data, size, gesture, time_difference); +    last_update_timestamp = shared_memory->gesture_lifo.timestamp;  }  void Controller_Gesture::ReadTouchInput() { @@ -97,7 +100,7 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,      GestureType type = GestureType::Idle;      GestureAttribute attributes{}; -    const auto& last_entry = gesture_lifo.ReadCurrentEntry().state; +    const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;      // Reset next state to default      next_state.sampling_number = last_entry.sampling_number + 1; @@ -127,8 +130,7 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,      next_state.points = gesture.points;      last_gesture = gesture; -    gesture_lifo.WriteNextEntry(next_state); -    std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo)); +    shared_memory->gesture_lifo.WriteNextEntry(next_state);  }  void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type, @@ -305,7 +307,7 @@ void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,  }  const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { -    return gesture_lifo.ReadCurrentEntry().state; +    return shared_memory->gesture_lifo.ReadCurrentEntry().state;  }  Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 7a9d28dc6..c62a341bf 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -14,7 +14,7 @@  namespace Service::HID {  class Controller_Gesture final : public ControllerBase {  public: -    explicit Controller_Gesture(Core::HID::HIDCore& hid_core_); +    explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);      ~Controller_Gesture() override;      // Called when the controller is initialized @@ -24,7 +24,7 @@ public:      void OnRelease() override;      // When the controller is requesting an update for the shared memory -    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; +    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;  private:      static constexpr size_t MAX_FINGERS = 16; @@ -66,19 +66,19 @@ private:      // This is nn::hid::GestureState      struct GestureState { -        s64 sampling_number; -        s64 detection_count; -        GestureType type; -        GestureDirection direction; -        Common::Point<s32> pos; -        Common::Point<s32> delta; -        f32 vel_x; -        f32 vel_y; -        GestureAttribute attributes; -        f32 scale; -        f32 rotation_angle; -        s32 point_count; -        std::array<Common::Point<s32>, 4> points; +        s64 sampling_number{}; +        s64 detection_count{}; +        GestureType type{GestureType::Idle}; +        GestureDirection direction{GestureDirection::None}; +        Common::Point<s32> pos{}; +        Common::Point<s32> delta{}; +        f32 vel_x{}; +        f32 vel_y{}; +        GestureAttribute attributes{}; +        f32 scale{}; +        f32 rotation_angle{}; +        s32 point_count{}; +        std::array<Common::Point<s32>, 4> points{};      };      static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); @@ -92,6 +92,14 @@ private:          f32 angle{};      }; +    struct GestureSharedMemory { +        // This is nn::hid::detail::GestureLifo +        Lifo<GestureState, hid_entry_count> gesture_lifo{}; +        static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size"); +        INSERT_PADDING_WORDS(0x3E); +    }; +    static_assert(sizeof(GestureSharedMemory) == 0x800, "GestureSharedMemory is an invalid size"); +      // Reads input from all available input engines      void ReadTouchInput(); @@ -134,12 +142,9 @@ private:      // Returns the average distance, angle and middle point of the active fingers      GestureProperties GetGestureProperties(); -    // This is nn::hid::detail::GestureLifo -    Lifo<GestureState, hid_entry_count> gesture_lifo{}; -    static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");      GestureState next_state{}; - -    Core::HID::EmulatedConsole* console; +    GestureSharedMemory* shared_memory = nullptr; +    Core::HID::EmulatedConsole* console = nullptr;      std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};      GestureProperties last_gesture{}; diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 8dc67757b..117d87433 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -12,8 +12,12 @@  namespace Service::HID {  constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; -Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_) +Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)      : ControllerBase{hid_core_} { +    static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size, +                  "KeyboardSharedMemory is bigger than the shared memory"); +    shared_memory = std::construct_at( +        reinterpret_cast<KeyboardSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));      emulated_devices = hid_core.GetEmulatedDevices();  } @@ -23,16 +27,14 @@ void Controller_Keyboard::OnInit() {}  void Controller_Keyboard::OnRelease() {} -void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                                   std::size_t size) { +void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {      if (!IsControllerActivated()) { -        keyboard_lifo.buffer_count = 0; -        keyboard_lifo.buffer_tail = 0; -        std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo)); +        shared_memory->keyboard_lifo.buffer_count = 0; +        shared_memory->keyboard_lifo.buffer_tail = 0;          return;      } -    const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state; +    const auto& last_entry = shared_memory->keyboard_lifo.ReadCurrentEntry().state;      next_state.sampling_number = last_entry.sampling_number + 1;      if (Settings::values.keyboard_enabled) { @@ -44,8 +46,7 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing,          next_state.attribute.is_connected.Assign(1);      } -    keyboard_lifo.WriteNextEntry(next_state); -    std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo)); +    shared_memory->keyboard_lifo.WriteNextEntry(next_state);  }  } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index 39c6085f1..7532f53c6 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -16,7 +16,7 @@ struct KeyboardKey;  namespace Service::HID {  class Controller_Keyboard final : public ControllerBase {  public: -    explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_); +    explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);      ~Controller_Keyboard() override;      // Called when the controller is initialized @@ -26,23 +26,28 @@ public:      void OnRelease() override;      // When the controller is requesting an update for the shared memory -    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; +    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;  private:      // This is nn::hid::detail::KeyboardState      struct KeyboardState { -        s64 sampling_number; -        Core::HID::KeyboardModifier modifier; -        Core::HID::KeyboardAttribute attribute; -        Core::HID::KeyboardKey key; +        s64 sampling_number{}; +        Core::HID::KeyboardModifier modifier{}; +        Core::HID::KeyboardAttribute attribute{}; +        Core::HID::KeyboardKey key{};      };      static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size"); -    // This is nn::hid::detail::KeyboardLifo -    Lifo<KeyboardState, hid_entry_count> keyboard_lifo{}; -    static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size"); -    KeyboardState next_state{}; +    struct KeyboardSharedMemory { +        // This is nn::hid::detail::KeyboardLifo +        Lifo<KeyboardState, hid_entry_count> keyboard_lifo{}; +        static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size"); +        INSERT_PADDING_WORDS(0xA); +    }; +    static_assert(sizeof(KeyboardSharedMemory) == 0x400, "KeyboardSharedMemory is an invalid size"); -    Core::HID::EmulatedDevices* emulated_devices; +    KeyboardState next_state{}; +    KeyboardSharedMemory* shared_memory = nullptr; +    Core::HID::EmulatedDevices* emulated_devices = nullptr;  };  } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index a0292f586..b11cb438d 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -12,7 +12,12 @@  namespace Service::HID {  constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; -Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} { +Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) +    : ControllerBase{hid_core_} { +    static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size, +                  "MouseSharedMemory is bigger than the shared memory"); +    shared_memory = std::construct_at( +        reinterpret_cast<MouseSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));      emulated_devices = hid_core.GetEmulatedDevices();  } @@ -21,16 +26,14 @@ Controller_Mouse::~Controller_Mouse() = default;  void Controller_Mouse::OnInit() {}  void Controller_Mouse::OnRelease() {} -void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                                std::size_t size) { +void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {      if (!IsControllerActivated()) { -        mouse_lifo.buffer_count = 0; -        mouse_lifo.buffer_tail = 0; -        std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo)); +        shared_memory->mouse_lifo.buffer_count = 0; +        shared_memory->mouse_lifo.buffer_tail = 0;          return;      } -    const auto& last_entry = mouse_lifo.ReadCurrentEntry().state; +    const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state;      next_state.sampling_number = last_entry.sampling_number + 1;      next_state.attribute.raw = 0; @@ -50,8 +53,7 @@ void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*          next_state.button = mouse_button_state;      } -    mouse_lifo.WriteNextEntry(next_state); -    std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo)); +    shared_memory->mouse_lifo.WriteNextEntry(next_state);  }  } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 62acdfb77..733d00577 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -16,7 +16,7 @@ struct AnalogStickState;  namespace Service::HID {  class Controller_Mouse final : public ControllerBase {  public: -    explicit Controller_Mouse(Core::HID::HIDCore& hid_core_); +    explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);      ~Controller_Mouse() override;      // Called when the controller is initialized @@ -26,15 +26,20 @@ public:      void OnRelease() override;      // When the controller is requesting an update for the shared memory -    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; +    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;  private: -    // This is nn::hid::detail::MouseLifo -    Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{}; -    static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size"); -    Core::HID::MouseState next_state{}; +    struct MouseSharedMemory { +        // This is nn::hid::detail::MouseLifo +        Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{}; +        static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size"); +        INSERT_PADDING_WORDS(0x2C); +    }; +    static_assert(sizeof(MouseSharedMemory) == 0x400, "MouseSharedMemory is an invalid size"); -    Core::HID::AnalogStickState last_mouse_wheel_state; -    Core::HID::EmulatedDevices* emulated_devices; +    Core::HID::MouseState next_state{}; +    Core::HID::AnalogStickState last_mouse_wheel_state{}; +    MouseSharedMemory* shared_memory = nullptr; +    Core::HID::EmulatedDevices* emulated_devices = nullptr;  };  } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 16e973b25..17f71beaf 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -61,11 +61,14 @@ bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle&      return npad_id && npad_type && device_index;  } -Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, +Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,                                   KernelHelpers::ServiceContext& service_context_)      : ControllerBase{hid_core_}, service_context{service_context_} { +    static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size);      for (std::size_t i = 0; i < controller_data.size(); ++i) {          auto& controller = controller_data[i]; +        controller.shared_memory = std::construct_at(reinterpret_cast<NpadInternalState*>( +            raw_shared_memory_ + NPAD_OFFSET + (i * sizeof(NpadInternalState))));          controller.device = hid_core.GetEmulatedControllerByIndex(i);          controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =              Core::HID::DEFAULT_VIBRATION_VALUE; @@ -115,11 +118,11 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,          if (!controller.device->IsConnected()) {              return;          } -        auto& shared_memory = controller.shared_memory_entry; +        auto* shared_memory = controller.shared_memory;          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; +        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;          break;      }      default: @@ -134,99 +137,100 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {      }      LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);      const auto controller_type = controller.device->GetNpadStyleIndex(); -    auto& shared_memory = controller.shared_memory_entry; +    auto* shared_memory = controller.shared_memory;      if (controller_type == Core::HID::NpadStyleIndex::None) {          controller.styleset_changed_event->GetWritableEvent().Signal();          return;      } -    shared_memory.style_tag.raw = Core::HID::NpadStyleSet::None; -    shared_memory.device_type.raw = 0; -    shared_memory.system_properties.raw = 0; +    shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; +    shared_memory->device_type.raw = 0; +    shared_memory->system_properties.raw = 0;      switch (controller_type) {      case Core::HID::NpadStyleIndex::None:          UNREACHABLE();          break;      case Core::HID::NpadStyleIndex::ProController: -        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.applet_footer.type = AppletFooterUiType::SwitchProController; +        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->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;          break;      case Core::HID::NpadStyleIndex::Handheld: -        shared_memory.style_tag.handheld.Assign(1); -        shared_memory.device_type.handheld_left.Assign(1); -        shared_memory.device_type.handheld_right.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.use_directional_buttons.Assign(1); -        shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual; -        shared_memory.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; +        shared_memory->style_tag.handheld.Assign(1); +        shared_memory->device_type.handheld_left.Assign(1); +        shared_memory->device_type.handheld_right.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.use_directional_buttons.Assign(1); +        shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; +        shared_memory->applet_nfc_xcd.applet_footer.type = +            AppletFooterUiType::HandheldJoyConLeftJoyConRight;          break;      case Core::HID::NpadStyleIndex::JoyconDual: -        shared_memory.style_tag.joycon_dual.Assign(1); +        shared_memory->style_tag.joycon_dual.Assign(1);          if (controller.is_dual_left_connected) { -            shared_memory.device_type.joycon_left.Assign(1); -            shared_memory.system_properties.use_minus.Assign(1); +            shared_memory->device_type.joycon_left.Assign(1); +            shared_memory->system_properties.use_minus.Assign(1);          }          if (controller.is_dual_right_connected) { -            shared_memory.device_type.joycon_right.Assign(1); -            shared_memory.system_properties.use_plus.Assign(1); +            shared_memory->device_type.joycon_right.Assign(1); +            shared_memory->system_properties.use_plus.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; +        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_footer.type = AppletFooterUiType::JoyDual; +            shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual;          } else if (controller.is_dual_left_connected) { -            shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly; +            shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;          } else { -            shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly; +            shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;          }          break;      case Core::HID::NpadStyleIndex::JoyconLeft: -        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.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; +        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->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;          break;      case Core::HID::NpadStyleIndex::JoyconRight: -        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.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; +        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->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;          break;      case Core::HID::NpadStyleIndex::GameCube: -        shared_memory.style_tag.gamecube.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->style_tag.gamecube.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);          break;      case Core::HID::NpadStyleIndex::Pokeball: -        shared_memory.style_tag.palma.Assign(1); -        shared_memory.device_type.palma.Assign(1); +        shared_memory->style_tag.palma.Assign(1); +        shared_memory->device_type.palma.Assign(1);          break;      case Core::HID::NpadStyleIndex::NES: -        shared_memory.style_tag.lark.Assign(1); -        shared_memory.device_type.fullkey.Assign(1); +        shared_memory->style_tag.lark.Assign(1); +        shared_memory->device_type.fullkey.Assign(1);          break;      case Core::HID::NpadStyleIndex::SNES: -        shared_memory.style_tag.lucia.Assign(1); -        shared_memory.device_type.fullkey.Assign(1); -        shared_memory.applet_footer.type = AppletFooterUiType::Lucia; +        shared_memory->style_tag.lucia.Assign(1); +        shared_memory->device_type.fullkey.Assign(1); +        shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::Lucia;          break;      case Core::HID::NpadStyleIndex::N64: -        shared_memory.style_tag.lagoon.Assign(1); -        shared_memory.device_type.fullkey.Assign(1); -        shared_memory.applet_footer.type = AppletFooterUiType::Lagon; +        shared_memory->style_tag.lagoon.Assign(1); +        shared_memory->device_type.fullkey.Assign(1); +        shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::Lagon;          break;      case Core::HID::NpadStyleIndex::SegaGenesis: -        shared_memory.style_tag.lager.Assign(1); -        shared_memory.device_type.fullkey.Assign(1); +        shared_memory->style_tag.lager.Assign(1); +        shared_memory->device_type.fullkey.Assign(1);          break;      default:          break; @@ -234,23 +238,23 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {      const auto& body_colors = controller.device->GetColors(); -    shared_memory.fullkey_color.attribute = ColorAttribute::Ok; -    shared_memory.fullkey_color.fullkey = body_colors.fullkey; +    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; +    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; +    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); -    WriteEmptyEntry(controller.shared_memory_entry); +    WriteEmptyEntry(controller.shared_memory);  }  void Controller_NPad::OnInit() { @@ -270,12 +274,12 @@ void Controller_NPad::OnInit() {      // Prefill controller buffers      for (auto& controller : controller_data) { -        auto& npad = controller.shared_memory_entry; -        npad.fullkey_color = { +        auto* npad = controller.shared_memory; +        npad->fullkey_color = {              .attribute = ColorAttribute::NoController,              .fullkey = {},          }; -        npad.joycon_color = { +        npad->joycon_color = {              .attribute = ColorAttribute::NoController,              .left = {},              .right = {}, @@ -287,25 +291,25 @@ void Controller_NPad::OnInit() {      }  } -void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { +void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) {      NPadGenericState dummy_pad_state{};      NpadGcTriggerState dummy_gc_state{}; -    dummy_pad_state.sampling_number = npad.fullkey_lifo.ReadCurrentEntry().sampling_number + 1; -    npad.fullkey_lifo.WriteNextEntry(dummy_pad_state); -    dummy_pad_state.sampling_number = npad.handheld_lifo.ReadCurrentEntry().sampling_number + 1; -    npad.handheld_lifo.WriteNextEntry(dummy_pad_state); -    dummy_pad_state.sampling_number = npad.joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; -    npad.joy_dual_lifo.WriteNextEntry(dummy_pad_state); -    dummy_pad_state.sampling_number = npad.joy_left_lifo.ReadCurrentEntry().sampling_number + 1; -    npad.joy_left_lifo.WriteNextEntry(dummy_pad_state); -    dummy_pad_state.sampling_number = npad.joy_right_lifo.ReadCurrentEntry().sampling_number + 1; -    npad.joy_right_lifo.WriteNextEntry(dummy_pad_state); -    dummy_pad_state.sampling_number = npad.palma_lifo.ReadCurrentEntry().sampling_number + 1; -    npad.palma_lifo.WriteNextEntry(dummy_pad_state); -    dummy_pad_state.sampling_number = npad.system_ext_lifo.ReadCurrentEntry().sampling_number + 1; -    npad.system_ext_lifo.WriteNextEntry(dummy_pad_state); -    dummy_gc_state.sampling_number = npad.gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; -    npad.gc_trigger_lifo.WriteNextEntry(dummy_gc_state); +    dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1; +    npad->fullkey_lifo.WriteNextEntry(dummy_pad_state); +    dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().sampling_number + 1; +    npad->handheld_lifo.WriteNextEntry(dummy_pad_state); +    dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().sampling_number + 1; +    npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state); +    dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().sampling_number + 1; +    npad->joy_left_lifo.WriteNextEntry(dummy_pad_state); +    dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().sampling_number + 1; +    npad->joy_right_lifo.WriteNextEntry(dummy_pad_state); +    dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().sampling_number + 1; +    npad->palma_lifo.WriteNextEntry(dummy_pad_state); +    dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().sampling_number + 1; +    npad->system_ext_lifo.WriteNextEntry(dummy_pad_state); +    dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1; +    npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);  }  void Controller_NPad::OnRelease() { @@ -371,23 +375,19 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {      }  } -void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                               std::size_t data_len) { +void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {      if (!IsControllerActivated()) {          return;      }      for (std::size_t i = 0; i < controller_data.size(); ++i) {          auto& controller = controller_data[i]; -        auto& npad = controller.shared_memory_entry; +        auto* npad = controller.shared_memory;          const auto& controller_type = controller.device->GetNpadStyleIndex();          if (controller_type == Core::HID::NpadStyleIndex::None ||              !controller.device->IsConnected()) { -            // Refresh shared memory -            std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), -                        &controller.shared_memory_entry, sizeof(NpadInternalState));              continue;          } @@ -415,8 +415,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*              libnx_state.connection_status.is_wired.Assign(1);              pad_state.sampling_number = -                npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; -            npad.fullkey_lifo.WriteNextEntry(pad_state); +                npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->fullkey_lifo.WriteNextEntry(pad_state);              break;          case Core::HID::NpadStyleIndex::Handheld:              pad_state.connection_status.raw = 0; @@ -433,8 +433,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*              libnx_state.connection_status.is_left_wired.Assign(1);              libnx_state.connection_status.is_right_wired.Assign(1);              pad_state.sampling_number = -                npad.handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; -            npad.handheld_lifo.WriteNextEntry(pad_state); +                npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->handheld_lifo.WriteNextEntry(pad_state);              break;          case Core::HID::NpadStyleIndex::JoyconDual:              pad_state.connection_status.raw = 0; @@ -449,8 +449,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*              }              pad_state.sampling_number = -                npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; -            npad.joy_dual_lifo.WriteNextEntry(pad_state); +                npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->joy_dual_lifo.WriteNextEntry(pad_state);              break;          case Core::HID::NpadStyleIndex::JoyconLeft:              pad_state.connection_status.raw = 0; @@ -459,8 +459,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*              libnx_state.connection_status.is_left_connected.Assign(1);              pad_state.sampling_number = -                npad.joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1; -            npad.joy_left_lifo.WriteNextEntry(pad_state); +                npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->joy_left_lifo.WriteNextEntry(pad_state);              break;          case Core::HID::NpadStyleIndex::JoyconRight:              pad_state.connection_status.raw = 0; @@ -469,8 +469,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*              libnx_state.connection_status.is_right_connected.Assign(1);              pad_state.sampling_number = -                npad.joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1; -            npad.joy_right_lifo.WriteNextEntry(pad_state); +                npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->joy_right_lifo.WriteNextEntry(pad_state);              break;          case Core::HID::NpadStyleIndex::GameCube:              pad_state.connection_status.raw = 0; @@ -479,18 +479,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*              libnx_state.connection_status.is_wired.Assign(1);              pad_state.sampling_number = -                npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; +                npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;              trigger_state.sampling_number = -                npad.gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1; -            npad.fullkey_lifo.WriteNextEntry(pad_state); -            npad.gc_trigger_lifo.WriteNextEntry(trigger_state); +                npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->fullkey_lifo.WriteNextEntry(pad_state); +            npad->gc_trigger_lifo.WriteNextEntry(trigger_state);              break;          case Core::HID::NpadStyleIndex::Pokeball:              pad_state.connection_status.raw = 0;              pad_state.connection_status.is_connected.Assign(1);              pad_state.sampling_number = -                npad.palma_lifo.ReadCurrentEntry().state.sampling_number + 1; -            npad.palma_lifo.WriteNextEntry(pad_state); +                npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->palma_lifo.WriteNextEntry(pad_state);              break;          default:              break; @@ -499,17 +499,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*          libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;          libnx_state.l_stick = pad_state.l_stick;          libnx_state.r_stick = pad_state.r_stick; -        npad.system_ext_lifo.WriteNextEntry(pad_state); +        npad->system_ext_lifo.WriteNextEntry(pad_state);          press_state |= static_cast<u64>(pad_state.npad_buttons.raw); - -        std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), -                    &controller.shared_memory_entry, sizeof(NpadInternalState));      }  } -void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                                     std::size_t data_len) { +void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {      if (!IsControllerActivated()) {          return;      } @@ -524,7 +520,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing              continue;          } -        auto& npad = controller.shared_memory_entry; +        auto* npad = controller.shared_memory;          const auto& motion_state = controller.device->GetMotions();          auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;          auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; @@ -610,32 +606,30 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing          }          sixaxis_fullkey_state.sampling_number = -            npad.sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;          sixaxis_handheld_state.sampling_number = -            npad.sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;          sixaxis_dual_left_state.sampling_number = -            npad.sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;          sixaxis_dual_right_state.sampling_number = -            npad.sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;          sixaxis_left_lifo_state.sampling_number = -            npad.sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;          sixaxis_right_lifo_state.sampling_number = -            npad.sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;          if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {              // This buffer only is updated on handheld on HW -            npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state); +            npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);          } else {              // Handheld doesn't update this buffer on HW -            npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state); +            npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);          } -        npad.sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); -        npad.sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); -        npad.sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); -        npad.sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state); -        std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)), -                    &controller.shared_memory_entry, sizeof(NpadInternalState)); +        npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state); +        npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state); +        npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state); +        npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);      }  } @@ -713,8 +707,8 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy      }      auto& controller = GetControllerFromNpadIdType(npad_id); -    if (controller.shared_memory_entry.assignment_mode != assignment_mode) { -        controller.shared_memory_entry.assignment_mode = assignment_mode; +    if (controller.shared_memory->assignment_mode != assignment_mode) { +        controller.shared_memory->assignment_mode = assignment_mode;      }      if (!controller.device->IsConnected()) { @@ -981,32 +975,32 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {          controller.vibration[device_idx].device_mounted = false;      } -    auto& shared_memory_entry = controller.shared_memory_entry; -    // Don't reset shared_memory_entry.assignment_mode this value is persistent -    shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out -    shared_memory_entry.device_type.raw = 0; -    shared_memory_entry.system_properties.raw = 0; -    shared_memory_entry.button_properties.raw = 0; -    shared_memory_entry.battery_level_dual = 0; -    shared_memory_entry.battery_level_left = 0; -    shared_memory_entry.battery_level_right = 0; -    shared_memory_entry.fullkey_color = { +    auto* shared_memory = controller.shared_memory; +    // Don't reset shared_memory->assignment_mode this value is persistent +    shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out +    shared_memory->device_type.raw = 0; +    shared_memory->system_properties.raw = 0; +    shared_memory->button_properties.raw = 0; +    shared_memory->battery_level_dual = 0; +    shared_memory->battery_level_left = 0; +    shared_memory->battery_level_right = 0; +    shared_memory->fullkey_color = {          .attribute = ColorAttribute::NoController,          .fullkey = {},      }; -    shared_memory_entry.joycon_color = { +    shared_memory->joycon_color = {          .attribute = ColorAttribute::NoController,          .left = {},          .right = {},      }; -    shared_memory_entry.applet_footer.type = AppletFooterUiType::None; +    shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::None;      controller.is_dual_left_connected = true;      controller.is_dual_right_connected = true;      controller.is_connected = false;      controller.device->Disconnect();      SignalStyleSetChangedEvent(npad_id); -    WriteEmptyEntry(controller.shared_memory_entry); +    WriteEmptyEntry(shared_memory);  }  ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index b42532b68..0a96825a5 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -35,7 +35,7 @@ namespace Service::HID {  class Controller_NPad final : public ControllerBase {  public: -    explicit Controller_NPad(Core::HID::HIDCore& hid_core_, +    explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,                               KernelHelpers::ServiceContext& service_context_);      ~Controller_NPad() override; @@ -46,11 +46,10 @@ public:      void OnRelease() override;      // When the controller is requesting an update for the shared memory -    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; +    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;      // When the controller is requesting a motion update for the shared memory -    void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                        std::size_t size) override; +    void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override;      // This is nn::hid::GyroscopeZeroDriftMode      enum class GyroscopeZeroDriftMode : u32 { @@ -188,6 +187,8 @@ public:      static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);  private: +    static constexpr std::size_t NPAD_COUNT = 10; +      // This is nn::hid::detail::ColorAttribute      enum class ColorAttribute : u32 {          Ok = 0, @@ -409,6 +410,13 @@ private:          U,      }; +    struct AppletNfcXcd { +        union { +            AppletFooterUi applet_footer{}; +            Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo; +        }; +    }; +      // This is nn::hid::detail::NpadInternalState      struct NpadInternalState {          Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None}; @@ -435,10 +443,7 @@ private:          Core::HID::NpadBatteryLevel battery_level_dual{};          Core::HID::NpadBatteryLevel battery_level_left{};          Core::HID::NpadBatteryLevel battery_level_right{}; -        union { -            AppletFooterUi applet_footer{}; -            Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo; -        }; +        AppletNfcXcd applet_nfc_xcd{};          INSERT_PADDING_BYTES(0x20); // Unknown          Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};          NpadLarkType lark_type_l_and_main{}; @@ -465,9 +470,9 @@ private:      };      struct NpadControllerData { -        Core::HID::EmulatedController* device;          Kernel::KEvent* styleset_changed_event{}; -        NpadInternalState shared_memory_entry{}; +        NpadInternalState* shared_memory = nullptr; +        Core::HID::EmulatedController* device = nullptr;          std::array<VibrationData, 2> vibration{};          bool unintended_home_button_input_protection{}; @@ -497,15 +502,14 @@ private:          SixAxisSensorState sixaxis_dual_right_state{};          SixAxisSensorState sixaxis_left_lifo_state{};          SixAxisSensorState sixaxis_right_lifo_state{}; - -        int callback_key; +        int callback_key{};      };      void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);      void InitNewlyAddedController(Core::HID::NpadIdType npad_id);      bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const;      void RequestPadStateUpdate(Core::HID::NpadIdType npad_id); -    void WriteEmptyEntry(NpadInternalState& npad); +    void WriteEmptyEntry(NpadInternalState* npad);      NpadControllerData& GetControllerFromHandle(          const Core::HID::SixAxisSensorHandle& device_handle); @@ -520,7 +524,7 @@ private:      std::atomic<u64> press_state{}; -    std::array<NpadControllerData, 10> controller_data{}; +    std::array<NpadControllerData, NPAD_COUNT> controller_data{};      KernelHelpers::ServiceContext& service_context;      std::mutex mutex;      std::vector<Core::HID::NpadIdType> supported_npad_id_types{}; diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 4d3b9d2be..df9ee0c3f 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp @@ -9,15 +9,18 @@  namespace Service::HID { -Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} +Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) +    : ControllerBase{hid_core_} { +    raw_shared_memory = raw_shared_memory_; +} +  Controller_Stubbed::~Controller_Stubbed() = default;  void Controller_Stubbed::OnInit() {}  void Controller_Stubbed::OnRelease() {} -void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                                  std::size_t size) { +void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {      if (!smart_update) {          return;      } @@ -28,7 +31,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u      header.entry_count = 0;      header.last_entry_index = 0; -    std::memcpy(data + common_offset, &header, sizeof(CommonHeader)); +    std::memcpy(raw_shared_memory + common_offset, &header, sizeof(CommonHeader));  }  void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) { diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h index 512066eb3..1483a968e 100644 --- a/src/core/hle/service/hid/controllers/stubbed.h +++ b/src/core/hle/service/hid/controllers/stubbed.h @@ -9,7 +9,7 @@  namespace Service::HID {  class Controller_Stubbed final : public ControllerBase {  public: -    explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_); +    explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);      ~Controller_Stubbed() override;      // Called when the controller is initialized @@ -19,19 +19,20 @@ public:      void OnRelease() override;      // When the controller is requesting an update for the shared memory -    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; +    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;      void SetCommonHeaderOffset(std::size_t off);  private:      struct CommonHeader { -        s64 timestamp; -        s64 total_entry_count; -        s64 last_entry_index; -        s64 entry_count; +        s64 timestamp{}; +        s64 total_entry_count{}; +        s64 last_entry_index{}; +        s64 entry_count{};      };      static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); +    u8* raw_shared_memory = nullptr;      bool smart_update{};      std::size_t common_offset{};  }; diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 5b4b51a69..108ce5a41 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -15,8 +15,13 @@  namespace Service::HID {  constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; -Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_) +Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_, +                                               u8* raw_shared_memory_)      : ControllerBase{hid_core_} { +    static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, +                  "TouchSharedMemory is bigger than the shared memory"); +    shared_memory = std::construct_at( +        reinterpret_cast<TouchSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));      console = hid_core.GetEmulatedConsole();  } @@ -26,14 +31,12 @@ void Controller_Touchscreen::OnInit() {}  void Controller_Touchscreen::OnRelease() {} -void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                                      std::size_t size) { -    touch_screen_lifo.timestamp = core_timing.GetCPUTicks(); +void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { +    shared_memory->touch_screen_lifo.timestamp = core_timing.GetCPUTicks();      if (!IsControllerActivated()) { -        touch_screen_lifo.buffer_count = 0; -        touch_screen_lifo.buffer_tail = 0; -        std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo)); +        shared_memory->touch_screen_lifo.buffer_count = 0; +        shared_memory->touch_screen_lifo.buffer_tail = 0;          return;      } @@ -74,7 +77,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin          static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));      const u64 tick = core_timing.GetCPUTicks(); -    const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state; +    const auto& last_entry = shared_memory->touch_screen_lifo.ReadCurrentEntry().state;      next_state.sampling_number = last_entry.sampling_number + 1;      next_state.entry_count = static_cast<s32>(active_fingers_count); @@ -106,8 +109,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin          }      } -    touch_screen_lifo.WriteNextEntry(next_state); -    std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo)); +    shared_memory->touch_screen_lifo.WriteNextEntry(next_state);  }  } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 424973b38..e57a3a80e 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -25,14 +25,14 @@ public:      // This is nn::hid::TouchScreenConfigurationForNx      struct TouchScreenConfigurationForNx { -        TouchScreenModeForNx mode; +        TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};          INSERT_PADDING_BYTES_NOINIT(0x7);          INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved      };      static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,                    "TouchScreenConfigurationForNx is an invalid size"); -    explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_); +    explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);      ~Controller_Touchscreen() override;      // Called when the controller is initialized @@ -42,26 +42,32 @@ public:      void OnRelease() override;      // When the controller is requesting an update for the shared memory -    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; +    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;  private:      static constexpr std::size_t MAX_FINGERS = 16;      // This is nn::hid::TouchScreenState      struct TouchScreenState { -        s64 sampling_number; -        s32 entry_count; +        s64 sampling_number{}; +        s32 entry_count{};          INSERT_PADDING_BYTES(4); // Reserved -        std::array<Core::HID::TouchState, MAX_FINGERS> states; +        std::array<Core::HID::TouchState, MAX_FINGERS> states{};      };      static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); -    // This is nn::hid::detail::TouchScreenLifo -    Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{}; -    static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size"); +    struct TouchSharedMemory { +        // This is nn::hid::detail::TouchScreenLifo +        Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{}; +        static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size"); +        INSERT_PADDING_WORDS(0xF2); +    }; +    static_assert(sizeof(TouchSharedMemory) == 0x3000, "TouchSharedMemory is an invalid size"); +      TouchScreenState next_state{}; +    TouchSharedMemory* shared_memory = nullptr; +    Core::HID::EmulatedConsole* console = nullptr; -    std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers; -    Core::HID::EmulatedConsole* console; +    std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};  };  } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index fc6ac6457..62119e2c5 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp @@ -10,28 +10,31 @@  namespace Service::HID {  constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; -Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {} +Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) +    : ControllerBase{hid_core_} { +    static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size, +                  "XpadSharedMemory is bigger than the shared memory"); +    shared_memory = std::construct_at( +        reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET)); +}  Controller_XPad::~Controller_XPad() = default;  void Controller_XPad::OnInit() {}  void Controller_XPad::OnRelease() {} -void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, -                               std::size_t size) { +void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {      if (!IsControllerActivated()) { -        basic_xpad_lifo.buffer_count = 0; -        basic_xpad_lifo.buffer_tail = 0; -        std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo)); +        shared_memory->basic_xpad_lifo.buffer_count = 0; +        shared_memory->basic_xpad_lifo.buffer_tail = 0;          return;      } -    const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state; +    const auto& last_entry = shared_memory->basic_xpad_lifo.ReadCurrentEntry().state;      next_state.sampling_number = last_entry.sampling_number + 1;      // TODO(ogniK): Update xpad states -    basic_xpad_lifo.WriteNextEntry(next_state); -    std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo)); +    shared_memory->basic_xpad_lifo.WriteNextEntry(next_state);  }  } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index 8211b6ee3..d01dee5fc 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h @@ -12,7 +12,7 @@  namespace Service::HID {  class Controller_XPad final : public ControllerBase {  public: -    explicit Controller_XPad(Core::HID::HIDCore& hid_core_); +    explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);      ~Controller_XPad() override;      // Called when the controller is initialized @@ -22,7 +22,7 @@ public:      void OnRelease() override;      // When the controller is requesting an update for the shared memory -    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; +    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;  private:      // This is nn::hid::BasicXpadAttributeSet @@ -90,17 +90,23 @@ private:      // This is nn::hid::detail::BasicXpadState      struct BasicXpadState { -        s64 sampling_number; -        BasicXpadAttributeSet attributes; -        BasicXpadButtonSet pad_states; -        Core::HID::AnalogStickState l_stick; -        Core::HID::AnalogStickState r_stick; +        s64 sampling_number{}; +        BasicXpadAttributeSet attributes{}; +        BasicXpadButtonSet pad_states{}; +        Core::HID::AnalogStickState l_stick{}; +        Core::HID::AnalogStickState r_stick{};      };      static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size"); -    // This is nn::hid::detail::BasicXpadLifo -    Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{}; -    static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size"); +    struct XpadSharedMemory { +        // This is nn::hid::detail::BasicXpadLifo +        Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{}; +        static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size"); +        INSERT_PADDING_WORDS(0x4E); +    }; +    static_assert(sizeof(XpadSharedMemory) == 0x400, "XpadSharedMemory is an invalid size"); +      BasicXpadState next_state{}; +    XpadSharedMemory* shared_memory = nullptr;  };  } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index fb1ec52b2..36162ac97 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -39,7 +39,6 @@ constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000};  constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)  // TODO: Correct update rate for motion is 5ms. Check why some games don't behave at that speed  constexpr auto motion_update_ns = std::chrono::nanoseconds{10 * 1000 * 1000}; // (10ms, 100Hz) -constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;  IAppletResource::IAppletResource(Core::System& system_,                                   KernelHelpers::ServiceContext& service_context_) @@ -48,20 +47,20 @@ IAppletResource::IAppletResource(Core::System& system_,          {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},      };      RegisterHandlers(functions); - -    MakeController<Controller_DebugPad>(HidController::DebugPad); -    MakeController<Controller_Touchscreen>(HidController::Touchscreen); -    MakeController<Controller_Mouse>(HidController::Mouse); -    MakeController<Controller_Keyboard>(HidController::Keyboard); -    MakeController<Controller_XPad>(HidController::XPad); -    MakeController<Controller_Stubbed>(HidController::HomeButton); -    MakeController<Controller_Stubbed>(HidController::SleepButton); -    MakeController<Controller_Stubbed>(HidController::CaptureButton); -    MakeController<Controller_Stubbed>(HidController::InputDetector); -    MakeController<Controller_Stubbed>(HidController::UniquePad); -    MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad); -    MakeController<Controller_Gesture>(HidController::Gesture); -    MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor); +    u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); +    MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory); +    MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory); +    MakeController<Controller_Mouse>(HidController::Mouse, shared_memory); +    MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory); +    MakeController<Controller_XPad>(HidController::XPad, shared_memory); +    MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory); +    MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory); +    MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory); +    MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory); +    MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory); +    MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory); +    MakeController<Controller_Gesture>(HidController::Gesture, shared_memory); +    MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory);      // Homebrew doesn't try to activate some controllers, so we activate them by default      GetController<Controller_NPad>(HidController::NPad).ActivateController(); @@ -135,8 +134,7 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,          if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {              continue;          } -        controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(), -                             SHARED_MEMORY_SIZE); +        controller->OnUpdate(core_timing);      }      // If ns_late is higher than the update rate ignore the delay @@ -151,10 +149,8 @@ void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,                                            std::chrono::nanoseconds ns_late) {      auto& core_timing = system.CoreTiming(); -    controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate( -        core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); -    controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate( -        core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); +    controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing); +    controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);      // If ns_late is higher than the update rate ignore the delay      if (ns_late > mouse_keyboard_update_ns) { @@ -167,8 +163,7 @@ void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,  void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {      auto& core_timing = system.CoreTiming(); -    controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate( -        core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE); +    controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);      // If ns_late is higher than the update rate ignore the delay      if (ns_late > motion_update_ns) { diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 95778e673..e61f8ed08 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -58,13 +58,14 @@ public:  private:      template <typename T> -    void MakeController(HidController controller) { -        controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system.HIDCore()); +    void MakeController(HidController controller, u8* shared_memory) { +        controllers[static_cast<std::size_t>(controller)] = +            std::make_unique<T>(system.HIDCore(), shared_memory);      }      template <typename T> -    void MakeControllerWithServiceContext(HidController controller) { +    void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {          controllers[static_cast<std::size_t>(controller)] = -            std::make_unique<T>(system.HIDCore(), service_context); +            std::make_unique<T>(system.HIDCore(), shared_memory, service_context);      }      void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp index 185d0387b..8f2920c51 100644 --- a/src/core/hle/service/jit/jit.cpp +++ b/src/core/hle/service/jit/jit.cpp @@ -21,9 +21,10 @@ struct CodeRange {  class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {  public: -    explicit IJitEnvironment(Core::System& system_, CodeRange user_rx, CodeRange user_ro) +    explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx, +                             CodeRange user_ro)          : ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew}, -          context{system_.Memory()} { +          process{&process_}, context{system_.Memory()} {          // clang-format off          static const FunctionInfo functions[] = {              {0, &IJitEnvironment::GenerateCode, "GenerateCode"}, @@ -43,54 +44,80 @@ public:      }      void GenerateCode(Kernel::HLERequestContext& ctx) { -        struct Parameters { +        LOG_DEBUG(Service_JIT, "called"); + +        struct InputParameters {              u32 data_size;              u64 command; -            CodeRange cr1; -            CodeRange cr2; +            std::array<CodeRange, 2> ranges;              Struct32 data;          }; +        struct OutputParameters { +            s32 return_value; +            std::array<CodeRange, 2> ranges; +        }; +          IPC::RequestParser rp{ctx}; -        const auto parameters{rp.PopRaw<Parameters>()}; +        const auto parameters{rp.PopRaw<InputParameters>()}; + +        // Optional input/output buffers          std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};          std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); -        const VAddr return_ptr{context.AddHeap(0u)}; -        const VAddr cr1_in_ptr{context.AddHeap(parameters.cr1)}; -        const VAddr cr2_in_ptr{context.AddHeap(parameters.cr2)}; -        const VAddr cr1_out_ptr{ -            context.AddHeap(CodeRange{.offset = parameters.cr1.offset, .size = 0})}; -        const VAddr cr2_out_ptr{ -            context.AddHeap(CodeRange{.offset = parameters.cr2.offset, .size = 0})}; +        // Function call prototype: +        // void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg, +        //                   u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in, +        //                   CodeRange* c1_in, Struct32* data, size_t data_size, u8* output_buf, +        //                   size_t output_size); +        // +        // The command argument is used to control the behavior of the plugin during code +        // generation. The configuration allows the plugin to access the output code ranges, and the +        // other arguments are used to transfer state between the game and the plugin. + +        const VAddr ret_ptr{context.AddHeap(0u)}; +        const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])}; +        const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])}; +        const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))}; +        const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))}; +          const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};          const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};          const VAddr data_ptr{context.AddHeap(parameters.data)};          const VAddr configuration_ptr{context.AddHeap(configuration)}; -        context.CallFunction(callbacks.GenerateCode, return_ptr, cr1_out_ptr, cr2_out_ptr, +        // The callback does not directly return a value, it only writes to the output pointer +        context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr,                               configuration_ptr, parameters.command, input_ptr, input_buffer.size(), -                             cr1_in_ptr, cr2_in_ptr, data_ptr, parameters.data_size, output_ptr, +                             c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr,                               output_buffer.size()); -        const s32 return_value{context.GetHeap<s32>(return_ptr)}; +        const s32 return_value{context.GetHeap<s32>(ret_ptr)};          if (return_value == 0) { +            // The callback has written to the output executable code range, +            // requiring an instruction cache invalidation              system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset,                                                        configuration.user_rx_memory.size); +            // Write back to the IPC output buffer, if provided              if (ctx.CanWriteBuffer()) {                  context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());                  ctx.WriteBuffer(output_buffer.data(), output_buffer.size());              } -            const auto cr1_out{context.GetHeap<CodeRange>(cr1_out_ptr)}; -            const auto cr2_out{context.GetHeap<CodeRange>(cr2_out_ptr)}; + +            const OutputParameters out{ +                .return_value = return_value, +                .ranges = +                    { +                        context.GetHeap<CodeRange>(c0_out_ptr), +                        context.GetHeap<CodeRange>(c1_out_ptr), +                    }, +            };              IPC::ResponseBuilder rb{ctx, 8};              rb.Push(ResultSuccess); -            rb.Push<u64>(return_value); -            rb.PushRaw(cr1_out); -            rb.PushRaw(cr2_out); +            rb.PushRaw(out);          } else {              LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");              IPC::ResponseBuilder rb{ctx, 2}; @@ -99,25 +126,40 @@ public:      };      void Control(Kernel::HLERequestContext& ctx) { +        LOG_DEBUG(Service_JIT, "called"); +          IPC::RequestParser rp{ctx};          const auto command{rp.PopRaw<u64>()}; -        const auto input_buffer{ctx.ReadBuffer()}; + +        // Optional input/output buffers +        std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};          std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); -        const VAddr return_ptr{context.AddHeap(0u)}; +        // Function call prototype: +        // u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size, +        //             u8* output_buf, size_t output_size); +        // +        // This function is used to set up the state of the plugin before code generation, generally +        // passing objects like pointers to VM state from the game. It is usually called once. + +        const VAddr ret_ptr{context.AddHeap(0u)};          const VAddr configuration_ptr{context.AddHeap(configuration)};          const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};          const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; -        const u64 wrapper_value{ -            context.CallFunction(callbacks.Control, return_ptr, configuration_ptr, command, -                                 input_ptr, input_buffer.size(), output_ptr, output_buffer.size())}; -        const s32 return_value{context.GetHeap<s32>(return_ptr)}; + +        const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, +                                                     command, input_ptr, input_buffer.size(), +                                                     output_ptr, output_buffer.size())}; + +        const s32 return_value{context.GetHeap<s32>(ret_ptr)};          if (wrapper_value == 0 && return_value == 0) { +            // Write back to the IPC output buffer, if provided              if (ctx.CanWriteBuffer()) {                  context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());                  ctx.WriteBuffer(output_buffer.data(), output_buffer.size());              } +              IPC::ResponseBuilder rb{ctx, 3};              rb.Push(ResultSuccess);              rb.Push(return_value); @@ -129,8 +171,13 @@ public:      }      void LoadPlugin(Kernel::HLERequestContext& ctx) { +        LOG_DEBUG(Service_JIT, "called"); +          IPC::RequestParser rp{ctx};          const auto tmem_size{rp.PopRaw<u64>()}; +        const auto tmem_handle{ctx.GetCopyHandle(0)}; +        const auto nro_plugin{ctx.ReadBuffer(1)}; +          if (tmem_size == 0) {              LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");              IPC::ResponseBuilder rb{ctx, 2}; @@ -138,9 +185,7 @@ public:              return;          } -        const auto tmem_handle{ctx.GetCopyHandle(0)}; -        auto tmem{system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( -            tmem_handle)}; +        auto tmem{process->GetHandleTable().GetObject<Kernel::KTransferMemory>(tmem_handle)};          if (tmem.IsNull()) {              LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");              IPC::ResponseBuilder rb{ctx, 2}; @@ -148,24 +193,24 @@ public:              return;          } -        configuration.work_memory.offset = tmem->GetSourceAddress(); -        configuration.work_memory.size = tmem_size; +        // Set up the configuration with the required TransferMemory address +        configuration.transfer_memory.offset = tmem->GetSourceAddress(); +        configuration.transfer_memory.size = tmem_size; -        const auto nro_plugin{ctx.ReadBuffer(1)}; +        // Gather up all the callbacks from the loaded plugin          auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)}; -        const auto GetSymbol{[&](std::string name) { return symbols[name].first; }}; - -        callbacks = -            GuestCallbacks{.rtld_fini = GetSymbol("_fini"), -                           .rtld_init = GetSymbol("_init"), -                           .Control = GetSymbol("nnjitpluginControl"), -                           .ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"), -                           .SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"), -                           .Configure = GetSymbol("nnjitpluginConfigure"), -                           .GenerateCode = GetSymbol("nnjitpluginGenerateCode"), -                           .GetVersion = GetSymbol("nnjitpluginGetVersion"), -                           .Keeper = GetSymbol("nnjitpluginKeeper"), -                           .OnPrepared = GetSymbol("nnjitpluginOnPrepared")}; +        const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }}; + +        callbacks.rtld_fini = GetSymbol("_fini"); +        callbacks.rtld_init = GetSymbol("_init"); +        callbacks.Control = GetSymbol("nnjitpluginControl"); +        callbacks.ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"); +        callbacks.SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"); +        callbacks.Configure = GetSymbol("nnjitpluginConfigure"); +        callbacks.GenerateCode = GetSymbol("nnjitpluginGenerateCode"); +        callbacks.GetVersion = GetSymbol("nnjitpluginGetVersion"); +        callbacks.OnPrepared = GetSymbol("nnjitpluginOnPrepared"); +        callbacks.Keeper = GetSymbol("nnjitpluginKeeper");          if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||              callbacks.OnPrepared == 0) { @@ -186,12 +231,16 @@ public:                                   configuration.sys_ro_memory.size);          context.MapProcessMemory(configuration.sys_rx_memory.offset,                                   configuration.sys_rx_memory.size); -        context.MapProcessMemory(configuration.work_memory.offset, configuration.work_memory.size); +        context.MapProcessMemory(configuration.transfer_memory.offset, +                                 configuration.transfer_memory.size); +        // Run ELF constructors, if needed          if (callbacks.rtld_init != 0) {              context.CallFunction(callbacks.rtld_init);          } +        // Function prototype: +        // u64 GetVersion();          const auto version{context.CallFunction(callbacks.GetVersion)};          if (version != 1) {              LOG_ERROR(Service_JIT, "unknown plugin version {}", version); @@ -200,16 +249,26 @@ public:              return;          } +        // Function prototype: +        // void ResolveBasicSymbols(void (*resolver)(const char* name));          const auto resolve{context.GetHelper("_resolve")};          if (callbacks.ResolveBasicSymbols != 0) {              context.CallFunction(callbacks.ResolveBasicSymbols, resolve);          } + +        // Function prototype: +        // void SetupDiagnostics(u32 enabled, void (**resolver)(const char* name));          const auto resolve_ptr{context.AddHeap(resolve)};          if (callbacks.SetupDiagnostics != 0) {              context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr);          } -        context.CallFunction(callbacks.Configure, 0u); +        // Function prototype: +        // void Configure(u32* memory_flags); +        context.CallFunction(callbacks.Configure, 0ull); + +        // Function prototype: +        // void OnPrepared(JITConfiguration* cfg);          const auto configuration_ptr{context.AddHeap(configuration)};          context.CallFunction(callbacks.OnPrepared, configuration_ptr); @@ -218,6 +277,8 @@ public:      }      void GetCodeAddress(Kernel::HLERequestContext& ctx) { +        LOG_DEBUG(Service_JIT, "called"); +          IPC::ResponseBuilder rb{ctx, 6};          rb.Push(ResultSuccess);          rb.Push(configuration.user_rx_memory.offset); @@ -243,11 +304,17 @@ private:      struct JITConfiguration {          CodeRange user_rx_memory;          CodeRange user_ro_memory; -        CodeRange work_memory; +        CodeRange transfer_memory;          CodeRange sys_rx_memory;          CodeRange sys_ro_memory;      }; +    static CodeRange ClearSize(CodeRange in) { +        in.size = 0; +        return in; +    } + +    Kernel::KScopedAutoObject<Kernel::KProcess> process;      GuestCallbacks callbacks;      JITConfiguration configuration;      JITContext context; @@ -275,8 +342,9 @@ public:          IPC::RequestParser rp{ctx};          const auto parameters{rp.PopRaw<Parameters>()}; -        const auto executable_mem_handle{ctx.GetCopyHandle(1)}; -        const auto readable_mem_handle{ctx.GetCopyHandle(2)}; +        const auto process_handle{ctx.GetCopyHandle(0)}; +        const auto rx_mem_handle{ctx.GetCopyHandle(1)}; +        const auto ro_mem_handle{ctx.GetCopyHandle(2)};          if (parameters.rx_size == 0 || parameters.ro_size == 0) {              LOG_ERROR(Service_JIT, "attempted to init with empty code regions"); @@ -285,42 +353,47 @@ public:              return;          } -        // The copy handle at index 0 is the process handle, but handle tables are -        // per-process, so there is no point reading it here until we are multiprocess -        const auto& process{*system.CurrentProcess()}; +        // Fetch using the handle table for the current process here, +        // since we are not multiprocess yet. +        const auto& handle_table{system.CurrentProcess()->GetHandleTable()}; + +        auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)}; +        if (process.IsNull()) { +            LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); +            IPC::ResponseBuilder rb{ctx, 2}; +            rb.Push(ResultUnknown); +            return; +        } -        auto executable_mem{ -            process.GetHandleTable().GetObject<Kernel::KCodeMemory>(executable_mem_handle)}; -        if (executable_mem.IsNull()) { -            LOG_ERROR(Service_JIT, "executable_mem is null for handle=0x{:08X}", -                      executable_mem_handle); +        auto rx_mem{handle_table.GetObject<Kernel::KCodeMemory>(rx_mem_handle)}; +        if (rx_mem.IsNull()) { +            LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);              IPC::ResponseBuilder rb{ctx, 2};              rb.Push(ResultUnknown);              return;          } -        auto readable_mem{ -            process.GetHandleTable().GetObject<Kernel::KCodeMemory>(readable_mem_handle)}; -        if (readable_mem.IsNull()) { -            LOG_ERROR(Service_JIT, "readable_mem is null for handle=0x{:08X}", readable_mem_handle); +        auto ro_mem{handle_table.GetObject<Kernel::KCodeMemory>(ro_mem_handle)}; +        if (ro_mem.IsNull()) { +            LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);              IPC::ResponseBuilder rb{ctx, 2};              rb.Push(ResultUnknown);              return;          }          const CodeRange user_rx{ -            .offset = executable_mem->GetSourceAddress(), +            .offset = rx_mem->GetSourceAddress(),              .size = parameters.rx_size,          };          const CodeRange user_ro{ -            .offset = readable_mem->GetSourceAddress(), +            .offset = ro_mem->GetSourceAddress(),              .size = parameters.ro_size,          };          IPC::ResponseBuilder rb{ctx, 2, 0, 1};          rb.Push(ResultSuccess); -        rb.PushIpcInterface<IJitEnvironment>(system, user_rx, user_ro); +        rb.PushIpcInterface<IJitEnvironment>(system, *process, user_rx, user_ro);      }  }; diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp index 17e58131c..19bd85b6c 100644 --- a/src/core/hle/service/jit/jit_context.cpp +++ b/src/core/hle/service/jit/jit_context.cpp @@ -17,61 +17,15 @@  namespace Service::JIT { -constexpr std::array<u8, 4> STOP_ARM64 = { +constexpr std::array<u8, 8> SVC0_ARM64 = {      0x01, 0x00, 0x00, 0xd4, // svc  #0 -}; - -constexpr std::array<u8, 8> RESOLVE_ARM64 = { -    0x21, 0x00, 0x00, 0xd4, // svc  #1      0xc0, 0x03, 0x5f, 0xd6, // ret  }; -constexpr std::array<u8, 4> PANIC_ARM64 = { -    0x41, 0x00, 0x00, 0xd4, // svc  #2 -}; - -constexpr std::array<u8, 60> MEMMOVE_ARM64 = { -    0x1f, 0x00, 0x01, 0xeb, // cmp  x0, x1 -    0x83, 0x01, 0x00, 0x54, // b.lo #+34 -    0x42, 0x04, 0x00, 0xd1, // sub  x2, x2, 1 -    0x22, 0x01, 0xf8, 0xb7, // tbnz x2, #63, #+36 -    0x23, 0x68, 0x62, 0x38, // ldrb w3, [x1, x2] -    0x03, 0x68, 0x22, 0x38, // strb w3, [x0, x2] -    0xfc, 0xff, 0xff, 0x17, // b    #-16 -    0x24, 0x68, 0x63, 0x38, // ldrb w4, [x1, x3] -    0x04, 0x68, 0x23, 0x38, // strb w4, [x0, x3] -    0x63, 0x04, 0x00, 0x91, // add  x3, x3, 1 -    0x7f, 0x00, 0x02, 0xeb, // cmp  x3, x2 -    0x8b, 0xff, 0xff, 0x54, // b.lt #-16 -    0xc0, 0x03, 0x5f, 0xd6, // ret -    0x03, 0x00, 0x80, 0xd2, // mov  x3, 0 -    0xfc, 0xff, 0xff, 0x17, // b    #-16 +constexpr std::array HELPER_FUNCTIONS{ +    "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset",  }; -constexpr std::array<u8, 28> MEMSET_ARM64 = { -    0x03, 0x00, 0x80, 0xd2, // mov  x3, 0 -    0x7f, 0x00, 0x02, 0xeb, // cmp  x3, x2 -    0x4b, 0x00, 0x00, 0x54, // b.lt #+8 -    0xc0, 0x03, 0x5f, 0xd6, // ret -    0x01, 0x68, 0x23, 0x38, // strb w1, [x0, x3] -    0x63, 0x04, 0x00, 0x91, // add  x3, x3, 1 -    0xfb, 0xff, 0xff, 0x17, // b    #-20 -}; - -struct HelperFunction { -    const char* name; -    const std::span<const u8> data; -}; - -constexpr std::array<HelperFunction, 6> HELPER_FUNCTIONS{{ -    {"_stop", STOP_ARM64}, -    {"_resolve", RESOLVE_ARM64}, -    {"_panic", PANIC_ARM64}, -    {"memcpy", MEMMOVE_ARM64}, -    {"memmove", MEMMOVE_ARM64}, -    {"memset", MEMSET_ARM64}, -}}; -  struct Elf64_Dyn {      u64 d_tag;      u64 d_un; @@ -224,17 +178,24 @@ public:              InsertHelperFunctions();              InsertStack();              return true; -        } else { -            return false;          } + +        return false;      }      bool FixupRelocations() { +        // The loaded NRO file has ELF relocations that must be processed before it can run. +        // Normally this would be processed by RTLD, but in HLE context, we don't have +        // the linker available, so we have to do it ourselves. +          const VAddr mod_offset{callbacks->MemoryRead32(4)};          if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {              return false;          } +        // For more info about dynamic entries, see the ELF ABI specification: +        // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html +        // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html          VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};          VAddr rela_dyn = 0;          size_t num_rela = 0; @@ -266,13 +227,15 @@ public:      }      void InsertHelperFunctions() { -        for (const auto& [name, contents] : HELPER_FUNCTIONS) { +        for (const auto& name : HELPER_FUNCTIONS) {              helpers[name] = local_memory.size(); -            local_memory.insert(local_memory.end(), contents.begin(), contents.end()); +            local_memory.insert(local_memory.end(), SVC0_ARM64.begin(), SVC0_ARM64.end());          }      }      void InsertStack() { +        // Allocate enough space to avoid any reasonable risk of +        // overflowing the stack during plugin execution          const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) -                               local_memory.size()};          local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0); @@ -292,9 +255,21 @@ public:      }      void SetupArguments() { +        // The first 8 integer registers are used for the first 8 integer +        // arguments. Floating-point arguments are not handled at this time. +        // +        // If a function takes more than 8 arguments, then stack space is reserved +        // for the remaining arguments, and the remaining arguments are inserted in +        // ascending memory order, each argument aligned to an 8-byte boundary. The +        // stack pointer must remain aligned to 16 bytes. +        // +        // For more info, see the AArch64 ABI PCS: +        // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst +          for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) {              jit->SetRegister(i, argument_stack[i]);          } +          if (argument_stack.size() > 8) {              const VAddr new_sp = Common::AlignDown(                  top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN); @@ -303,6 +278,8 @@ public:              }              jit->SetSP(new_sp);          } + +        // Reset the call state for the next invocation          argument_stack.clear();          heap_pointer = top_of_stack;      } @@ -322,11 +299,16 @@ public:      }      VAddr AddHeap(const void* data, size_t size) { +        // Require all heap data types to have the same alignment as the +        // stack pointer, for compatibility          const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)}; + +        // Make additional memory space if required          if (heap_pointer + num_bytes > local_memory.size()) {              local_memory.insert(local_memory.end(),                                  (heap_pointer + num_bytes) - local_memory.size(), 0);          } +          const VAddr location{heap_pointer};          std::memcpy(local_memory.data() + location, data, size);          heap_pointer += num_bytes; @@ -350,30 +332,67 @@ public:  };  void DynarmicCallbacks64::CallSVC(u32 swi) { -    switch (swi) { -    case 0: +    // Service calls are used to implement helper functionality. +    // +    // The most important of these is the _stop helper, which transfers control +    // from the plugin back to HLE context to return a value. However, a few more +    // are also implemented to reduce the need for direct ARM implementations of +    // basic functionality, like memory operations. +    // +    // When we receive a helper request, the swi number will be zero, and the call +    // will have originated from an address we know is a helper function. Otherwise, +    // the plugin may be trying to issue a service call, which we shouldn't handle. + +    if (swi != 0) { +        LOG_CRITICAL(Service_JIT, "plugin issued unknown service call {}", swi);          parent.jit->HaltExecution(); -        break; +        return; +    } + +    u64 pc{parent.jit->GetPC() - 4}; +    auto& helpers{parent.helpers}; + +    if (pc == helpers["memcpy"] || pc == helpers["memmove"]) { +        const VAddr dest{parent.jit->GetRegister(0)}; +        const VAddr src{parent.jit->GetRegister(1)}; +        const size_t n{parent.jit->GetRegister(2)}; + +        if (dest < src) { +            for (size_t i = 0; i < n; i++) { +                MemoryWrite8(dest + i, MemoryRead8(src + i)); +            } +        } else { +            for (size_t i = n; i > 0; i--) { +                MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1)); +            } +        } +    } else if (pc == helpers["memset"]) { +        const VAddr dest{parent.jit->GetRegister(0)}; +        const u64 c{parent.jit->GetRegister(1)}; +        const size_t n{parent.jit->GetRegister(2)}; -    case 1: { +        for (size_t i = 0; i < n; i++) { +            MemoryWrite8(dest + i, static_cast<u8>(c)); +        } +    } else if (pc == helpers["_resolve"]) {          // X0 contains a char* for a symbol to resolve -        std::string name{MemoryReadCString(parent.jit->GetRegister(0))}; -        const auto helper{parent.helpers[name]}; +        const auto name{MemoryReadCString(parent.jit->GetRegister(0))}; +        const auto helper{helpers[name]};          if (helper != 0) {              parent.jit->SetRegister(0, helper);          } else {              LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name); -            parent.jit->SetRegister(0, parent.helpers["_panic"]); +            parent.jit->SetRegister(0, helpers["_panic"]);          } -        break; -    } - -    case 2: -    default: +    } else if (pc == helpers["_stop"]) { +        parent.jit->HaltExecution(); +    } else if (pc == helpers["_panic"]) {          LOG_CRITICAL(Service_JIT, "plugin panicked!");          parent.jit->HaltExecution(); -        break; +    } else { +        LOG_CRITICAL(Service_JIT, "plugin issued syscall at unknown address 0x{:x}", pc); +        parent.jit->HaltExecution();      }  } diff --git a/src/core/hle/service/jit/jit_context.h b/src/core/hle/service/jit/jit_context.h index 3cb935e3c..f17fc5e24 100644 --- a/src/core/hle/service/jit/jit_context.h +++ b/src/core/hle/service/jit/jit_context.h @@ -28,6 +28,7 @@ public:      template <typename T, typename... Ts>      u64 CallFunction(VAddr func, T argument, Ts... rest) {          static_assert(std::is_trivially_copyable_v<T>); +        static_assert(!std::is_floating_point_v<T>);          PushArgument(&argument, sizeof(argument));          if constexpr (sizeof...(rest) > 0) { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 7a1d911eb..7c78d0299 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -127,6 +127,11 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {      if (!render_window.IsShown()) {          return;      } +    // Update screen info if the framebuffer size has changed. +    if (screen_info.width != framebuffer->width || screen_info.height != framebuffer->height) { +        screen_info.width = framebuffer->width; +        screen_info.height = framebuffer->height; +    }      const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;      const bool use_accelerated =          rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 46e5409db..d3a60cdd1 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -9,6 +9,7 @@  #include <QDirIterator>  #include "common/common_types.h"  #include "common/fs/path_util.h" +#include "common/logging/log.h"  #include "common/settings.h"  #include "core/core.h"  #include "ui_configure_ui.h" @@ -170,14 +171,74 @@ void ConfigureUi::RetranslateUI() {  }  void ConfigureUi::InitializeLanguageComboBox() { +    // This is a list of lexicographically sorted languages, only the available translations are +    // shown to the user. +    static const struct { +        const QString name; +        const char* id; +    } languages[] = { +        // clang-format off +        {QStringLiteral(u"Bahasa Indonesia"), "id"},                                 // Indonesian +        {QStringLiteral(u"Bahasa Melayu"), "ms"},                                    // Malay +        {QStringLiteral(u"Catal\u00E0"), "ca"},                                      // Catalan +        {QStringLiteral(u"\u010Ce\u0161tina"), "cs"},                                // Czech +        {QStringLiteral(u"Dansk"), "da"},                                            // Danish +        {QStringLiteral(u"Deutsch"), "de"},                                          // German +        {QStringLiteral(u"English"), "en"},                                          // English +        {QStringLiteral(u"Espa\u00F1ol"), "es"},                                     // Spanish +        {QStringLiteral(u"Fran\u00E7ais"), "fr"},                                    // French +        {QStringLiteral(u"Hrvatski"), "hr"},                                         // Croatian +        {QStringLiteral(u"Italiano"), "it"},                                         // Italian +        {QStringLiteral(u"Magyar"), "hu"},                                           // Hungarian +        {QStringLiteral(u"Nederlands"), "nl"},                                       // Dutch +        {QStringLiteral(u"Norsk bokm\u00E5l"), "nb"},                                // Norwegian +        {QStringLiteral(u"Polski"), "pl"},                                           // Polish +        {QStringLiteral(u"Portugu\u00EAs"), "pt_PT"},                                // Portuguese +        {QStringLiteral(u"Portugu\u00EAs (Brasil)"), "pt_BR"},                       // Portuguese (Brazil) +        {QStringLiteral(u"Rom\u00E2n\u0103"), "ro"},                                 // Romanian +        {QStringLiteral(u"Srpski"), "sr"},                                           // Serbian +        {QStringLiteral(u"Suomi"), "fi"},                                            // Finnish +        {QStringLiteral(u"Svenska"), "sv"},                                          // Swedish +        {QStringLiteral(u"Ti\u1EBFng Vi\u1EC7t"), "vi"},                             // Vietnamese +        {QStringLiteral(u"Ti\u1EBFng Vi\u1EC7t (Vi\u1EC7t Nam)"), "vi_VN"},          // Vietnamese +        {QStringLiteral(u"T\u00FCrk\u00E7e"), "tr_TR"},                              // Turkish +        {QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"}, // Greek +        {QStringLiteral(u"\u0420\u0443\u0441\u0441\u043A\u0438\u0439"), "ru_RU"},    // Russian +        {QStringLiteral(u"\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"), +         "uk"},                                                                      // Ukrainian +        {QStringLiteral(u"\u0627\u0644\u0639\u0631\u0628\u064A\u0629"), "ar"},       // Arabic +        {QStringLiteral(u"\u0641\u0627\u0631\u0633\u06CC"), "fa"},                   // Farsi +        {QStringLiteral(u"\uD55C\uAD6D\uC5B4"), "ko_KR"},                            // Korean +        {QStringLiteral(u"\u65E5\u672C\u8A9E"), "ja_JP"},                            // Japanese +        {QStringLiteral(u"\u7B80\u4F53\u4E2D\u6587"), "zh_CN"},                      // Simplified Chinese +        {QStringLiteral(u"\u7E41\u9AD4\u4E2D\u6587"), "zh_TW"},                      // Traditional Chinese +        // clang-format on +    };      ui->language_combobox->addItem(tr("<System>"), QString{}); -    ui->language_combobox->addItem(tr("English"), QStringLiteral("en")); -    QDirIterator it(QStringLiteral(":/languages"), QDirIterator::NoIteratorFlags); -    while (it.hasNext()) { -        QString locale = it.next(); +    QDir languages_dir{QStringLiteral(":/languages")}; +    QStringList language_files = languages_dir.entryList(); +    for (const auto& lang : languages) { +        if (QString::fromLatin1(lang.id) == QStringLiteral("en")) { +            ui->language_combobox->addItem(lang.name, QStringLiteral("en")); +            continue; +        } +        for (int i = 0; i < language_files.size(); ++i) { +            QString locale = language_files[i]; +            locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); +            if (QString::fromLatin1(lang.id) == locale) { +                ui->language_combobox->addItem(lang.name, locale); +                language_files.removeAt(i); +                break; +            } +        } +    } +    // Anything remaining will be at the bottom +    for (const QString& file : language_files) { +        LOG_CRITICAL(Frontend, "Unexpected Language File: {}", file.toStdString()); +        QString locale = file;          locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); -        locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1); -        const QString lang = QLocale::languageToString(QLocale(locale).language()); +        const QString language_name = QLocale::languageToString(QLocale(locale).language()); +        const QString lang = QStringLiteral("%1 [%2]").arg(language_name, locale);          ui->language_combobox->addItem(lang, locale);      } | 
