unit dev_hid;

{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}

interface

uses
 sys_conf;

procedure hid_init();

implementation

uses
 windows,
 errno,
 systm,
 vuio,
 kern_thr,
 subr_backtrace;

var
 pad_counter:Byte=0;

function generateHandle(port_type,handle_id,proc_id:Byte):DWORD;
var
 count:Byte;
begin
 count:=pad_counter+1;
 if (count=0) then
 begin
  count:=pad_counter+2;
 end;
 pad_counter:=count;
 Result:=handle_id or
         ((port_type and $f) shl 24) or
         ((proc_id and $ff) shl  8) or
                     (count shl 16);
end;

//case 0: Mouse
//case 1: Keyboard
//case 2: Status
//case 3: Controller
//case 5: Auth

type
 p_open_port_args=^t_open_port_args;
 t_open_port_args=packed record
  port_userid:Integer;
  port_type  :Integer;
  port_index :Integer;
 end;

 p_pad_state_args=^t_pad_state_args;
 t_pad_state_args=packed record
  id    :Integer; // handle/device_id
  unknow:Integer;
  state :Pointer;
 end;


 t_pad_stick_info=packed record
  deadZoneLeft :Byte;
  deadZoneRight:Byte;
 end;

 t_touch_pad_info=packed record
  pixelDensity:DWORD;
  x           :Word;
  y           :Word;
 end;

 t_pad_ext_info=packed record
  case byte of
   0:(quantityOfSelectorSwitch:Byte);
   1:(maxPhysicalWheelAngle:Integer);
   2:(data:array[0..7] of Byte);
 end;

 p_pad_device_info=^t_pad_device_info;
 t_pad_device_info=packed record //0x40
  connect_type  :Byte;
  pad1          :array[0..2] of Byte;
  device_id     :Integer;
  bluetooth_addr:QWORD;
  hid_vid       :Word;
  hid_did       :Word;
  f_0x14        :QWORD;
  f_0x1c        :Word;
  f_0x1e        :Word;
  dualshock_id  :Byte;
  pad2          :array[0..2] of Byte;
  capability1   :Integer;
  dev_class_id  :Byte;
  motion_scale  :Byte;
  capability2   :Byte;
  unknow4       :Byte;
  ext_data      :t_pad_ext_info;
  touchpad      :t_touch_pad_info;
  stick_info    :t_pad_stick_info;
  unknow5       :Word;
 end;
 {$IF sizeof(t_pad_device_info)<>64}{$STOP sizeof(t_pad_device_info)<>64}{$ENDIF}

 t_analog_stick=packed record
  x,y:Byte;
 end;

 t_analog_buttons=packed record
  l2,r2:Byte;
 end;

 t_touch=packed record
  id :Byte;
  pad:array[0..2] of Byte;
  x  :word;
  y  :word;
 end;

 t_touch_group=packed record
  reserve1:DWORD;
  touchNum:Byte;
  pad     :array[0..2] of Byte;
  touch   :array[0..1] of t_touch;
 end;

 p_pad_state=^t_pad_state;
 t_pad_state=packed record //0xA8
  timestamp    :QWORD;
  unk1         :DWORD;
  buttons      :DWORD;
  leftStick    :t_analog_stick;
  rightStick   :t_analog_stick;
  analogButtons:t_analog_buttons;
  unknow0      :Byte;
  touch_gcount :Byte;  //[0..3]
  touch_groups :array[0..3] of t_touch_group;
  unknow       :array[0..39] of Byte;
  unique_len   :Byte;
  unique_data  :array[0..6] of Byte;
 end;
 {$IF sizeof(t_pad_state)<>168}{$STOP sizeof(t_pad_state)<>168}{$ENDIF}

 p_read_state_args=^t_read_state_args;
 t_read_state_args=packed record
  handle   :Integer;
  _align1  :Integer;
  state    :p_pad_state; //num*0xA8
  num      :Integer;
  unk1     :Integer;
  device_id:PInteger;
  s_version:PInteger;
  read_type:Integer;
  unk3     :Integer;
 end;

 p_close_args=^t_close_args;
 t_close_args=packed record
  port_type:Integer;
  handle   :Integer;
 end;

 pScePadVibrationParam=^ScePadVibrationParam;
 ScePadVibrationParam=packed record
  largeMotor:Byte;
  smallMotor:Byte;
 end;

 p_vibration_args=^t_vibration_args;
 t_vibration_args=packed record
  handle :Integer;
  _align1:Integer;
  pParam :pScePadVibrationParam;
  force  :Byte;
 end;

 p_pad_lightbar_param=^t_pad_lightbar_param;
 t_pad_lightbar_param=packed record
  r,g,b  :Byte;
  tracker:Byte;
 end;

 p_set_light_bar_args=^t_set_light_bar_args;
 t_set_light_bar_args=packed record
  handle :Integer;
  _align1:Integer;
  pParam :p_pad_lightbar_param;
 end;

 p_mouse_read_data=^t_mouse_read_data;
 t_mouse_read_data=packed record //0x18
  timestamp  :QWORD;
  xAxis      :Word;
  yAxis      :Word;
  buttons    :Byte;
  INTERCEPTED:Byte;
  wheel      :Byte;
  tilt       :Byte;
  btn_type   :Integer;
  unk_bits   :Integer;
 end;
 {$IF sizeof(t_mouse_read_data)<>24}{$STOP sizeof(t_mouse_read_data)<>24}{$ENDIF}

 p_mouse_read_ioctl=^t_mouse_read_ioctl;
 t_mouse_read_ioctl=packed record
  handle   :Integer;
  _align1  :Integer;
  p_dst    :p_mouse_read_data;
  num      :Integer;
  _align2  :Integer;
  p_count  :PInteger;
 end;

const
 //ScePadButtonDataOffset
 SCE_PAD_BUTTON_L3          = $00000002;
 SCE_PAD_BUTTON_R3          = $00000004;
 SCE_PAD_BUTTON_OPTIONS     = $00000008;
 SCE_PAD_BUTTON_UP          = $00000010;
 SCE_PAD_BUTTON_RIGHT       = $00000020;
 SCE_PAD_BUTTON_DOWN        = $00000040;
 SCE_PAD_BUTTON_LEFT        = $00000080;
 SCE_PAD_BUTTON_L2          = $00000100;
 SCE_PAD_BUTTON_R2          = $00000200;
 SCE_PAD_BUTTON_L1          = $00000400;
 SCE_PAD_BUTTON_R1          = $00000800;
 SCE_PAD_BUTTON_TRIANGLE    = $00001000;
 SCE_PAD_BUTTON_CIRCLE      = $00002000;
 SCE_PAD_BUTTON_CROSS       = $00004000;
 SCE_PAD_BUTTON_SQUARE      = $00008000;
 SCE_PAD_BUTTON_TOUCH_PAD   = $00100000;
 SCE_PAD_BUTTON_INTERCEPTED = $80000000;

type
 p_motion_calib_state=^t_motion_calib_state;
 t_motion_calib_state=packed record
  field_0x00:Word;
  field_0x02:Integer;
  field_0x06:Word;
  field_0x08:Integer;
  field_0x0c:Word;
  field_0x0e:Integer;
  field_0x12:Word;
  field_0x14:Word;
  field_0x16:Integer;
  field_0x1a:Word;
  field_0x1c:Integer;
  field_0x20:Word;
  field_0x22:Word;
  field_0x24:Word;
  field_0x26:Word;
  field_0x28:Word;
 end;

 p_motion_scale_param=^t_motion_scale_param;
 t_motion_scale_param=packed record
  field0_0x0:Word;
  field1_0x2:Word;
  field2_0x4:Word;
  field3_0x6:Word;
  field4_0x8:Word;
  field4_0x10:array[0..15] of Byte;
 end;

procedure fill_device_info(var device_info:t_pad_device_info);
begin
 device_info:=Default(t_pad_device_info);

 device_info.connect_type            :=0;
 device_info.device_id               :=$60300;
 device_info.hid_vid                 :=$54c;  // Vendor ID = 054C
 device_info.hid_did                 :=$5c4;  // Device ID = 05C4 (DualShock 4 [CUH-ZCT1x])
 device_info.f_0x1c                  :=$2;
 device_info.f_0x1e                  :=$b;
 device_info.dualshock_id            :=4;
 device_info.capability1             :=$37;
 device_info.dev_class_id            :=0;
 device_info.touchpad.pixelDensity   :=$1186; // (4.486)
 device_info.touchpad.x              :=$780;  // 1920
 device_info.touchpad.y              :=$3ae;  // 942
 device_info.stick_info.deadZoneLeft :=$d;
 device_info.stick_info.deadZoneRight:=$d;
end;



function GetAsyncKeyState(vKey:longint):Boolean; inline;
begin
 Result:=(Windows.GetKeyState(vKey) and $8000)<>0;
end;

Function hidIoctl(dev:p_cdev;cmd:QWORD;data:Pointer;fflag:Integer):Integer;
var
 td:p_kthread;
 val:Integer;
 u:packed record
  case byte of
   0:(pad_device_info:t_pad_device_info);
   1:(pad_state:t_pad_state);
   2:(motion_calib_state:t_motion_calib_state);
   3:(motion_scale_param:t_motion_scale_param);
 end;
begin
 Result:=0;

 td:=curkthread;
 if (td=nil) then Exit(-1);

 case cmd of

  $800c4802: //sceHidOpenPortForUser
    begin
     with p_open_port_args(data)^ do
     begin
      if (port_userid<>-1) or   //user_id
         (port_type  <> 3) or   //port_type
         (port_index <> 0) then //port_index
      begin
       Writeln('sceHidOpenPortForUser(',port_userid,',',
                                        port_type  ,',',
                                        port_index ,')');

      end;

      //handle
      td^.td_retval[0]:=generateHandle(port_type,0,0);

      Writeln('handle=0x',HexStr(td^.td_retval[0],8));
     end;
    end;

  $80084803: //sceHidClosePortForUser
    begin
     with p_close_args(data)^ do
     begin

      Writeln('sceHidClosePortForUser(',port_type,',0x',HexStr(handle,8),')');

      Result:=0;
     end;
    end;

  $80104801: //sceHidGetDeviceInfoForUser
    begin
     with p_pad_state_args(data)^ do
     begin
      Writeln('device_id=0x',HexStr(id,8));

      fill_device_info(u.pad_device_info);

      Result:=copyout(@u.pad_device_info,state,sizeof(t_pad_device_info));
     end;
    end;

  $8010480E: //sceHidGetDeviceInfoByHandleForUser
    begin
     with p_pad_state_args(data)^ do
     begin
      //Writeln('handle=0x',HexStr(id,8));

      fill_device_info(u.pad_device_info);

      Result:=copyout(@u.pad_device_info,state,sizeof(t_pad_device_info));
     end;
    end;

  $80104823: //sceHidControllerMotionCalibReadForUser
    begin
     with p_pad_state_args(data)^ do
     begin
      Writeln('device_id=0x',HexStr(id,8));

      u.motion_calib_state:=Default(t_motion_calib_state);

      Result:=copyout(@u.motion_calib_state,state,sizeof(t_motion_calib_state));
     end;
    end;

  $80104831: //sceHidControllerGetMotionScaleParamForUser
    begin
     with p_pad_state_args(data)^ do
     begin
      Writeln('device_id=0x',HexStr(id,8));

      u.motion_scale_param:=Default(t_motion_scale_param);

      Result:=copyout(@u.motion_scale_param,state,sizeof(t_motion_scale_param));
     end;
    end;

  $8030482E: //sceHidControllerRead2ForUser
    begin
     with p_read_state_args(data)^ do
     begin

      //Writeln('handle=0x',HexStr(handle,8));

      u.pad_state:=Default(t_pad_state);

      u.pad_state.timestamp:=$101010;
      //u.pad_state.buttons:=SCE_PAD_BUTTON_UP or SCE_PAD_BUTTON_LEFT;

      u.pad_state.leftStick.x   :=$80;
      u.pad_state.leftStick.y   :=$80;

      u.pad_state.rightStick.x  :=$80;
      u.pad_state.rightStick.y  :=$80;

      //u.pad_state.leftStick.x:=1;
      //u.pad_state.leftStick.y:=2;
      //
      //u.pad_state.rightStick.x:=3;
      //u.pad_state.rightStick.y:=4;

      //u.pad_state.analogButtons.l2:=5;
      //u.pad_state.analogButtons.r2:=6;


      u.pad_state.touch_gcount:=1;
      u.pad_state.touch_groups[0].touchNum:=2;

      u.pad_state.touch_groups[0].touch[0].id:=1;
      u.pad_state.touch_groups[0].touch[0].x :=2;
      u.pad_state.touch_groups[0].touch[0].y :=3;

      u.pad_state.touch_groups[0].touch[1].id:=4;
      u.pad_state.touch_groups[0].touch[1].x :=5;
      u.pad_state.touch_groups[0].touch[1].y :=6;


      u.pad_state.unique_len :=7;
      u.pad_state.unique_data[6]:=7;

      //

      if GetAsyncKeyState(VK_W) then
       u.pad_state.leftStick.y:=0;

      if GetAsyncKeyState(VK_S) then
       u.pad_state.leftStick.y:=$FF;

      if GetAsyncKeyState(VK_A) then
       u.pad_state.leftStick.x:=0;

      if GetAsyncKeyState(VK_D) then
       u.pad_state.leftStick.x:=$FF;

      //

      if GetAsyncKeyState(VK_I) then
       u.pad_state.rightStick.y:=0;

      if GetAsyncKeyState(VK_K) then
       u.pad_state.rightStick.y:=$FF;

      if GetAsyncKeyState(VK_J) then
       u.pad_state.rightStick.x:=0;

      if GetAsyncKeyState(VK_L) then
       u.pad_state.rightStick.x:=$FF;

      if GetAsyncKeyState(VK_RETURN) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_OPTIONS;

      if GetAsyncKeyState(VK_UP) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_UP;

      if GetAsyncKeyState(VK_RIGHT) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_RIGHT;

      if GetAsyncKeyState(VK_DOWN) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_DOWN;

      if GetAsyncKeyState(VK_LEFT) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_LEFT;

      if GetAsyncKeyState(VK_NUMPAD8) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_TRIANGLE;

      if GetAsyncKeyState(VK_NUMPAD6) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_CIRCLE;

      if GetAsyncKeyState(VK_NUMPAD2) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_CROSS;

      if GetAsyncKeyState(VK_NUMPAD4) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_SQUARE;

      if GetAsyncKeyState(VK_Q) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_L1;

      if GetAsyncKeyState(VK_1) then
      begin
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_L2;
       u.pad_state.analogButtons.l2:=255;
      end;

      if GetAsyncKeyState(VK_Z) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_L3;


      if GetAsyncKeyState(VK_E) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_R1;

      if GetAsyncKeyState(VK_4) then
      begin
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_R2;
       u.pad_state.analogButtons.r2:=255;
      end;

      if GetAsyncKeyState(VK_C) then
       u.pad_state.buttons:=u.pad_state.buttons or SCE_PAD_BUTTON_R3;

      //

      Result:=copyout(@u.pad_state,state,sizeof(t_pad_state));

      val:=$60300;
      copyout(@val,device_id,4);

      val:=1;
      copyout(@val,s_version,4);

      //num * 0xa8

      //num
      td^.td_retval[0]:=1;
     end;
    end;

  $80184822: //scePadSetVibration/scePadSetVibrationForce
    begin
     //with p_vibration_args(data)^ do
    end;

  $80104821: //scePadSetLightBar/scePadSetLightBarForTracker
    begin
     //with p_set_light_bar_args(data)^ do
    end;

  $80044825: //scePadResetLightBar
    begin
     //PInteger(data)^ -> handle
    end;

  $80204819: //sceHidMouseReadForUser
    begin
     with p_mouse_read_ioctl(data)^ do
     begin
      val:=0;
      copyout(@val,p_count,4);
     end;
    end;

 else
  begin
   print_error_td('hidIoctl(0x'+HexStr(cmd,8)+')');
   Assert(False);
   Result:=EINVAL;
  end;
 end;

end;

Function hidRead(dev:p_cdev;uio:p_uio;ioflag:Integer):Integer;
begin
 Result:=0;
end;

const
 hid_cdevsw:t_cdevsw=(
  d_version     :D_VERSION;
  d_flags       :D_TRACKCLOSE;
  d_name        :'hid';
  d_read        :@hidRead;
  d_ioctl       :@hidIoctl;
 );

procedure hid_init();
begin
 make_dev(@hid_cdevsw,0,0,0,&666,'hid',[]);
end;

end.

