Просмотр исходного кода

HID Implementation (#20)

* Basic HID Implementation

* Basic HID Implementation in Config

* HID Corrections

* HID Corrections 2
Ac_K 8 лет назад
Родитель
Сommit
f469b968a8

+ 37 - 0
Ryujinx/Config.cs

@@ -16,6 +16,8 @@ namespace Ryujinx
         public static bool LoggingEnableFatal { get; private set; }
         public static bool LoggingEnableLogFile { get; private set; }
 
+        public static JoyCon FakeJoyCon { get; private set; }
+
         public static void Read()
         {
             IniParser Parser = new IniParser(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Ryujinx.conf"));
@@ -27,6 +29,41 @@ namespace Ryujinx
             LoggingEnableError   = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
             LoggingEnableFatal   = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
             LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
+
+            FakeJoyCon = new JoyCon
+            {
+                Left = new JoyConLeft
+                {
+                    StickUp     = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Up")),
+                    StickDown   = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Down")),
+                    StickLeft   = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Left")),
+                    StickRight  = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Right")),
+                    StickButton = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Button")),
+                    DPadUp      = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Up")),
+                    DPadDown    = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Down")),
+                    DPadLeft    = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Left")),
+                    DPadRight   = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Right")),
+                    ButtonMinus = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_Minus")),
+                    ButtonL     = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_L")),
+                    ButtonZL    = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_ZL"))
+                },
+
+                Right = new JoyConRight
+                {
+                    StickUp     = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Up")),
+                    StickDown   = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Down")),
+                    StickLeft   = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Left")),
+                    StickRight  = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Right")),
+                    StickButton = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Button")),
+                    ButtonA     = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_A")),
+                    ButtonB     = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_B")),
+                    ButtonX     = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_X")),
+                    ButtonY     = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_Y")),
+                    ButtonPlus  = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_Plus")),
+                    ButtonR     = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_R")),
+                    ButtonZR    = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_ZR"))
+                }
+            };
         }
     }
 

+ 185 - 0
Ryujinx/Hid.cs

@@ -0,0 +1,185 @@
+using Ryujinx.OsHle;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx
+{
+    public class Hid
+    {
+        /*
+        Thanks to:
+        https://github.com/reswitched/libtransistor/blob/development/lib/hid.c
+        https://github.com/reswitched/libtransistor/blob/development/include/libtransistor/hid.h
+        https://github.com/switchbrew/libnx/blob/master/nx/source/services/hid.c
+        https://github.com/switchbrew/libnx/blob/master/nx/include/switch/services/hid.h
+        
+        struct HidSharedMemory
+        {
+            header[0x400];
+            touchscreen[0x3000];
+            mouse[0x400];
+            keyboard[0x400];
+            unkSection1[0x400];
+            unkSection2[0x400];
+            unkSection3[0x400];
+            unkSection4[0x400];
+            unkSection5[0x200];
+            unkSection6[0x200];
+            unkSection7[0x200];
+            unkSection8[0x800];
+            controllerSerials[0x4000];
+            controllers[0x5000 * 10]; 
+            unkSection9[0x4600];
+        }
+        */
+
+        private const int Hid_Num_Entries = 16;
+        private Switch Ns;
+        private long SharedMemOffset;
+
+        public Hid(Switch Ns)
+        {
+            this.Ns = Ns;
+        }
+
+        public void Init(long HidOffset)
+        {
+            unsafe
+            {
+                if (HidOffset == 0 || HidOffset + Horizon.HidSize > uint.MaxValue)
+                {
+                    return;
+                }
+
+                SharedMemOffset = HidOffset;
+
+                uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader));
+
+                IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset);
+
+                HidTouchScreen TouchScreen = new HidTouchScreen();
+                TouchScreen.Header.TimestampTicks = (ulong)Environment.TickCount;
+                TouchScreen.Header.NumEntries = (ulong)Hid_Num_Entries;
+                TouchScreen.Header.LatestEntry = 0;
+                TouchScreen.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1;
+                TouchScreen.Header.Timestamp = (ulong)Environment.TickCount;
+
+                //TODO: Write this structure when the input is implemented
+                //Marshal.StructureToPtr(TouchScreen, HidPtr, false);
+
+                InnerOffset += (uint)Marshal.SizeOf(typeof(HidTouchScreen));
+                HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset);
+
+                HidMouse Mouse = new HidMouse();
+                Mouse.Header.TimestampTicks = (ulong)Environment.TickCount;
+                Mouse.Header.NumEntries = (ulong)Hid_Num_Entries;
+                Mouse.Header.LatestEntry = 0;
+                Mouse.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1;
+
+                //TODO: Write this structure when the input is implemented
+                //Marshal.StructureToPtr(Mouse, HidPtr, false);
+
+                InnerOffset += (uint)Marshal.SizeOf(typeof(HidMouse));
+                HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset);
+
+                HidKeyboard Keyboard = new HidKeyboard();
+                Keyboard.Header.TimestampTicks = (ulong)Environment.TickCount;
+                Keyboard.Header.NumEntries = (ulong)Hid_Num_Entries;
+                Keyboard.Header.LatestEntry = 0;
+                Keyboard.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1;
+
+                //TODO: Write this structure when the input is implemented
+                //Marshal.StructureToPtr(Keyboard, HidPtr, false);
+
+                InnerOffset += (uint)Marshal.SizeOf(typeof(HidKeyboard)) +
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection1)) + 
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection2)) + 
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection3)) + 
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection4)) +
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection5)) + 
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection6)) + 
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection7)) +
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection8)) + 
+                               (uint)Marshal.SizeOf(typeof(HidControllerSerials));
+
+                //Increase the loop to initialize more controller.
+                for (int i = 8; i < Enum.GetNames(typeof(HidControllerID)).Length - 1; i++)
+                {
+                    HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset + (uint)(Marshal.SizeOf(typeof(HidController)) * i));
+
+                    HidController Controller = new HidController();
+                    Controller.Header.Type = (uint)(HidControllerType.ControllerType_Handheld | HidControllerType.ControllerType_JoyconPair);
+                    Controller.Header.IsHalf = 0;
+                    Controller.Header.SingleColorsDescriptor = (uint)(HidControllerColorDescription.ColorDesc_ColorsNonexistent);
+                    Controller.Header.SingleColorBody = 0;
+                    Controller.Header.SingleColorButtons = 0;
+                    Controller.Header.SplitColorsDescriptor = 0;
+                    Controller.Header.LeftColorBody = (uint)JoyConColor.Body_Neon_Red;
+                    Controller.Header.LeftColorButtons = (uint)JoyConColor.Buttons_Neon_Red;
+                    Controller.Header.RightColorBody = (uint)JoyConColor.Body_Neon_Blue;
+                    Controller.Header.RightColorButtons = (uint)JoyConColor.Buttons_Neon_Blue;
+
+                    Controller.Layouts = new HidControllerLayout[Enum.GetNames(typeof(HidControllerLayouts)).Length];
+                    Controller.Layouts[(int)HidControllerLayouts.Main] = new HidControllerLayout();
+                    Controller.Layouts[(int)HidControllerLayouts.Main].Header.LatestEntry = (ulong)Hid_Num_Entries;
+
+                    Marshal.StructureToPtr(Controller, HidPtr, false);
+                }
+
+                Logging.Info("HID Initialized!");
+            }
+        }
+
+        public void SendControllerButtons(HidControllerID ControllerId, 
+                                          HidControllerLayouts Layout, 
+                                          HidControllerKeys Buttons, 
+                                          JoystickPosition LeftJoystick, 
+                                          JoystickPosition RightJoystick)
+        {
+            uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader)) +
+                               (uint)Marshal.SizeOf(typeof(HidTouchScreen)) +
+                               (uint)Marshal.SizeOf(typeof(HidMouse)) +
+                               (uint)Marshal.SizeOf(typeof(HidKeyboard)) +
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection1)) + 
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection2)) + 
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection3)) + 
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection4)) +
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection5)) + 
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection6)) + 
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection7)) +
+                               (uint)Marshal.SizeOf(typeof(HidUnknownSection8)) + 
+                               (uint)Marshal.SizeOf(typeof(HidControllerSerials)) +
+                               ((uint)(Marshal.SizeOf(typeof(HidController)) * (int)ControllerId)) +
+                               (uint)Marshal.SizeOf(typeof(HidControllerHeader)) +
+                               (uint)Layout * (uint)Marshal.SizeOf(typeof(HidControllerLayout));
+
+            IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset);
+
+            HidControllerLayoutHeader OldControllerHeaderLayout = (HidControllerLayoutHeader)Marshal.PtrToStructure(HidPtr, typeof(HidControllerLayoutHeader));
+
+            HidControllerLayoutHeader ControllerLayoutHeader = new HidControllerLayoutHeader
+            {
+                TimestampTicks = (ulong)Environment.TickCount,
+                NumEntries = (ulong)Hid_Num_Entries,
+                MaxEntryIndex = (ulong)Hid_Num_Entries - 1,
+                LatestEntry = (OldControllerHeaderLayout.LatestEntry < (ulong)Hid_Num_Entries ? OldControllerHeaderLayout.LatestEntry + 1 : 0)
+            };
+
+            Marshal.StructureToPtr(ControllerLayoutHeader, HidPtr, false);
+
+            InnerOffset += (uint)Marshal.SizeOf(typeof(HidControllerLayoutHeader)) + (uint)((uint)(ControllerLayoutHeader.LatestEntry) * Marshal.SizeOf(typeof(HidControllerInputEntry)));
+            HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset);
+
+            HidControllerInputEntry ControllerInputEntry = new HidControllerInputEntry();
+            ControllerInputEntry.Timestamp = (ulong)Environment.TickCount;
+            ControllerInputEntry.Timestamp_2 = (ulong)Environment.TickCount;
+            ControllerInputEntry.Buttons = (ulong)Buttons;
+            ControllerInputEntry.Joysticks = new JoystickPosition[(int)HidControllerJoystick.Joystick_Num_Sticks];
+            ControllerInputEntry.Joysticks[(int)HidControllerJoystick.Joystick_Left] = LeftJoystick;
+            ControllerInputEntry.Joysticks[(int)HidControllerJoystick.Joystick_Right] = RightJoystick;
+            ControllerInputEntry.ConnectionState = (ulong)(HidControllerConnectionState.Controller_State_Connected | HidControllerConnectionState.Controller_State_Wired);
+
+            Marshal.StructureToPtr(ControllerInputEntry, HidPtr, false);
+        }
+    }
+}

+ 184 - 0
Ryujinx/Hid/HidController.cs

@@ -0,0 +1,184 @@
+using System.Runtime.InteropServices;
+ 
+namespace Ryujinx
+{
+    public enum HidControllerKeys
+    {
+        KEY_A            = (1 << 0),
+        KEY_B            = (1 << 1),
+        KEY_X            = (1 << 2),
+        KEY_Y            = (1 << 3),
+        KEY_LSTICK       = (1 << 4),
+        KEY_RSTICK       = (1 << 5),
+        KEY_L            = (1 << 6),
+        KEY_R            = (1 << 7),
+        KEY_ZL           = (1 << 8),
+        KEY_ZR           = (1 << 9),
+        KEY_PLUS         = (1 << 10),
+        KEY_MINUS        = (1 << 11),
+        KEY_DLEFT        = (1 << 12),
+        KEY_DUP          = (1 << 13),
+        KEY_DRIGHT       = (1 << 14),
+        KEY_DDOWN        = (1 << 15),
+        KEY_LSTICK_LEFT  = (1 << 16),
+        KEY_LSTICK_UP    = (1 << 17),
+        KEY_LSTICK_RIGHT = (1 << 18),
+        KEY_LSTICK_DOWN  = (1 << 19),
+        KEY_RSTICK_LEFT  = (1 << 20),
+        KEY_RSTICK_UP    = (1 << 21),
+        KEY_RSTICK_RIGHT = (1 << 22),
+        KEY_RSTICK_DOWN  = (1 << 23),
+        KEY_SL           = (1 << 24),
+        KEY_SR           = (1 << 25),
+
+        // Pseudo-key for at least one finger on the touch screen
+        KEY_TOUCH        = (1 << 26),
+
+        // Buttons by orientation (for single Joy-Con), also works with Joy-Con pairs, Pro Controller
+        KEY_JOYCON_RIGHT = (1 << 0),
+        KEY_JOYCON_DOWN  = (1 << 1),
+        KEY_JOYCON_UP    = (1 << 2),
+        KEY_JOYCON_LEFT  = (1 << 3),
+
+        // Generic catch-all directions, also works for single Joy-Con
+        KEY_UP           = KEY_DUP | KEY_LSTICK_UP | KEY_RSTICK_UP,
+        KEY_DOWN         = KEY_DDOWN | KEY_LSTICK_DOWN | KEY_RSTICK_DOWN,
+        KEY_LEFT         = KEY_DLEFT | KEY_LSTICK_LEFT | KEY_RSTICK_LEFT,
+        KEY_RIGHT        = KEY_DRIGHT | KEY_LSTICK_RIGHT | KEY_RSTICK_RIGHT,
+    }
+
+    public enum HidControllerID
+    {
+        CONTROLLER_PLAYER_1 = 0,
+        CONTROLLER_PLAYER_2 = 1,
+        CONTROLLER_PLAYER_3 = 2,
+        CONTROLLER_PLAYER_4 = 3,
+        CONTROLLER_PLAYER_5 = 4,
+        CONTROLLER_PLAYER_6 = 5,
+        CONTROLLER_PLAYER_7 = 6,
+        CONTROLLER_PLAYER_8 = 7,
+        CONTROLLER_HANDHELD = 8,
+        CONTROLLER_UNKNOWN  = 9
+    }
+
+    public enum HidControllerJoystick
+    {
+        Joystick_Left       = 0,
+        Joystick_Right      = 1,
+        Joystick_Num_Sticks = 2
+    }
+
+    public enum HidControllerLayouts
+    {
+        Pro_Controller,
+        Handheld_Joined,
+        Joined,
+        Left,
+        Right,
+        Main_No_Analog,
+        Main
+    }
+
+    public enum HidControllerConnectionState
+    {
+        Controller_State_Connected = (1 << 0),
+        Controller_State_Wired     = (1 << 1)
+    }
+
+    public enum HidControllerType
+    {
+        ControllerType_ProController = (1 << 0),
+        ControllerType_Handheld      = (1 << 1),
+        ControllerType_JoyconPair    = (1 << 2),
+        ControllerType_JoyconLeft    = (1 << 3),
+        ControllerType_JoyconRight   = (1 << 4)
+    }
+
+    public enum HidControllerColorDescription
+    {
+        ColorDesc_ColorsNonexistent = (1 << 1),
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x8)]
+    public struct JoystickPosition
+    {
+        public int DX;
+        public int DY;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x20)]
+    public struct HidControllerMAC
+    {
+        public ulong Timestamp;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+        public byte[] MAC;
+        public ulong Unknown;
+        public ulong Timestamp_2;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x28)]
+    public struct HidControllerHeader
+    {
+        public uint Type;
+        public uint IsHalf;
+        public uint SingleColorsDescriptor;
+        public uint SingleColorBody;
+        public uint SingleColorButtons;
+        public uint SplitColorsDescriptor;
+        public uint LeftColorBody;
+        public uint LeftColorButtons;
+        public uint RightColorBody;
+        public uint RightColorButtons;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x20)]
+    public struct HidControllerLayoutHeader
+    {
+        public ulong TimestampTicks;
+        public ulong NumEntries;
+        public ulong LatestEntry;
+        public ulong MaxEntryIndex;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x30)]
+    public struct HidControllerInputEntry
+    {
+        public ulong Timestamp;
+        public ulong Timestamp_2;
+        public ulong Buttons;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)HidControllerJoystick.Joystick_Num_Sticks)]
+        public JoystickPosition[] Joysticks;
+        public ulong ConnectionState;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x350)]
+    public struct HidControllerLayout
+    {
+        public HidControllerLayoutHeader Header;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
+        public HidControllerInputEntry[] Entries;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x5000)]
+    public struct HidController
+    {
+        public HidControllerHeader Header;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
+        public HidControllerLayout[] Layouts;
+        /*
+            pro_controller
+            handheld_joined
+            joined
+            left
+            right
+            main_no_analog
+            main
+        */
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x2A70)]
+        public byte[] Unknown_1;
+        public HidControllerMAC MacLeft;
+        public HidControllerMAC MacRight;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xDF8)]
+        public byte[] Unknown_2;
+    }
+}

+ 33 - 0
Ryujinx/Hid/HidKeyboard.cs

@@ -0,0 +1,33 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x20)]
+    public struct HidKeyboardHeader
+    {
+        public ulong TimestampTicks;
+        public ulong NumEntries;
+        public ulong LatestEntry;
+        public ulong MaxEntryIndex;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x38)]
+    public struct HidKeyboardEntry
+    {
+        public ulong Timestamp;
+        public ulong Timestamp_2;
+        public ulong Modifier;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+        public uint[] Keys;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x400)]
+    public struct HidKeyboard
+    {
+        public HidKeyboardHeader Header;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
+        public HidKeyboardEntry[] Entries;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x28)]
+        public byte[] Padding;
+    }
+}

+ 37 - 0
Ryujinx/Hid/HidMouse.cs

@@ -0,0 +1,37 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x20)]
+    public struct HidMouseHeader
+    {
+        public ulong TimestampTicks;
+        public ulong NumEntries;
+        public ulong LatestEntry;
+        public ulong MaxEntryIndex;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x30)]
+    public struct HidMouseEntry
+    {
+        public ulong Timestamp;
+        public ulong Timestamp_2;
+        public uint X;
+        public uint Y;
+        public uint VelocityX;
+        public uint VelocityY;
+        public uint ScrollVelocityX;
+        public uint ScrollVelocityY;
+        public ulong Buttons;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x400)]
+    public struct HidMouse
+    {
+        public HidMouseHeader Header;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
+        public HidMouseEntry[] Entries;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xB0)]
+        public byte[] Padding;
+    }
+}

+ 54 - 0
Ryujinx/Hid/HidTouchScreen.cs

@@ -0,0 +1,54 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x28)]
+    public struct HidTouchScreenHeader
+    {
+        public ulong TimestampTicks;
+        public ulong NumEntries;
+        public ulong LatestEntry;
+        public ulong MaxEntryIndex;
+        public ulong Timestamp;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x10)]
+    public struct HidTouchScreenEntryHeader
+    {
+        public ulong Timestamp;
+        public ulong NumTouches;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x28)]
+    public struct HidTouchScreenEntryTouch
+    {
+        public ulong Timestamp;
+        public uint Padding;
+        public uint TouchIndex;
+        public uint X;
+        public uint Y;
+        public uint DiameterX;
+        public uint DiameterY;
+        public uint Angle;
+        public uint Padding_2;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x298)]
+    public struct HidTouchScreenEntry
+    {
+        public HidTouchScreenEntryHeader Header;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
+        public HidTouchScreenEntryTouch[] Touches; 
+        public ulong Unknown;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x3000)]
+    public struct HidTouchScreen
+    {
+        public HidTouchScreenHeader Header;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
+        public HidTouchScreenEntry[] Entries;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3C0)]
+        public byte[] Padding;
+    }
+}

+ 81 - 0
Ryujinx/Hid/HidUnknown.cs

@@ -0,0 +1,81 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x400)]
+    public struct HidSharedMemHeader
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
+        public byte[] Padding;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x400)]
+    public struct HidUnknownSection1
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
+        public byte[] Padding;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x400)]
+    public struct HidUnknownSection2
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
+        public byte[] Padding;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x400)]
+    public struct HidUnknownSection3
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
+        public byte[] Padding;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x400)]
+    public struct HidUnknownSection4
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
+        public byte[] Padding;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x200)]
+    public struct HidUnknownSection5
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)]
+        public byte[] Padding;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x200)]
+    public struct HidUnknownSection6
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)]
+        public byte[] Padding;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x200)]
+    public struct HidUnknownSection7
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)]
+        public byte[] Padding;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x800)]
+    public struct HidUnknownSection8
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x800)]
+        public byte[] Padding;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x4000)]
+    public struct HidControllerSerials
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)]
+        public byte[] Padding;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Size = 0x4600)]
+    public struct HidUnknownSection9
+    {
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4600)]
+        public byte[] Padding;
+    }
+}

+ 63 - 0
Ryujinx/Hid/JoyCon.cs

@@ -0,0 +1,63 @@
+namespace Ryujinx
+{
+    public enum JoyConColor //Thanks to CTCaer
+    {
+        Body_Grey = 0x828282,
+        Body_Neon_Blue = 0x0AB9E6,
+        Body_Neon_Red = 0xFF3C28,
+        Body_Neon_Yellow = 0xE6FF00,
+        Body_Neon_Pink = 0xFF3278,
+        Body_Neon_Green = 0x1EDC00,
+        Body_Red = 0xE10F00,
+
+        Buttons_Grey = 0x0F0F0F,
+        Buttons_Neon_Blue = 0x001E1E,
+        Buttons_Neon_Red = 0x1E0A0A,
+        Buttons_Neon_Yellow = 0x142800,
+        Buttons_Neon_Pink = 0x28001E,
+        Buttons_Neon_Green = 0x002800,
+        Buttons_Red = 0x280A0A
+    }
+
+    public struct JoyConLeft
+    {
+        public int StickUp;
+        public int StickDown;
+        public int StickLeft;
+        public int StickRight;
+        public int StickButton;
+        public int DPadUp;
+        public int DPadDown;
+        public int DPadLeft;
+        public int DPadRight;
+        public int ButtonMinus;
+        public int ButtonL;
+        public int ButtonZL;
+        public int ButtonSL;
+        public int ButtonSR;
+    }
+
+    public struct JoyConRight
+    {
+        public int StickUp;
+        public int StickDown;
+        public int StickLeft;
+        public int StickRight;
+        public int StickButton;
+        public int ButtonA;
+        public int ButtonB;
+        public int ButtonX;
+        public int ButtonY;
+        public int ButtonPlus;
+        public int ButtonR;
+        public int ButtonZR;
+        public int ButtonSL;
+        public int ButtonSR;
+    }
+
+    public struct JoyCon
+    {
+        public JoyConLeft Left;
+        public JoyConRight Right;
+    }
+}

+ 2 - 8
Ryujinx/OsHle/Horizon.cs

@@ -87,7 +87,7 @@ namespace Ryujinx.OsHle
                         continue;
                     }
 
-                    Logging.Info($"Loding {Path.GetFileNameWithoutExtension(File)}...");
+                    Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}...");
 
                     using (FileStream Input = new FileStream(File, FileMode.Open))
                     {
@@ -195,14 +195,8 @@ namespace Ryujinx.OsHle
             if (SharedMem.TryGetLastVirtualPosition(out long Position))
             {
                 Logging.Info($"HID shared memory successfully mapped to {Position:x16}!");
+                Ns.Hid.Init(Position);
             }
         }
-
-        public long GetVirtHidOffset()
-        {
-            HidSharedMem.TryGetLastVirtualPosition(out long Position);
-
-            return Position;
-        }
     }
 }

+ 27 - 0
Ryujinx/Ryujinx.conf

@@ -18,3 +18,30 @@ Logging_Enable_Fatal = true
 
 #Saved logs into Ryujinx.log
 Logging_Enable_LogFile = false
+
+#https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs
+Controls_Left_FakeJoycon_Stick_Up = 91
+Controls_Left_FakeJoycon_Stick_Down = 93
+Controls_Left_FakeJoycon_Stick_Left = 92
+Controls_Left_FakeJoycon_Stick_Right = 94
+Controls_Left_FakeJoycon_Stick_Button = 0
+Controls_Left_FakeJoycon_DPad_Up = 0
+Controls_Left_FakeJoycon_DPad_Down = 0
+Controls_Left_FakeJoycon_DPad_Left = 0
+Controls_Left_FakeJoycon_DPad_Right = 0
+Controls_Left_FakeJoycon_Button_Minus = 52
+Controls_Left_FakeJoycon_Button_L = 0
+Controls_Left_FakeJoycon_Button_ZL = 0
+
+Controls_Right_FakeJoycon_Stick_Up = 45
+Controls_Right_FakeJoycon_Stick_Down = 46
+Controls_Right_FakeJoycon_Stick_Left = 47
+Controls_Right_FakeJoycon_Stick_Right = 48
+Controls_Right_FakeJoycon_Stick_Button = 0
+Controls_Right_FakeJoycon_Button_A = 83
+Controls_Right_FakeJoycon_Button_B = 101
+Controls_Right_FakeJoycon_Button_X = 106
+Controls_Right_FakeJoycon_Button_Y = 108
+Controls_Right_FakeJoycon_Button_Plus = 49
+Controls_Right_FakeJoycon_Button_R = 0
+Controls_Right_FakeJoycon_Button_ZR = 0

+ 2 - 0
Ryujinx/Switch.cs

@@ -14,6 +14,7 @@ namespace Ryujinx
         internal NsGpu     Gpu { get; private set; }
         internal Horizon   Os  { get; private set; }
         internal VirtualFs VFs { get; private set; }
+        internal Hid       Hid { get; private set; }
 
         public event EventHandler Finish;
 
@@ -24,6 +25,7 @@ namespace Ryujinx
             Gpu = new NsGpu(Renderer);
             Os  = new Horizon(this);
             VFs = new VirtualFs();
+            Hid = new Hid(this);
         }
 
         internal virtual void OnFinish(EventArgs e)

+ 53 - 67
Ryujinx/Ui/GLScreen.cs

@@ -276,76 +276,62 @@ void main(void) {
 
         protected override void OnUpdateFrame(FrameEventArgs e)
         {
-            unsafe
+            HidControllerKeys CurrentButton = 0;
+            JoystickPosition LeftJoystick;
+            JoystickPosition RightJoystick;
+
+            if (Keyboard[OpenTK.Input.Key.Escape]) this.Exit();
+
+            //RightJoystick
+            int LeftJoystickDX = 0;
+            int LeftJoystickDY = 0;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickUp])     LeftJoystickDY = short.MaxValue;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickDown])   LeftJoystickDY = -short.MaxValue;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickLeft])   LeftJoystickDX = -short.MaxValue;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickRight])  LeftJoystickDX = short.MaxValue;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickButton]) CurrentButton |= HidControllerKeys.KEY_LSTICK;
+
+            //LeftButtons
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadUp])      CurrentButton |= HidControllerKeys.KEY_DUP;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadDown])    CurrentButton |= HidControllerKeys.KEY_DDOWN;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadLeft])    CurrentButton |= HidControllerKeys.KEY_DLEFT;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadRight])   CurrentButton |= HidControllerKeys.KEY_DRIGHT;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonMinus]) CurrentButton |= HidControllerKeys.KEY_MINUS;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonL])     CurrentButton |= HidControllerKeys.KEY_L;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonZL])    CurrentButton |= HidControllerKeys.KEY_ZL;
+
+            //RightJoystick
+            int RightJoystickDX = 0;
+            int RightJoystickDY = 0;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickUp])    RightJoystickDY = short.MaxValue;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickDown])  RightJoystickDY = -short.MaxValue;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickLeft])  RightJoystickDX = -short.MaxValue;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickRight]) RightJoystickDX = short.MaxValue;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickButton]) CurrentButton |= HidControllerKeys.KEY_RSTICK;
+
+            //RightButtons
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonA])    CurrentButton |= HidControllerKeys.KEY_A;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonB])    CurrentButton |= HidControllerKeys.KEY_B;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonX])    CurrentButton |= HidControllerKeys.KEY_X;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonY])    CurrentButton |= HidControllerKeys.KEY_Y;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonPlus]) CurrentButton |= HidControllerKeys.KEY_PLUS;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonR])    CurrentButton |= HidControllerKeys.KEY_R;
+            if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonZR])   CurrentButton |= HidControllerKeys.KEY_ZR;
+
+            LeftJoystick = new JoystickPosition
             {
-                long HidOffset = Ns.Os.GetVirtHidOffset();
-
-                if (HidOffset == 0 || HidOffset + Horizon.HidSize > uint.MaxValue)
-                {
-                    return;
-                }
-
-                byte* Ptr = (byte*)Ns.Ram + (uint)HidOffset;
-
-                int State = 0;
-                
-                if (Keyboard[OpenTK.Input.Key.Up])
-                {
-                    State |= 0x2000;
-                }
-
-                if (Keyboard[OpenTK.Input.Key.Down])
-                {
-                    State |= 0x8000;
-                }
-
-                if (Keyboard[OpenTK.Input.Key.Left])
-                {
-                    State |= 0x1000;
-                }
-
-                if (Keyboard[OpenTK.Input.Key.Right])
-                {
-                    State |= 0x4000;
-                }
-
-                if (Keyboard[OpenTK.Input.Key.A])
-                {
-                    State |= 0x1;
-                }
-
-                if (Keyboard[OpenTK.Input.Key.S])
-                {
-                    State |= 0x2;
-                }
-
-                if (Keyboard[OpenTK.Input.Key.Z])
-                {
-                    State |= 0x4;
-                }
-
-                if (Keyboard[OpenTK.Input.Key.X])
-                {
-                    State |= 0x8;
-                }
-
-                if (Keyboard[OpenTK.Input.Key.Enter])
-                {
-                    State |= 0x400;
-                }
-
-                if (Keyboard[OpenTK.Input.Key.Tab])
-                {
-                    State |= 0x800;
-                }
-
-                *((int*)(Ptr + 0xae38)) = (int)State;
-            }
+                DX = LeftJoystickDX,
+                DY = LeftJoystickDY
+            };
 
-            if (Keyboard[OpenTK.Input.Key.Escape])
+            RightJoystick = new JoystickPosition
             {
-                this.Exit();
-            }
+                DX = RightJoystickDX,
+                DY = RightJoystickDY
+            };
+
+            //We just need one pair of JoyCon because it's emulate by the keyboard.
+            Ns.Hid.SendControllerButtons(HidControllerID.CONTROLLER_HANDHELD, HidControllerLayouts.Main, CurrentButton, LeftJoystick, RightJoystick);
         }
 
         protected override void OnRenderFrame(FrameEventArgs e)