unit sys_kernel;

{$mode ObjFPC}{$H+}

interface

Uses
 SysUtils,
 Windows;

{$I sce_errno.inc}
{$I errno.inc}

var
 SDK_VERSION:DWORD=0;

type
 SceKernelModule=Integer;

 PSceKernelLoadModuleOpt=^SceKernelLoadModuleOpt;
 SceKernelLoadModuleOpt=packed record
  size:size_t;
 end;

 atexit_func=function(param:Pointer):Integer;SysV_ABI_CDecl;
 TKernelAtexitFuncCount=function(handle:Integer):Integer;SysV_ABI_CDecl;
 TKernelAtexitReportFunc=procedure(handle:Integer);

function  px2sce(e:Integer):Integer;
function  sce2px(e:Integer):Integer;

function  _set_errno(r:Integer):Integer;
function  _set_sce_errno(r:Integer):Integer;
function  _error:Pointer;

function  ntf2px(n:Integer):Integer;

function  SwFreeMem(p:pointer):ptruint;
function  SwAllocMem(Size:ptruint):pointer;

Procedure SwYieldExecution; inline;
function  SwDelayExecution(Alertable:Boolean;DelayInterval:PQWORD):DWORD;

function  SwWaitForSingleObject(
           ObjectHandle:THandle;
           TimeOut:PQWORD;
           Alertable:LONGBOOL):DWORD;

function  SwWaitFor(Handle:THandle;pTimeout:PQWORD):Integer; //pTimeout in ns

function  SwGetFileAttributes(Const lpFileName:RawByteString;lpFileInformation:LPVOID):DWORD;
function  SwGetFileType(hFile:HANDLE):DWORD;
function  SwGetFileInformationByHandle(hFile:HANDLE;lpFileInformation:LPBY_HANDLE_FILE_INFORMATION):DWORD;
Function  SwCreateDir(Const NewDir:RawByteString):DWORD;

Function  safe_move(const src;var dst;count:QWORD):QWORD;
procedure safe_move_ptr(const src;var dst);
function  safe_test(var src:DWORD;value:DWORD):Boolean;
function  safe_str(P:PChar):shortstring;

function  GetFileSizeEx(hFile:HANDLE;
                        lpFileSize:PLARGE_INTEGER):BOOL; stdcall; external 'kernel32';

function  MapViewOfFileEx(hFileMappingObject:HANDLE;
                          dwDesiredAccess:DWORD;
                          dwFileOffsetHigh:DWORD;
                          dwFileOffsetLow:DWORD;
                          dwNumberOfBytesToMap:SIZE_T;
                          lpBaseAddress:LPVOID):LPVOID; stdcall; external 'kernel32' name 'MapViewOfFileEx';

function  SetFilePointerEx(hFile:HANDLE;
                           liDistanceToMove:LARGE_INTEGER;
                           lpNewFilePointer:PLARGE_INTEGER;
                           dwMoveMethod:DWORD):BOOL; external 'kernel32';

const
 BCRYPT_USE_SYSTEM_PREFERRED_RNG=2;

function  BCryptGenRandom(hAlgorithm:Pointer;
                          pbBuffer:PByte;
                          cbBuffer:DWORD;
                          dwFlags:DWORD):DWORD; stdcall; external 'Bcrypt';

const
 HW_PROFILE_GUIDLEN=39;
 MAX_PROFILE_LEN   =80;

type
 P_HW_PROFILE_INFOA=^HW_PROFILE_INFOA;
 HW_PROFILE_INFOA=packed record
  dwDockInfo:DWORD;
  szHwProfileGuid:array[0..HW_PROFILE_GUIDLEN-1] of Char;
  szHwProfileName:array[0..MAX_PROFILE_LEN   -1] of Char;
 end;

function GetCurrentHwProfileA(lpHwProfileInfo:P_HW_PROFILE_INFOA):Boolean; stdcall; external 'advapi32';

type
 PPROCESS_MEMORY_COUNTERS = ^_PROCESS_MEMORY_COUNTERS;
 _PROCESS_MEMORY_COUNTERS = record
   cb                        :DWORD;
   PageFaultCount            :DWORD;
   PeakWorkingSetSize        :SIZE_T;
   WorkingSetSize            :SIZE_T;
   QuotaPeakPagedPoolUsage   :SIZE_T;
   QuotaPagedPoolUsage       :SIZE_T;
   QuotaPeakNonPagedPoolUsage:SIZE_T;
   QuotaNonPagedPoolUsage    :SIZE_T;
   PagefileUsage             :SIZE_T;
   PeakPagefileUsage         :SIZE_T;
 end;

function  GetProcessMemoryInfo(hProcess:HANDLE;
                               ppsmemCounters:PPROCESS_MEMORY_COUNTERS;
                               cb:DWORD):BOOL; external 'psapi.dll';

type
 PIO_COUNTERS=^_IO_COUNTERS;
 _IO_COUNTERS = record
  ReadOperationCount :SIZE_T;
  WriteOperationCount:SIZE_T;
  OtherOperationCount:SIZE_T;
  ReadTransferCount  :SIZE_T;
  WriteTransferCount :SIZE_T;
  OtherTransferCount :SIZE_T;
 end;

function  GetProcessIoCounters(hProcess:HANDLE;
                               lpIoCounters:PIO_COUNTERS
                              ):BOOL; external 'kernel32';

function SysLogPrefix : string;

implementation


uses
 ntapi,
 ps4_libscefiber,
 sys_pthread,
 sys_signal,
 sys_time;

function SysLogPrefix : string;
begin
 // Add thread+fiber name and id as prefix to log messages
 Result := '';
 if _get_curthread <> nil then
 begin
  Result:='['+_get_curthread^.name + ':'+ IntToStr(_get_curthread^.ThreadId) + '] ' + GetFiberString;
 end;
end;

function px2sce(e:Integer):Integer;
begin
 if (e=0) then
  Result:=0
 else
  Result:=e-$7ffe0000;
end;

function sce2px(e:Integer):Integer;
begin
 if (e=0) then
  Result:=0
 else
  Result:=e+$7ffe0000;
end;

function _set_errno(r:Integer):Integer;
var
 t:pthread;
begin
 Result:=0;

 t:=tcb_thread;
 if (t<>nil) then t^.errno:=r;

 if (r<>0) then
 begin
  Result:=-1;
 end;
end;

function _set_sce_errno(r:Integer):Integer;
var
 t:pthread;
begin
 t:=tcb_thread;
 if (t<>nil) then t^.errno:=sce2px(r);

 Result:=r;
end;

function _error:Pointer;
var
 t:pthread;
begin
 Result:=nil;
 t:=tcb_thread;
 if (t<>nil) then Result:=@t^.errno;
end;

function ntf2px(n:Integer):Integer;
begin
 case DWORD(n) of
  STATUS_SUCCESS           :Result:=0;
  STATUS_ABANDONED         :Result:=EPERM;
  STATUS_USER_APC          :Result:=EINTR;
  STATUS_KERNEL_APC        :Result:=EINTR;
  STATUS_ALERTED           :Result:=EINTR;
  STATUS_TIMEOUT           :Result:=ETIMEDOUT;
  STATUS_PENDING           :Result:=EAGAIN;
  STATUS_ACCESS_VIOLATION  :Result:=EFAULT;
  STATUS_INVALID_HANDLE    :Result:=EBADF;
  STATUS_INVALID_PARAMETER :Result:=EINVAL;
  STATUS_END_OF_FILE       :Result:=0;
  STATUS_ACCESS_DENIED     :Result:=EBADF;
  STATUS_DISK_FULL         :Result:=ENOSPC;
  else
                            Result:=EIO;
 end;
end;

function SwFreeMem(p:pointer):ptruint;
begin
 _sig_lock;
 Result:=FreeMem(p);
 _sig_unlock;
end;

function SwAllocMem(Size:ptruint):pointer;
begin
 _sig_lock;
 Result:=AllocMem(Size);
 _sig_unlock;
end;

Procedure SwYieldExecution; inline;
begin
 _sig_lock;
 NtYieldExecution;
 _sig_unlock;
end;

function SwDelayExecution(Alertable:Boolean;DelayInterval:PQWORD):DWORD;
begin
 _sig_lock(ord(Alertable));
 Result:=NtDelayExecution(Alertable,Pointer(DelayInterval));
 _sig_unlock;
end;


function SwWaitForSingleObject(
          ObjectHandle:THandle;
          TimeOut:PQWORD;
          Alertable:LONGBOOL):DWORD;
begin
 _sig_lock(ord(Alertable));
 Result:=NtWaitForSingleObject(ObjectHandle,Alertable,Pointer(TimeOut));
 _sig_unlock;
end;

function SwWaitFor(Handle:THandle;pTimeout:PQWORD):Integer;
var
 timeout:Int64;
 passed :Int64;
 START:QWORD;
 QTIME:QWORD;
 res:DWORD;
begin
 Result:=0;

 if (pTimeout<>nil) then
 begin
  timeout:=(pTimeout^ div 100);
  SwSaveTime(START);
 end else
 begin
  timeout:=NT_INFINITE;
 end;

 repeat

  if (pTimeout<>nil) then
  begin
   if (timeout=0) then
   begin
    pTimeout^:=0;
    Result:=ETIMEDOUT;
    Break;
   end;

   SwSaveTime(QTIME);

   timeout:=-timeout;
   _sig_lock(SL_ALERTABLE);
   res:=NtWaitForSingleObject(Handle,True,@timeout);
   _sig_unlock;
   timeout:=-timeout;

   passed:=SwTimePassedUnits(QTIME);

   if (passed>=timeout) then
   begin
    timeout:=0;
   end else
   begin
    timeout:=timeout-passed;
   end;

  end else
  begin
   _sig_lock(SL_ALERTABLE);
   res:=NtWaitForSingleObject(Handle,True,@timeout);
   _sig_unlock;
  end;

  case res of
   STATUS_ALERTED,
   STATUS_USER_APC:
    begin
     //continue
    end;
   STATUS_TIMEOUT:
    begin
     if (pTimeout<>nil) then
     begin
      pTimeout^:=0;
     end;
     Result:=ETIMEDOUT;
     Break;
    end;
   STATUS_ABANDONED:
    begin
     if (pTimeout<>nil) then
     begin
      pTimeout^:=timeout*100;
     end;
     Result:=EPERM;
     Break;
    end;
   STATUS_SUCCESS:
    begin
     if (pTimeout<>nil) then
     begin
      pTimeout^:=timeout*100;
     end;
     Result:=0;
     Break;
    end;
   else
    begin
     if (pTimeout<>nil) then
     begin
      pTimeout^:=timeout*100;
     end;
     Result:=EINVAL;
     Break;
    end;
  end;

 until false;

end;

function SwGetFileAttributes(Const lpFileName:RawByteString;lpFileInformation:LPVOID):DWORD;
var
 wp:WideString;
begin
 Result:=0;
 _sig_lock;
 wp:=UTF8Decode(lpFileName);
 if not GetFileAttributesExW(PWideChar(wp),GetFileExInfoStandard,lpFileInformation) then
 begin
  Result:=GetLastError;
 end;
 _sig_unlock;
end;

function SwGetFileType(hFile:HANDLE):DWORD;
begin
 _sig_lock;
 Result:=GetFileType(hFile);
 _sig_unlock;
end;

function SwGetFileInformationByHandle(hFile:HANDLE;lpFileInformation:LPBY_HANDLE_FILE_INFORMATION):DWORD;
begin
 Result:=0;
 _sig_lock;
 if not GetFileInformationByHandle(hFile,lpFileInformation) then
 begin
  Result:=GetLastError;
 end;
 _sig_unlock;
end;

Function SwCreateDir(Const NewDir:RawByteString):DWORD;
begin
 Result:=0;
 _sig_lock;
 if not CreateDir(NewDir) then
 begin
  Result:=GetLastError;
 end;
 _sig_unlock;
end;

Function safe_move(const src;var dst;count:QWORD):QWORD;
begin
 _sig_lock;
 if not ReadProcessMemory(GetCurrentProcess,@src,@dst,count,Result) then Result:=0;
 _sig_unlock;
end;

procedure safe_move_ptr(const src;var dst);
begin
 if safe_move(src,dst,SizeOf(Pointer))<>SizeOf(Pointer) then Pointer(dst):=nil;
end;

function safe_test(var src:DWORD;value:DWORD):Boolean;
var
 t:DWORD;
begin
 Result:=False;
 t:=0;
 if (safe_move(src,t,SizeOf(DWORD))=SizeOf(DWORD)) then
 begin
  Result:=(t=value);
 end;
end;

function safe_str(P:PChar):shortstring;
var
 ch:Char;
begin
 Result:='';
 repeat
  ch:=#0;
  safe_move(P^,ch,SizeOf(Char));
  if (ch=#0) then Exit;
  Result:=Result+ch;
  if (Result[0]=#255) then Exit;
  Inc(P);
 until false;
end;

end.

