mirror of https://github.com/red-prig/fpPS4.git
677 lines
13 KiB
Plaintext
677 lines
13 KiB
Plaintext
unit game_run;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
windows,
|
|
Classes,
|
|
SysUtils,
|
|
CharStream,
|
|
Dialogs,
|
|
kern_thr,
|
|
md_sleep,
|
|
md_pipe,
|
|
host_ipc,
|
|
host_ipc_interface,
|
|
md_host_ipc,
|
|
game_info;
|
|
|
|
type
|
|
TGameRunConfig=record
|
|
hOutput:THandle;
|
|
hError :THandle;
|
|
|
|
FConfInfo:TConfigInfo;
|
|
FGameItem:TGameItem;
|
|
end;
|
|
|
|
TGameProcessSimple=class(TGameProcess)
|
|
Ftd:p_kthread;
|
|
procedure suspend; override;
|
|
procedure resume; override;
|
|
Destructor Destroy; override;
|
|
end;
|
|
|
|
function run_item(const cfg:TGameRunConfig):TGameProcess;
|
|
|
|
implementation
|
|
|
|
uses
|
|
sys_sysinit,
|
|
kern_param,
|
|
kern_exec,
|
|
vfs_mountroot,
|
|
sys_crt, //<- init writeln redirect
|
|
sys_tty,
|
|
md_exception, //<- install custom
|
|
|
|
sys_event,
|
|
|
|
kern_proc,
|
|
md_systm,
|
|
md_systm_fork,
|
|
|
|
md_game_process,
|
|
|
|
kern_jit,
|
|
kern_jit_ctx,
|
|
|
|
dev_dce,
|
|
display_soft,
|
|
|
|
time,
|
|
pm4_me,
|
|
|
|
vDevice,
|
|
|
|
//internal libs
|
|
ps4_libSceDiscMap,
|
|
ps4_libSceSystemService,
|
|
ps4_libSceUserService,
|
|
ps4_libSceAppContent,
|
|
ps4_libSceIpmi,
|
|
ps4_libSceMbus,
|
|
ps4_libSceDialogs,
|
|
ps4_libSceAvSetting,
|
|
ps4_libSceNpCommon,
|
|
ps4_libSceNpManager,
|
|
ps4_libSceNpTrophy,
|
|
ps4_libSceNpScoreRanking,
|
|
ps4_libSceNpUtility,
|
|
ps4_libSceNpTus,
|
|
ps4_libSceNpGameIntent,
|
|
ps4_libSceNpWebApi,
|
|
ps4_libSceNpWebApi2,
|
|
ps4_libSceNpSns,
|
|
ps4_libSceNpMatching2,
|
|
ps4_libSceNpSignaling,
|
|
ps4_libSceNpParty,
|
|
ps4_libSceRemoteplay,
|
|
ps4_libSceScreenShot,
|
|
ps4_libSceSaveData,
|
|
ps4_libSceAudioOut,
|
|
ps4_libSceAudioIn,
|
|
ps4_libSceNetCtl,
|
|
ps4_libSceGameLiveStreaming,
|
|
ps4_libSceVideoRecording,
|
|
ps4_libSceIme,
|
|
ps4_libSceMove,
|
|
ps4_libSceSharePlay,
|
|
ps4_libSceShareUtility,
|
|
ps4_libScePlayGo,
|
|
ps4_libSceAjm,
|
|
ps4_libSceCompanionUtil,
|
|
ps4_libSceAutoMounterClient,
|
|
//internal libs
|
|
|
|
kern_rtld,
|
|
kern_budget,
|
|
kern_authinfo,
|
|
sys_bootparam,
|
|
subr_backtrace;
|
|
|
|
//
|
|
|
|
procedure TGameProcessSimple.suspend;
|
|
begin
|
|
thread_suspend_all(nil);
|
|
end;
|
|
|
|
procedure TGameProcessSimple.resume;
|
|
begin
|
|
thread_resume_all(nil);
|
|
end;
|
|
|
|
Destructor TGameProcessSimple.Destroy;
|
|
begin
|
|
if (Ftd<>nil) then
|
|
begin
|
|
thread_dec_ref(Ftd);
|
|
Ftd:=nil;
|
|
end;
|
|
inherited;
|
|
end;
|
|
|
|
procedure re_init_tty; register;
|
|
var
|
|
i:Integer;
|
|
begin
|
|
For i:=0 to High(std_tty) do
|
|
begin
|
|
//std_tty[i].t_rd_handle:=GetStdHandle(STD_INPUT_HANDLE);
|
|
//std_tty[i].t_wr_handle:=t_wr_handle;
|
|
//std_tty[i].t_update :=@WakeMainThread;
|
|
end;
|
|
|
|
For i:=0 to High(deci_tty) do
|
|
begin
|
|
//deci_tty[i].t_rd_handle:=GetStdHandle(STD_INPUT_HANDLE);
|
|
//deci_tty[i].t_wr_handle:=t_wr_handle;
|
|
//deci_tty[i].t_update :=@WakeMainThread;
|
|
end;
|
|
|
|
//debug_tty.t_wr_handle:=t_wr_handle;
|
|
//debug_tty.t_update :=@WakeMainThread;
|
|
end;
|
|
|
|
procedure load_config(ConfInfo:TConfigInfo);
|
|
begin
|
|
sys_bootparam.set_neo_mode(ConfInfo.BootParamInfo.Neo);
|
|
|
|
sys_bootparam.p_halt_on_exit :=ConfInfo.BootParamInfo.halt_on_exit;
|
|
sys_bootparam.p_print_guest_syscall:=ConfInfo.BootParamInfo.print_guest_syscall;
|
|
sys_bootparam.p_print_pmap :=ConfInfo.BootParamInfo.print_pmap;
|
|
sys_bootparam.p_print_jit_preload :=ConfInfo.BootParamInfo.print_jit_preload;
|
|
sys_bootparam.p_print_gpu_ops :=ConfInfo.BootParamInfo.print_gpu_ops;
|
|
sys_bootparam.p_print_gpu_hint :=ConfInfo.BootParamInfo.print_gpu_hint;
|
|
|
|
//
|
|
|
|
kern_jit.print_asm :=ConfInfo.JITInfo.print_asm;
|
|
kern_jit.debug_info:=ConfInfo.JITInfo.debug_info;
|
|
|
|
kern_jit_ctx.jit_relative_analize:=ConfInfo.JITInfo.relative_analize;
|
|
kern_jit_ctx.jit_memory_guard :=ConfInfo.JITInfo.memory_guard;
|
|
|
|
//
|
|
|
|
time.strict_ps4_freq :=ConfInfo.MiscInfo.strict_ps4_freq;
|
|
pm4_me.use_renderdoc_capture:=ConfInfo.MiscInfo.renderdoc_capture;
|
|
|
|
//
|
|
|
|
vDevice.VulkanDeviceGuid:=Default(TGUID);
|
|
TryStringToGUID(ConfInfo.VulkanInfo.device,vDevice.VulkanDeviceGuid);
|
|
|
|
vDevice.VulkanAppFlags:=t_vulkan_app_flags(ConfInfo.VulkanInfo.app_flags);
|
|
//
|
|
|
|
ps4_libSceSystemService.FSystemName :=ConfInfo.PS4SystemService.SystemName;
|
|
ps4_libSceSystemService.FLanguage :=ConfInfo.PS4SystemService.Language;
|
|
ps4_libSceSystemService.FDateFormat :=ConfInfo.PS4SystemService.DateFormat;
|
|
ps4_libSceSystemService.FTimeFormat :=ConfInfo.PS4SystemService.TimeFormat;
|
|
ps4_libSceSystemService.FButtonAssign:=ConfInfo.PS4SystemService.ButtonAssign;
|
|
end;
|
|
|
|
procedure free_params(argv:PPChar);
|
|
var
|
|
curr:PPChar;
|
|
begin
|
|
if (argv=nil) then Exit;
|
|
curr:=argv+1; //skip exec
|
|
while (curr^<>nil) do
|
|
begin
|
|
FreeMem(curr^);
|
|
Inc(curr);
|
|
end;
|
|
FreeMem(argv);
|
|
end;
|
|
|
|
function parse_params(const params:RawByteString;var argv:PPChar):Integer;
|
|
var
|
|
curr:PChar;
|
|
last:PChar;
|
|
|
|
barg:PChar;
|
|
blen:Integer;
|
|
|
|
argc:Integer;
|
|
|
|
state:char;
|
|
|
|
procedure concat_arg(delta:Integer);
|
|
var
|
|
i:Integer;
|
|
begin
|
|
if (curr<>last) then
|
|
begin
|
|
i:=(curr-last);
|
|
ReAllocMem(barg,blen+i+1); //zero truncate
|
|
Move(last^,barg[blen],i);
|
|
blen:=blen+i;
|
|
barg[blen]:=#0;
|
|
end;
|
|
last:=curr+delta;
|
|
end;
|
|
|
|
procedure next_arg;
|
|
begin
|
|
if (barg<>nil) then
|
|
begin
|
|
ReAllocMem(argv,SizeOf(Pointer)*(argc+1+1)); //zero truncate
|
|
argv[argc]:=barg;
|
|
Inc(argc);
|
|
argv[argc]:=nil; //truncate
|
|
barg:=nil; //reset
|
|
blen:=0; //reset
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result:=1;
|
|
|
|
//init
|
|
argc:=1;
|
|
argv:=AllocMem(SizeOf(Pointer)*2);
|
|
argv[0]:=nil; //exec place
|
|
argv[1]:=nil; //truncate
|
|
|
|
curr:=@params[1];
|
|
last:=curr;
|
|
|
|
barg:=nil;
|
|
blen:=0;
|
|
|
|
state:=#0;
|
|
|
|
if (curr<>nil) then
|
|
while (curr^<>#0) do
|
|
begin
|
|
|
|
case state of
|
|
' ':
|
|
if (curr^<>' ') then
|
|
begin
|
|
last:=curr; //update pos
|
|
state:=#0;
|
|
end;
|
|
'\':
|
|
begin
|
|
last:=curr; //update pos
|
|
state:=#0;
|
|
//skip
|
|
Inc(curr);
|
|
Continue;
|
|
end;
|
|
else;
|
|
end;
|
|
|
|
case curr^ of
|
|
|
|
' ':
|
|
begin
|
|
if (state=#0) then
|
|
begin
|
|
concat_arg(1);
|
|
next_arg;
|
|
state:=' ';
|
|
end;
|
|
end;
|
|
|
|
'''',
|
|
'"':
|
|
begin
|
|
if (state=#0) then
|
|
begin
|
|
concat_arg(1);
|
|
state:=curr^;
|
|
end else
|
|
if (state=curr^) then
|
|
begin
|
|
concat_arg(1);
|
|
state:=#0;
|
|
end;
|
|
end;
|
|
|
|
'\':
|
|
begin
|
|
concat_arg(1);
|
|
state:='\';
|
|
end;
|
|
|
|
else;
|
|
end;
|
|
|
|
Inc(curr);
|
|
end;
|
|
|
|
if (state<>' ') then
|
|
begin
|
|
concat_arg(0);
|
|
next_arg;
|
|
end;
|
|
|
|
Result:=argc;
|
|
end;
|
|
|
|
procedure prepare(GameStartupInfo:TGameStartupInfo); SysV_ABI_CDecl;
|
|
var
|
|
err:Integer;
|
|
len:Integer;
|
|
exec:array[0..PATH_MAX] of Char;
|
|
argv:PPChar;
|
|
i,argc:Integer;
|
|
Item:TGameItem;
|
|
begin
|
|
//re_init_tty;
|
|
//init_tty:=@re_init_tty;
|
|
|
|
load_config(GameStartupInfo.FConfInfo);
|
|
|
|
//init all
|
|
sys_init;
|
|
|
|
if (p_host_ipc<>nil) then
|
|
begin
|
|
THostIpcConnect(p_host_ipc).thread_new;
|
|
end;
|
|
|
|
//p_cpuid :=CPUID_NEO_MODE;
|
|
//p_base_ps4_mode:=0;
|
|
//p_neomode :=1;
|
|
|
|
dev_dce.dce_interface:=display_soft.TDisplayHandleSoft;
|
|
|
|
Item:=GameStartupInfo.FGameItem;
|
|
|
|
g_appinfo.mmap_flags:=1; //is_big_app ???
|
|
g_appinfo.CUSANAME:=Item.FGameInfo.TitleId;
|
|
//g_appinfo.hasParamSfo:=1; TODO: check
|
|
//g_appinfo.debug_level:=1;
|
|
|
|
g_appinfo.titleWorkaround.version:=69;
|
|
|
|
//budget init
|
|
p_proc.p_budget_ptype:=PTYPE_BIG_APP;
|
|
p_proc.p_vm_container:=1;
|
|
|
|
kern_app_state_change(as_start);
|
|
kern_app_state_change(as_begin_game_app_mount);
|
|
|
|
kern_reserve_2mb_page(0,M2MB_DEFAULT);
|
|
///
|
|
|
|
Writeln('Name :',Item.FGameInfo.Name );
|
|
Writeln('TitleId:',Item.FGameInfo.TitleId);
|
|
Writeln('Version:',Item.FGameInfo.Version);
|
|
Writeln('Exec :',Item.FGameInfo.Exec );
|
|
Writeln('Param :',Item.FGameInfo.Param );
|
|
|
|
Writeln('app0 :',Item.FMountList.app0 );
|
|
Writeln('system :',Item.FMountList.system);
|
|
Writeln('data :',Item.FMountList.data );
|
|
|
|
//temp hack
|
|
err:=vfs_mount_mkdir('ufs','/savedata0' ,'savedata',nil,0);
|
|
|
|
//fs guest host
|
|
err:=vfs_mount_mkdir('ufs','/app0' ,pchar(Item.FMountList.app0 ),nil,MNT_RDONLY);
|
|
if (err<>0) then
|
|
begin
|
|
print_error_td('error mount "'+Item.FMountList.app0+'" to "/app0" code='+IntToStr(err));
|
|
end;
|
|
|
|
err:=vfs_mount_mkdir('ufs','/system',pchar(Item.FMountList.system),nil,MNT_RDONLY);
|
|
if (err<>0) then
|
|
begin
|
|
print_error_td('error mount "'+Item.FMountList.system+'" to "/system" code='+IntToStr(err));
|
|
end;
|
|
|
|
err:=vfs_mount_mkdir('ufs','/data' ,pchar(Item.FMountList.data ),nil,0);
|
|
if (err<>0) then
|
|
begin
|
|
print_error_td('error mount "'+Item.FMountList.data+'" to "/data" code='+IntToStr(err));
|
|
end;
|
|
|
|
///argv
|
|
|
|
argv:=nil;
|
|
argc:=parse_params(Item.FGameInfo.Param,argv);
|
|
|
|
FillChar(exec,SizeOf(exec),0);
|
|
|
|
len:=Length(Item.FGameInfo.Exec);
|
|
if (len>PATH_MAX) then len:=PATH_MAX;
|
|
|
|
Move(pchar(Item.FGameInfo.Exec)^,exec,len);
|
|
|
|
argv[0]:=@exec;
|
|
|
|
Writeln('main_thread:',HexStr(curkthread));
|
|
|
|
//
|
|
FreeAndNil(GameStartupInfo);
|
|
//
|
|
|
|
Writeln('main_execve->');
|
|
For i:=0 to argc-1 do
|
|
begin
|
|
Writeln(' argv[',i,']:',argv[i]);
|
|
end;
|
|
|
|
Flush(stdout);
|
|
|
|
err:=main_execve(argv[0],argv,nil);
|
|
|
|
//free data
|
|
free_params(argv);
|
|
|
|
if (err=0) then
|
|
begin
|
|
//jump to code
|
|
main_switch_context;
|
|
end else
|
|
if (err<>0) then
|
|
begin
|
|
print_error_td('error execve "'+exec+'" code='+IntToStr(err));
|
|
end;
|
|
//
|
|
|
|
end;
|
|
|
|
{
|
|
function NtTerminateProcessTrap(ProcessHandle:THANDLE;ExitStatus:DWORD):DWORD; MS_ABI_Default;
|
|
begin
|
|
Result:=0;
|
|
Writeln(stderr,'NtTerminateProcess:0x',HexStr(ExitStatus,8));
|
|
print_backtrace(StdErr,Get_pc_addr,get_frame,0);
|
|
print_backtrace_td(StdErr);
|
|
asm
|
|
mov ProcessHandle,%R10
|
|
mov ExitStatus ,%EDX
|
|
mov $0x2c ,%EAX
|
|
syscall
|
|
end;
|
|
end;
|
|
|
|
type
|
|
t_jmp_rop=packed record
|
|
cmd:WORD; //FF 25
|
|
ofs:DWORD; //00 00 00 00
|
|
adr:QWORD;
|
|
end;
|
|
|
|
Procedure CreateNtTerminateTrap;
|
|
var
|
|
rop:t_jmp_rop;
|
|
adr:Pointer;
|
|
num:PTRUINT;
|
|
R:Boolean;
|
|
begin
|
|
rop.cmd:=$25FF;
|
|
rop.ofs:=0;
|
|
rop.adr:=QWORD(@NtTerminateProcessTrap);
|
|
|
|
adr:=GetProcAddress(GetModuleHandle('ntdll.dll'),'NtTerminateProcess');
|
|
|
|
num:=0;
|
|
R:=WriteProcessMemory(GetCurrentProcess,adr,@rop,SizeOf(rop),num);
|
|
Writeln('CreateNtTerminateTrap:0x',HexStr(adr),' ',R,' ',num);
|
|
end;
|
|
}
|
|
|
|
procedure fork_process(data:Pointer;size:QWORD); SysV_ABI_CDecl;
|
|
var
|
|
td:p_kthread;
|
|
r:Integer;
|
|
|
|
pipefd:THandle;
|
|
parent:THandle;
|
|
|
|
kipc:THostIpcPipeKERN;
|
|
|
|
mem:TPCharStream;
|
|
GameStartupInfo:TGameStartupInfo;
|
|
begin
|
|
//while not IsDebuggerPresent do sleep(100);
|
|
|
|
mem:=TPCharStream.Create(data,size);
|
|
|
|
GameStartupInfo:=TGameStartupInfo.Create(True);
|
|
GameStartupInfo.Deserialize(mem);
|
|
|
|
mem.Free;
|
|
|
|
//free shared
|
|
md_fork_unshare;
|
|
|
|
parent:=md_pidfd_open(md_getppid);
|
|
|
|
pipefd:=GameStartupInfo.FPipe;
|
|
pipefd:=md_pidfd_getfd(parent,pipefd);
|
|
|
|
kipc:=THostIpcPipeKERN.Create;
|
|
kipc.set_pipe(pipefd);
|
|
|
|
p_host_ipc :=kipc;
|
|
p_host_handler:=THostIpcHandler.Create;
|
|
p_host_ipc .FHandler:=p_host_handler;
|
|
|
|
//CreateNtTerminateTrap;
|
|
|
|
td:=nil;
|
|
r:=kthread_add(@prepare,GameStartupInfo,@td,0,'[main]');
|
|
Assert(r=0);
|
|
|
|
msleep_td(0);
|
|
end;
|
|
|
|
function run_item(const cfg:TGameRunConfig):TGameProcess;
|
|
label
|
|
_error;
|
|
var
|
|
r:Integer;
|
|
|
|
kern2mgui:array[0..1] of THandle;
|
|
|
|
fork_info:t_fork_proc;
|
|
|
|
kev:t_kevent;
|
|
|
|
p_mgui_ipc:THostIpcPipeMGUI;
|
|
|
|
s_kern_ipc:THostIpcSimpleKERN;
|
|
s_mgui_ipc:THostIpcSimpleMGUI;
|
|
|
|
GameStartupInfo:TGameStartupInfo;
|
|
mem:TMemoryStream;
|
|
begin
|
|
Result:=nil;
|
|
r:=0;
|
|
|
|
GameStartupInfo:=TGameStartupInfo.Create(False);
|
|
GameStartupInfo.FConfInfo:=cfg.FConfInfo;
|
|
GameStartupInfo.FGameItem:=cfg.FGameItem;
|
|
|
|
SetStdHandle(STD_OUTPUT_HANDLE,cfg.hOutput);
|
|
SetStdHandle(STD_ERROR_HANDLE ,cfg.hError );
|
|
|
|
fork_info:=Default(t_fork_proc);
|
|
|
|
if cfg.FConfInfo.MainInfo.fork_proc then
|
|
begin
|
|
Result:=TGameProcessPipe.Create;
|
|
Result.g_fork:=True;
|
|
|
|
with TGameProcessPipe(Result) do
|
|
begin
|
|
r:=md_pipe2(@kern2mgui,MD_PIPE_ASYNC0 or MD_PIPE_ASYNC1);
|
|
if (r<>0) then goto _error;
|
|
|
|
p_mgui_ipc:=THostIpcPipeMGUI.Create;
|
|
p_mgui_ipc.set_pipe(kern2mgui[0]);
|
|
|
|
g_ipc:=p_mgui_ipc;
|
|
FChildpip:=kern2mgui[1];
|
|
end;
|
|
|
|
//
|
|
|
|
mem:=TMemoryStream.Create;
|
|
|
|
GameStartupInfo.FPipe:=kern2mgui[1];
|
|
GameStartupInfo.Serialize(mem);
|
|
FreeAndNil(GameStartupInfo);
|
|
|
|
fork_info.hInput :=GetStdHandle(STD_INPUT_HANDLE);
|
|
fork_info.hOutput:=cfg.hOutput;
|
|
fork_info.hError :=cfg.hError;
|
|
|
|
fork_info.proc:=@fork_process;
|
|
fork_info.data:=mem.Memory;
|
|
fork_info.size:=mem.Size;
|
|
|
|
r:=md_fork_process(fork_info);
|
|
|
|
mem.Free;
|
|
end else
|
|
begin
|
|
Result:=TGameProcessSimple.Create;
|
|
Result.g_fork:=False;
|
|
|
|
with TGameProcessSimple(Result) do
|
|
begin
|
|
|
|
s_kern_ipc:=THostIpcSimpleKERN.Create;
|
|
s_mgui_ipc:=THostIpcSimpleMGUI.Create;
|
|
|
|
s_kern_ipc.FDest:=s_mgui_ipc;
|
|
s_mgui_ipc.FDest:=s_kern_ipc;
|
|
|
|
g_ipc:=s_mgui_ipc;
|
|
|
|
p_host_ipc :=s_kern_ipc;
|
|
p_host_handler:=THostIpcHandler.Create;
|
|
p_host_ipc .FHandler:=p_host_handler;
|
|
|
|
Ftd:=nil;
|
|
r:=kthread_add(@prepare,GameStartupInfo,@Ftd,0,'[main]');
|
|
|
|
fork_info.fork_pid:=GetProcessID;
|
|
end;
|
|
|
|
end;
|
|
|
|
if (r<>0) then
|
|
begin
|
|
_error:
|
|
ShowMessage('error run process code=0x'+HexStr(r,8));
|
|
FreeAndNil(Result);
|
|
Exit;
|
|
end;
|
|
|
|
Result.g_proc :=fork_info.hProcess;
|
|
Result.g_p_pid:=fork_info.fork_pid;
|
|
|
|
Result.g_ipc.thread_new;
|
|
|
|
kev.ident :=fork_info.fork_pid;
|
|
kev.filter:=EVFILT_PROC;
|
|
kev.flags :=EV_ADD;
|
|
kev.fflags:=NOTE_EXIT or NOTE_EXEC;
|
|
kev.data :=0;
|
|
kev.udata :=nil;
|
|
|
|
Result.g_ipc.kevent(@kev,1);
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|
|
|
|
|
|
|