unit ps4_program;

{$mode objfpc}{$H+}

interface

uses
  Windows,
  Classes,
  SysUtils,
  RWLock,
  hamt,
  sys_types,
  sys_crt,
  sys_kernel,
  ps4_handles;

type
 PMODULE=^TMODULE;
 TMODULE=packed record
  attr:DWORD;
  Import:Boolean;
  strName:string[80];
 end;

 PLIBRARY=^TLIBRARY;

 T_set_proc_cb=function(lib:PLIBRARY;nid:QWORD;value:Pointer):Boolean;
 T_get_proc_cb=function(lib:PLIBRARY;nid:QWORD):Pointer;

 TElf_node=class;

 TLIBRARY=packed object
  parent:TElf_node;
  MapSymbol:THAMT;
  attr:DWORD;
  Import:Boolean;
  strName:string[80];
  Fset_proc_cb:T_set_proc_cb;
  Fget_proc_cb:T_get_proc_cb;
  function _set_proc(nid:QWORD;value:Pointer):Boolean;
  function _get_proc(nid:QWORD):Pointer;
  function set_proc(nid:QWORD;value:Pointer):Boolean;
  function get_proc(nid:QWORD):Pointer;
 end;

 TElf_node=class(TClassHandle)
  private
   pPrev,pNext:TElf_node;
  protected
   FHandle:Integer;
   FStatic:Boolean;
   FPrepared:Boolean;
   FLoadImport:Boolean;
   FInitProt:Boolean;
   FInitThread:Boolean;
   FInitCode:Boolean;
   aNeed:array of RawByteString;
   aMods:array of TMODULE;
   aLibs:array of PLIBRARY;
   procedure _set_filename(const name:RawByteString);
   procedure _add_need(const name:RawByteString);
   procedure _set_mod(id:Word;_md:TMODULE);
   procedure _set_mod_attr(u:TModuleValue);
   function  _get_mod(id:Word):PMODULE;
   procedure _set_lib(id:Word;lib:TLIBRARY);
   procedure _set_lib_attr(u:TLibraryValue);
   function  _get_lib(id:Word):PLIBRARY;
   function  _find_mod_export:Word;
   function  _find_lib_export:Word;
  public
   pFileName:RawByteString;
   property    IsStatic:Boolean read FStatic write FStatic;
   property    IsInit:Boolean read FInitCode write FInitCode;
   property    Handle:Integer read FHandle;
   property    Next:TElf_node read pNext;
   function    _add_lib(const strName:RawByteString):PLIBRARY;
   function    ModuleNameFromId(id:WORD):RawByteString;
   function    LibraryNameFromId(id:WORD):RawByteString;
   Constructor Create;
   destructor  Destroy; override;
   Procedure   Clean; virtual;
   function    Prepare:Boolean; virtual;
   Procedure   LoadSymbolImport(cbs,data:Pointer); virtual;
   Procedure   ReLoadSymbolImport(cbs,data:Pointer); virtual;
   Procedure   InitThread(is_static:QWORD); virtual;
   Procedure   FreeThread; virtual;
   Procedure   InitProt;   virtual;
   Procedure   InitCode;   virtual;
   function    module_start(argc:size_t;argp,param:PPointer):Integer; virtual;
   function    GetCodeFrame:TMemChunk; virtual;
   function    GetEntry:Pointer; virtual;
   function    GetdInit:Pointer; virtual;
   function    GetdFini:Pointer; virtual;
   Function    GetModuleInfo:SceKernelModuleInfo; virtual;
   Function    GetModuleInfoEx:SceKernelModuleInfoEx; virtual;
   Function    get_proc(nid:QWORD):Pointer;
   Function    get_proc_by_name(const name:RawByteString):Pointer;
 end;

 TOnElfLoadCb=function(Const name:RawByteString):TElf_node;

 Phamt64locked=^Thamt64locked;
 Thamt64locked=object
  lock:TRWLock;
  hamt:TSTUB_HAMT64;
  Procedure Init;
  Procedure LockRd;
  Procedure LockWr;
  Procedure Unlock;
 end;

 Thamt64locked_proc=object(Thamt64locked)
  function _set_proc(nid:QWORD;value:Pointer):Boolean;
  function _get_proc(nid:QWORD):Pointer;
 end;

 TElfNodeList=object(Thamt64locked)
  pHead,pTail:TElf_node;
  procedure Push_head(Node:TElf_node);
  procedure Push_tail(Node:TElf_node);
  function  Pop_head:TElf_node;
  function  Pop_tail:TElf_node;
  procedure InsertAfter(node,new:TElf_node);
  procedure InsertBefore(node,new:TElf_node);
  procedure Remove(node:TElf_node);
 end;

 Tps4_program=object
  public
   resolve_cb:Pointer;
   reload_cb:Pointer;
   prog:TElf_node;
   app0_file:RawByteString;
   app0_path:RawByteString;
   app1_path:RawByteString;
   save_path:RawByteString;
  private
   pre_load:Thamt64locked_proc;
   fin_load:Thamt64locked_proc;

   files:TElfNodeList;
   mods:Thamt64locked;
   libs:Thamt64locked;
   elfs:TIntegerHandles;
   function  RegistredFile(node:TElf_node):Boolean;
   Procedure RegistredMod(node:TElf_node;const strName:RawByteString);
  public
   Procedure LockRd;
   Procedure LockWr;
   Procedure Unlock;
   function  FirstFile:TElf_node;
   function  AcqureFileByName(const strName:RawByteString):TElf_node;
   procedure PopupFile(node:TElf_node);
   Procedure SetLib(lib:PLIBRARY);
   function  GetLib(const strName:RawByteString):PLIBRARY;
   function  RegistredElf(node:TElf_node):Boolean;
   Procedure RegistredPreLoad(const strName:RawByteString;cb:TOnElfLoadCb);
   Procedure RegistredFinLoad(const strName:RawByteString;cb:TOnElfLoadCb);
   function  Loader(Const name:RawByteString):TElf_node;
   Procedure ResolveDepended(node:TElf_node);
   Procedure LoadSymbolImport(data:Pointer);
   Procedure ReLoadSymbolImport(data:Pointer);
   Procedure InitProt;
   Procedure InitCode;
   Procedure InitThread(is_static:QWORD);
   Procedure FreeThread;
   function  AcqureFileByCodeAdr(Adr:Pointer):TElf_node;
   function  AcqureFileByHandle(handle:Integer):TElf_node;
 end;

var
 ps4_app:Tps4_program;

Procedure DumpExceptionCallStack(E: Exception);

function  GetSceProcParam:Pointer;
function  GetSceUserMainThreadName:PChar;
function  GetSceUserMainThreadPriority:PDWORD;
function  GetSceUserMainThreadStackSize:PDWORD;
function  GetSceLibcParam:Pointer;
function  GetSceKernelMemParam:Pointer;
function  GetSceKernelFlexibleMemorySize:PQWORD;

Function  get_dev_progname:RawByteString;

implementation

uses
 ps4_elf,ps4_elf_tls;

Procedure DumpExceptionCallStack(E: Exception);
var
  I: Integer;
  Frames: PPointer;
  Report: string;
begin
  Report := '';
  if E <> nil then
  begin
    Report := Report + 'Exception class: ' + E.ClassName + LineEnding +
    'Message: ' + E.Message + LineEnding;
  end;
  Report := Report + BackTraceStrFunc(ExceptAddr);
  Frames := ExceptFrames;
  for I := 0 to ExceptFrameCount - 1 do
    Report := Report + LineEnding + BackTraceStrFunc(Frames[I]);
  Writeln(StdErr,Report);
end;

//--

procedure TElfNodeList.Push_head(Node:TElf_node);
begin
 if (pHead=nil) then
 begin
  pTail:=node;
  node.pNext:=nil;
 end else
 begin
  pHead.pPrev:=node;
  node.pNext:=pHead;
 end;
 node.pPrev:=nil;
 pHead:=node;
end;

procedure TElfNodeList.Push_tail(Node:TElf_node);
begin
 if (pTail=nil) then
 begin
  pHead:=node;
  node.pPrev:=nil;
 end else
 begin
  pTail.pNext:=node;
  node.pPrev:=pTail;
 end;
 node.pNext:=nil;
 pTail:=node;
end;

function TElfNodeList.Pop_head:TElf_node;
begin
 if (pHead=nil) then
 begin
  Result:=nil;
 end else
 begin
  Result:=pHead;
  pHead:=pHead.pNext;
  if (pHead=nil) then
  begin
   pTail:=nil;
  end else
  begin
   pHead.pPrev:=nil;
  end;
  Result.pPrev:=nil;
  Result.pNext:=nil;
 end;
end;

function TElfNodeList.Pop_tail:TElf_node;
begin
 if (pTail=nil) then
 begin
  Result:=nil;
 end else
 begin
  Result:=pTail;
  pTail:=pTail.pPrev;
  if (pTail=nil) then
  begin
   pHead:=nil;
  end else
  begin
   pTail.pNext:=nil;
  end;
  Result.pPrev:=nil;
  Result.pNext:=nil;
 end;
end;

procedure TElfNodeList.InsertAfter(node,new:TElf_node);
begin
 new.pPrev:=node;
 if (node.pNext=nil) then
 begin
  new.pNext:=nil;
  pTail:=new;
 end else
 begin
  new.pNext:=node.pNext;
  node.pNext.pPrev:=new;
 end;
 node.pNext:=new;
end;

procedure TElfNodeList.InsertBefore(node,new:TElf_node);
begin
 new.pNext:=node;
 if (node.pPrev=nil) then
 begin
  new.pPrev:=nil;
  pHead:=new;
 end else
 begin
  new.pPrev:=node.pPrev;
  node.pPrev.pNext:=new;
 end;
 node.pPrev:=new;
end;

procedure TElfNodeList.Remove(node:TElf_node);
begin
 if (node.pPrev=nil) then
 begin
  if (pHead=node) then
  begin
   pHead:=node.pNext;
  end;
 end else
 begin
  node.pPrev.pNext:=node.pNext;
 end;
 if (node.pNext=nil) then
 begin
  if (pTail=node) then
  begin
   pTail:=node.pPrev;
  end;
 end else
 begin
  node.pNext.pPrev:=node.pPrev;
 end;
end;

//

procedure TElf_node._set_filename(const name:RawByteString);
var
 i,L:SizeInt;
begin
 i:=Length(name);
 While (i>1) do
 begin
  if (name[i] in AllowDirectorySeparators) then
  begin
   Inc(i);
   L:=Length(name)-i+1;
   pFileName:=Copy(name,i,L);
   Exit;
  end;
  Dec(i);
 end;
 pFileName:=name;
end;

procedure TElf_node._add_need(const name:RawByteString);
var
 i:SizeInt;
begin
 i:=Length(aNeed);
 SetLength(aNeed,i+1);
 aNeed[i]:=name;
end;

procedure TElf_node._set_mod(id:Word;_md:TMODULE);
var
 i:SizeInt;
begin
 i:=Length(aMods);
 if (i<=id) then
 begin
  i:=id+1;
  SetLength(aMods,i);
 end;
 aMods[id]:=_md;
end;

procedure TElf_node._set_mod_attr(u:TModuleValue);
var
 i:SizeInt;
begin
 i:=Length(aMods);
 if (i<=u.id) then
 begin
  i:=u.id+1;
  SetLength(aMods,i);
 end;
 aMods[u.id].attr:=u.name_offset;
end;

function TElf_node._get_mod(id:Word):PMODULE;
begin
 Result:=nil;
 if (Length(aMods)>id) then
 begin
  Result:=@aMods[id];
 end;
end;

function TElf_node._find_mod_export:Word;
var
 i:Word;
begin
 Result:=0;
 if Length(aMods)>0 then
 For i:=0 to High(aMods) do
  if not aMods[i].Import then
  begin
   Exit(i);
  end;
end;

procedure TElf_node._set_lib(id:Word;lib:TLIBRARY);
var
 p,i:SizeInt;
 plib:PLIBRARY;
begin
 i:=Length(aLibs);
 if (i<=id) then
 begin
  i:=id+1;
  p:=Length(aLibs);
  SetLength(aLibs,i);
  for p:=p to i-1 do
  begin
   aLibs[p]:=nil;
  end;
 end;
 plib:=aLibs[id];
 if (plib=nil) then plib:=AllocMem(SizeOf(TLIBRARY));
 plib^:=lib;
 plib^.parent:=Self;
 aLibs[id]:=plib;
end;

procedure TElf_node._set_lib_attr(u:TLibraryValue);
var
 p,i:SizeInt;
 plib:PLIBRARY;
begin
 i:=Length(aLibs);
 if (i<=u.id) then
 begin
  i:=u.id+1;
  p:=Length(aLibs);
  SetLength(aLibs,i);
  for p:=p to i-1 do
  begin
   aLibs[p]:=nil;
  end;
 end;
 plib:=aLibs[u.id];
 if (plib=nil) then plib:=AllocMem(SizeOf(TLIBRARY));
 plib^.attr:=u.name_offset;
 plib^.parent:=Self;
 aLibs[u.id]:=plib;
end;

function TElf_node._get_lib(id:Word):PLIBRARY;
begin
 Result:=nil;
 if (Length(aLibs)>id) then
 begin
  Result:=aLibs[id];
 end;
end;

function TElf_node._add_lib(const strName:RawByteString):PLIBRARY;
var
 i:SizeInt;
begin
 i:=Length(aLibs);
 SetLength(aLibs,i+1);
 Result:=AllocMem(SizeOf(TLIBRARY));
 Result^.parent:=Self;
 Result^.strName:=strName;
 aLibs[i]:=Result;
end;

function TElf_node._find_lib_export:Word;
var
 i:Word;
begin
 Result:=0;
 if Length(aLibs)>0 then
 For i:=0 to High(aLibs) do
 if (aLibs[i]<>nil) then
  if not aLibs[i]^.Import then
  begin
   Exit(i);
  end;
end;

function TElf_node.ModuleNameFromId(id:WORD):RawByteString;
var
 _md:PMODULE;
begin
 Result:='';
 if (Self=nil) then Exit;
 _md:=_get_mod(id);
 if (_md<>nil) then Result:=_md^.strName;
end;

function TElf_node.LibraryNameFromId(id:WORD):RawByteString;
var
 lib:PLIBRARY;
begin
 Result:='';
 if (Self=nil) then Exit;
 lib:=_get_lib(id);
 if (lib<>nil) then Result:=lib^.strName;
end;

procedure _free_map_cb(data,userdata:Pointer);
begin
 FreeMem(data);
end;

Procedure TElf_node.Clean;
var
 i:SizeInt;
 lib:PLIBRARY;
begin
 if (Self=nil) then Exit;
 if Length(aLibs)<>0 then
 begin
  For i:=0 to Length(aLibs)-1 do
  begin
   lib:=aLibs[i];
   if (lib<>nil) then
   begin
    HAMT_destroy64(lib^.MapSymbol,@_free_map_cb,nil);
    lib^.strName:='';
    FreeMem(lib);
   end;
  end;
 end;
 pFileName:='';
 SetLength(aNeed,0);
 SetLength(aMods,0);
 SetLength(aLibs,0);
end;

Constructor TElf_node.Create;
begin
 FStatic:=True;
end;

destructor TElf_node.Destroy;
begin
 Clean;
 inherited;
end;

function TElf_node.Prepare:Boolean;
begin
 Result:=True;
 FPrepared:=True;;
end;

Procedure TElf_node.LoadSymbolImport(cbs,data:Pointer);
begin
 FLoadImport:=True;
end;

Procedure TElf_node.ReLoadSymbolImport(cbs,data:Pointer);
begin

end;

Procedure TElf_node.InitThread(is_static:QWORD);
begin

end;

Procedure TElf_node.FreeThread;
begin

end;

Procedure TElf_node.InitProt;
begin
 FInitProt:=True;
end;

Procedure TElf_node.InitCode;
begin
 FInitCode:=True;
end;

function TElf_node.module_start(argc:size_t;argp,param:PPointer):Integer;
begin
 Result:=0;
end;

function TElf_node.GetCodeFrame:TMemChunk;
begin
 Result:=Default(TMemChunk);
end;

function TElf_node.GetEntry:Pointer;
begin
 Result:=nil;
end;

function TElf_node.GetdInit:Pointer;
begin
 Result:=nil;
end;

function TElf_node.GetdFini:Pointer;
begin
 Result:=nil;
end;

Function TElf_node.GetModuleInfo:SceKernelModuleInfo;
begin
 Result:=Default(SceKernelModuleInfo);
 Result.size:=SizeOf(SceKernelModuleInfo);

 MoveChar0(PChar(pFileName)^,Result.name,SCE_DBG_MAX_NAME_LENGTH);
end;

Function TElf_node.GetModuleInfoEx:SceKernelModuleInfoEx;
begin
 Result:=Default(SceKernelModuleInfoEx);
 Result.st_size:=SizeOf(SceKernelModuleInfoEx);

 MoveChar0(PChar(pFileName)^,Result.name,SCE_DBG_MAX_NAME_LENGTH);

 Result.id:=FHandle;
 Result.ref_count:=1;
end;

Function TElf_node.get_proc(nid:QWORD):Pointer;
var
 i:Integer;
begin
 Result:=nil;
 if Length(aLibs)<>0 then
 begin
  For i:=0 to Length(aLibs)-1 do
  if (aLibs[i]<>nil) then
  if (not aLibs[i]^.Import) then
  begin
   Result:=aLibs[i]^.get_proc(nid);
   if (Result<>nil) then Exit;
  end;
 end;
end;

Function TElf_node.get_proc_by_name(const name:RawByteString):Pointer;
begin
 Result:=get_proc(ps4_nid_hash(name));
end;

function TLIBRARY._set_proc(nid:QWORD;value:Pointer):Boolean;
var
 data:PPointer;
 PP:PPointer;
begin
 if (@Self=nil) then Exit(False);
 if (MapSymbol=nil) then MapSymbol:=HAMT_create64;

 data:=nil;
 PP:=HAMT_search64(MapSymbol,nid);
 if (PP<>nil) then data:=PP^;

 if (data=nil) then
 begin
  data:=GetMem(SizeOf(Pointer)*2);
  data[0]:=value;
  data[1]:=Pointer(nid);
  PP:=HAMT_insert64(MapSymbol,nid,data);
  Assert(PP<>nil);
  Result:=(PP^=data);
  if not Result then
  begin
   FreeMem(data);
  end;
 end else
 begin
  data[0]:=value;
 end;

end;

function TLIBRARY._get_proc(nid:QWORD):Pointer;
var
 data:PPointer;
 PP:PPointer;
begin
 Result:=nil;
 if (@Self=nil) then Exit;
 data:=nil;
 PP:=HAMT_search64(MapSymbol,nid);
 if (PP<>nil) then data:=PP^;
 if (data<>nil) then Result:=data^;
end;

function TLIBRARY.set_proc(nid:QWORD;value:Pointer):Boolean;
begin
 if (@Self=nil) then Exit(False);
 if (Fset_proc_cb<>nil) then
  Result:=Fset_proc_cb(@self,nid,value)
 else
  Result:=_set_proc(nid,value);
end;

function TLIBRARY.get_proc(nid:QWORD):Pointer;
begin
 if (@Self=nil) then Exit(nil);
 if (Fget_proc_cb<>nil) then
  Result:=Fget_proc_cb(@self,nid)
 else
  Result:=_get_proc(nid);
end;

function Tps4_program.RegistredFile(node:TElf_node):Boolean;
var
 nid:QWORD;
 PP:PPointer;
begin
 Result:=True;

 files.LockWr;
 files.Remove(node);
 files.Push_tail(node);

 nid:=ps4_nid_hash(node.pFileName);
 PP:=HAMT_insert64(@files.hamt,nid,Pointer(node));
 Assert(PP<>nil);

 if (PP^<>Pointer(node)) then
 begin
  Writeln(StdWrn,'[Warn]:',node.pFileName,' file is registred');
  Result:=False;
 end;

 files.Unlock;
end;

Procedure Tps4_program.LockRd;
begin
 files.LockRd;
end;

Procedure Tps4_program.LockWr;
begin
 files.LockWr;
end;

Procedure Tps4_program.Unlock;
begin
 files.Unlock;
end;

function Tps4_program.FirstFile:TElf_node;
begin
 Result:=files.pHead;
end;

function Tps4_program.AcqureFileByName(const strName:RawByteString):TElf_node;
var
 nid:QWORD;
 PP:PPointer;
begin
 Result:=nil;
 nid:=ps4_nid_hash(strName);

 files.LockRd;

 PP:=HAMT_search64(@files.hamt,nid);
 if (PP<>nil) then Result:=TElf_node(PP^);

 if (Result<>nil) then Result.Acqure;

 files.Unlock;
end;

procedure Tps4_program.PopupFile(node:TElf_node);
begin
 if (node=nil) then Exit;
 files.LockRd;
 files.Remove(node);
 files.Push_head(node);
 files.Unlock;
end;

Procedure Tps4_program.RegistredMod(node:TElf_node;const strName:RawByteString);
var
 nid:QWORD;
 PP:PPointer;
begin
 nid:=ps4_nid_hash(strName);
 PP:=HAMT_insert64(@mods.hamt,nid,Pointer(node));
 Assert(PP<>nil);
 if (PP^<>Pointer(node)) then Writeln(StdWrn,'[Warn]:',strName,' module is registred');
end;

Procedure Tps4_program.SetLib(lib:PLIBRARY);
var
 nid:QWORD;
 PP:PPointer;
begin
 if (lib=nil) then Exit;
 if lib^.Import then Exit;
 //Writeln('Regs, ',lib^.strName);
 nid:=ps4_nid_hash(lib^.strName);
 PP:=HAMT_insert64(@libs.hamt,nid,Pointer(lib));
 Assert(PP<>nil);
 if (PP^<>Pointer(lib)) then Writeln(StdWrn,'[Warn]:',lib^.strName,' lib is registred');
end;

function Tps4_program.GetLib(const strName:RawByteString):PLIBRARY;
var
 nid:QWORD;
 PP:PPointer;
begin
 Result:=nil;
 nid:=ps4_nid_hash(strName);
 libs.LockRd;
 PP:=HAMT_search64(@libs.hamt,nid);
 if (PP<>nil) then Result:=PP^;
 libs.Unlock;
end;

function Tps4_program.RegistredElf(node:TElf_node):Boolean;
var
 FHandle:Integer;
 i:SizeInt;
begin
 if (node=nil) then Exit(False);
 FHandle:=0;

 if not elfs.New(node,FHandle) then
  raise Exception.Create('Error alloc handle');
 node.FHandle:=FHandle;

 Result:=RegistredFile(node);
 if not Result then Exit;

 if Length(node.aMods)<>0 then
 begin
  mods.LockWr;
  For i:=0 to Length(node.aMods)-1 do
  with node.aMods[i] do
   if not Import then
    RegistredMod(node,strName);
  mods.Unlock;
 end;

 if Length(node.aLibs)<>0 then
 begin
  libs.LockWr;
  For i:=0 to Length(node.aLibs)-1 do
   SetLib(node.aLibs[i]);
  libs.Unlock;
 end;

 node.Release;
end;

function Thamt64locked_proc._set_proc(nid:QWORD;value:Pointer):Boolean;
var
 data:PPointer;
 PP:PPointer;
begin
 data:=GetMem(SizeOf(Pointer));
 data^:=value;
 PP:=HAMT_insert64(@hamt,nid,data);
 Assert(PP<>nil);
 Result:=(PP^=data);
 if not Result then
 begin
  FreeMem(data);
 end;
end;

function Thamt64locked_proc._get_proc(nid:QWORD):Pointer;
var
 data:PPointer;
 PP:PPointer;
begin
 Result:=nil;
 data:=nil;
 PP:=HAMT_search64(@hamt,nid);
 if (PP<>nil) then data:=PP^;
 if (data<>nil) then Result:=data^;
end;

Procedure Tps4_program.RegistredPreLoad(const strName:RawByteString;cb:TOnElfLoadCb);
var
 nid:QWORD;
begin
 if (cb=nil) then Exit;
 nid:=ps4_nid_hash(strName);
 pre_load.LockWr;
 if not pre_load._set_proc(nid,Pointer(cb)) then
 begin
  Writeln(StdWrn,'[Warn]:',strName,' is registred')
 end;
 pre_load.Unlock;
end;

Procedure Tps4_program.RegistredFinLoad(const strName:RawByteString;cb:TOnElfLoadCb);
var
 nid:QWORD;
begin
 if (cb=nil) then Exit;
 nid:=ps4_nid_hash(strName);
 fin_load.LockWr;
 if not fin_load._set_proc(nid,Pointer(cb)) then
 begin
  Writeln(StdWrn,'[Warn]:',strName,' is registred')
 end;
 fin_load.Unlock;
end;

type
 TNodeStack=object
  type
   PNode=^TNode;
   TNode=record
    pNext:PNode;
    S:RawByteString;
   end;
  var
   pHead:PNode;
  procedure Push(Const S:RawByteString);
  function  Pop:RawByteString;
 end;

procedure TNodeStack.Push(Const S:RawByteString);
var
 Node:PNode;
begin
 Node:=AllocMem(SizeOf(TNode));
 Node^.S:=S;
 if (pHead=nil) then
 begin
  node^.pNext:=nil;
 end else
 begin
  node^.pNext:=pHead;
 end;
 pHead:=node;
end;

function TNodeStack.Pop:RawByteString;
var
 Node:PNode;
begin
 Result:='';
 Node:=pHead;
 if (pHead<>nil) then
 begin
  pHead:=pHead^.pNext;
 end;
 if (Node<>nil) then
 begin
  Node^.pNext:=nil;
 end;
 if (Node<>nil) then
 begin
  Result:=Node^.S;
  FreeMem(Node);
 end;
end;

Function sce_load_filter(Const name:RawByteString):Boolean;
const
 c_libc='libc';
 c_libSce='libSce';
begin
 Result:=(Copy(name,1,Length(c_libc))=c_libc) or
         (Copy(name,1,Length(c_libSce))=c_libSce);
end;

function TryLoadElf(Const path,name:RawByteString):TElf_node;
var
 s:RawByteString;
begin
 //bulid path
 s:=IncludeTrailingPathDelimiter(path)+'sce_module'+DirectorySeparator+name;
 Result:=LoadPs4ElfFromFile(s); //try defaut .prx
 if (Result=nil) then
 begin
  s:=ChangeFileExt(s,'.sprx');
  Result:=LoadPs4ElfFromFile(s); //try .sprx
 end;
end;

function Tps4_program.Loader(Const name:RawByteString):TElf_node;
var
 nid:QWORD;
 PP:PPointer;
 cb:TOnElfLoadCb;
begin
 Result:=nil;
 nid:=ps4_nid_hash(name);
 files.LockRd;
 PP:=HAMT_search64(@files.hamt,nid);
 if (PP<>nil) then Result:=TElf_node(PP^);
 files.Unlock;
 if (Result<>nil) then Exit; //is loaded

 cb:=nil;
 pre_load.LockRd;
 cb:=TOnElfLoadCb(pre_load._get_proc(nid));
 pre_load.Unlock;
 if (cb<>nil) then Result:=cb(name);
 if (Result<>nil) then //is pre load
 begin
  Result.Prepare;
  ps4_app.RegistredElf(Result);
  Exit;
 end;

 if sce_load_filter(name) then
 begin
  Result:=TryLoadElf(app1_path,name);
  if (Result<>nil) then //is default load app1_path\sce_module
  begin
   Result.Prepare;
   ps4_app.RegistredElf(Result);
   Exit;
  end else
  begin
   Result:=TryLoadElf(app0_path,name);
   if (Result<>nil) then //is default load app0_path\sce_module
   begin
    Result.Prepare;
    ps4_app.RegistredElf(Result);
    Exit;
   end;
  end;
 end;

 //

 Result:=TryLoadElf(GetCurrentDir,name);
 if (Result<>nil) then //is default load current_dir\sce_module
 begin
  Result.Prepare;
  ps4_app.RegistredElf(Result);
  Exit;
 end;

 cb:=nil;
 fin_load.LockRd;
 cb:=TOnElfLoadCb(fin_load._get_proc(nid));
 fin_load.Unlock;
 if (cb<>nil) then Result:=cb(name);
 if (Result<>nil) then //is fin load
 begin
  Result.Prepare;
  ps4_app.RegistredElf(Result);
  Exit;
 end;
end;

Procedure Tps4_program.ResolveDepended(node:TElf_node);
var
 fstack:TNodeStack;
 s:RawByteString;

 Procedure _to_stack;
 var
  i:SizeInt;
 begin
  if Length(node.aNeed)<>0 then
   For i:=0 to Length(node.aNeed)-1 do
   begin
    fstack.Push(node.aNeed[i]);
   end;
 end;

begin
 if (node=nil) then Exit;
 fstack:=Default(TNodeStack);
 _to_stack;
 While (fstack.pHead<>nil) do
 begin
  S:=fstack.Pop;
  node:=Loader(S);
  if (node=nil) then
  begin
   Writeln(StdWrn,'[Warn]:',' file ',S,' not loaded!');
  end else
  begin
   PopupFile(node);
   _to_stack;
  end;
 end;
end;

Procedure Tps4_program.LoadSymbolImport(data:Pointer);
var
 Node:TElf_node;
begin
 files.LockRd;
 Node:=files.pHead;
 While (Node<>nil) do
 begin
  Node.LoadSymbolImport(resolve_cb,data);
  Node:=Node.pNext;
 end;
 files.Unlock;
end;

Procedure Tps4_program.ReLoadSymbolImport(data:Pointer);
var
 Node:TElf_node;
begin
 files.LockRd;
 Node:=files.pHead;
 While (Node<>nil) do
 begin
  Node.ReLoadSymbolImport(reload_cb,data);
  Node:=Node.pNext;
 end;
 files.Unlock;
end;

Procedure Tps4_program.InitProt;
var
 Node:TElf_node;
begin
 files.LockRd;
 Node:=files.pHead;
 While (Node<>nil) do
 begin
  Node.InitProt;
  Node:=Node.pNext;
 end;
 files.Unlock;
end;

Procedure Tps4_program.InitCode;
var
 Node:TElf_node;
begin
 Assert(ps4_app.prog<>nil);
 files.LockRd;

 Node:=files.pHead;
 While (Node<>nil) do
 begin
  Node.InitCode;
  Node:=Node.pNext;
 end;

 files.Unlock;
end;

Procedure Tps4_program.InitThread(is_static:QWORD);
var
 Node:TElf_node;
begin
 files.LockRd;
 Node:=files.pHead;
 While (Node<>nil) do
 begin
  Node.InitThread(is_static);
  Node:=Node.pNext;
 end;
 files.Unlock;
end;

Procedure Tps4_program.FreeThread;
var
 Node:TElf_node;
begin
 files.LockRd;
 Node:=files.pHead;
 While (Node<>nil) do
 begin
  Node.FreeThread;
  Node:=Node.pNext;
 end;
 files.Unlock;
 _free_tls_tcb_all;
end;

function Tps4_program.AcqureFileByCodeAdr(Adr:Pointer):TElf_node;
var
 tmp:TElfNodeList;
 Node:TElf_node;
 Mem:TMemChunk;
begin
 Result:=nil;
 tmp:=Default(TElfNodeList);
 if safe_move(files,tmp,SizeOf(TElfNodeList))<>SizeOf(TElfNodeList) then Exit;
 files.LockRd;

 Node:=tmp.pHead;
 While (Node<>nil) do
 begin
  Mem:=Node.GetCodeFrame;
  if (Mem.pAddr<>nil) and (Mem.nSize<>0) then
  begin
   if (Adr>=Mem.pAddr) and (Adr<(Mem.pAddr+Mem.nSize)) then
   begin
    Result:=Node;
    Result.Acqure;

    files.Unlock;
    Exit;
   end;
  end;
  safe_move_ptr(Node.pNext,Node);
 end;

 files.Unlock;
end;

function Tps4_program.AcqureFileByHandle(handle:Integer):TElf_node;
begin
 Result:=TElf_node(elfs.Acqure(handle));
end;

Procedure Thamt64locked.Init;
begin
 FillChar(Self,SizeOf(Self),0);
 rwlock_init(lock);
end;

Procedure Thamt64locked.LockRd;
begin
 rwlock_rdlock(lock);
end;

Procedure Thamt64locked.LockWr;
begin
 rwlock_wrlock(lock);
end;

Procedure Thamt64locked.Unlock;
begin
 rwlock_unlock(lock);
end;

function GetSceProcParam:Pointer;
var
 elf:Telf_file;
begin
 Result:=nil;
 elf:=Telf_file(ps4_program.ps4_app.prog);
 if (elf=nil) then Exit;
 if (elf.pProcParam=0) then Exit;
 Result:=elf.mMap.pAddr+elf.pProcParam;
end;

function GetSceUserMainThreadName:PChar;
var
 p:PSceProcParam;
begin
 Result:=nil;
 p:=GetSceProcParam;
 if (p=nil) then Exit;

 if (P^.Header.Size>=qword(@PSceProcParam(nil)^.sceUserMainThreadName)+SizeOf(Pointer)) then
 begin
  Result:=p^.sceUserMainThreadName;
 end;
end;

function GetSceUserMainThreadPriority:PDWORD;
var
 p:PSceProcParam;
begin
 Result:=nil;
 p:=GetSceProcParam;
 if (p=nil) then Exit;

 if (P^.Header.Size>=qword(@PSceProcParam(nil)^.SceUserMainThreadPriority)+SizeOf(Pointer)) then
 begin
  Result:=p^.SceUserMainThreadPriority;
 end;
end;

function GetSceUserMainThreadStackSize:PDWORD;
var
 p:PSceProcParam;
begin
 Result:=nil;
 p:=GetSceProcParam;
 if (p=nil) then Exit;

 if (P^.Header.Size>=qword(@PSceProcParam(nil)^.SceUserMainThreadStackSize)+SizeOf(Pointer)) then
 begin
  Result:=p^.SceUserMainThreadStackSize;
 end;
end;

function GetSceLibcParam:Pointer;
var
 p:PSceProcParam;
begin
 Result:=nil;
 p:=GetSceProcParam;
 if (p=nil) then Exit;

 if (P^.Header.Size>=qword(@PSceProcParam(nil)^._sceLibcParam)+SizeOf(Pointer)) then
 begin
  Result:=p^._sceLibcParam;
 end;
end;

function GetSceKernelMemParam:Pointer;
var
 p:PSceProcParam;
begin
 Result:=nil;
 p:=GetSceProcParam;
 if (p=nil) then Exit;

 if (P^.Header.Size>=qword(@PSceProcParam(nil)^._sceKernelMemParam)+SizeOf(Pointer)) then
 begin
  Result:=p^._sceKernelMemParam;
 end;
end;

function GetSceKernelFlexibleMemorySize:PQWORD;
var
 p:PSceKernelMemParam;
begin
 Result:=nil;
 p:=GetSceKernelMemParam;
 if (p=nil) then Exit;

 if (P^.Size>=qword(@PSceKernelMemParam(nil)^.sceKernelFlexibleMemorySize)+SizeOf(Pointer)) then
 begin
  Result:=p^.sceKernelFlexibleMemorySize;
 end;
end;

Function get_dev_progname:RawByteString;
begin
 Result:='';
 if (ps4_app.prog=nil) then Exit;
 Result:=ChangeFileExt(ExtractFileName(Telf_file(ps4_app.prog).pSceFileName),'');
end;

initialization
 ps4_app.pre_load.Init;
 ps4_app.fin_load.Init;
 ps4_app.files.Init;
 ps4_app.mods.Init;
 ps4_app.libs.Init;
 ps4_app.elfs:=TIntegerHandles.Create(1);

end.

