unit ProbeAssets; {$mode objfpc}{$H+} interface uses ProbeReport; function PrepareOptionalAssets(Report:TProbeReport;const WorkDir:string):string; implementation uses Classes,SysUtils,fpjson,jsonparser,CastleDownload,CastleFilesUtils; function SafeRelative(const S:string):Boolean; var Parts:TStringList; I:Integer; begin Result:=False; if (S='') or (S[1]='/') or (Pos('\',S)>0) or (Pos(':',S)>0) or (Pos(#0,S)>0) or (Pos(#10,S)>0) or (Pos(#13,S)>0) then Exit; Parts:=TStringList.Create; try Parts.StrictDelimiter:=True;Parts.Delimiter:='/';Parts.DelimitedText:=S; for I:=0 to Parts.Count-1 do if (Parts[I]='') or (Parts[I]='.') or (Parts[I]='..') then Exit; Result:=True; finally Parts.Free;end; end; function PrepareOptionalAssets(Report:TProbeReport;const WorkDir:string):string; var Manifest:TJSONData; Files:TJSONArray; I:Integer; Rel,Name,Url:string; Src:TStream;Dst:TFileStream;Count,Expected:Int64; begin Result:=IncludeTrailingPathDelimiter(WorkDir)+'game/'; Manifest:=GetJSON(FileToString('castle-data:/assets-manifest.json')); try Files:=TJSONObject(Manifest).Arrays['files']; if Files.Count=0 then begin Report.Add('assets.native_adapter','NOT_RUN','No game resources supplied; code-only build is expected'); Exit; end; Count:=0; for I:=0 to Files.Count-1 do begin Rel:=Files.Objects[I].Strings['path']; if not SafeRelative(Rel) then raise Exception.Create('Unsafe asset name: '+Rel); Name:=Result+Rel; Url:=Files.Objects[I].Strings['url']; if Pos('castle-data:/game/',Url)<>1 then raise Exception.Create('Unsupported asset URL'); Expected:=Files.Objects[I].Int64s['size']; if not ForceDirectories(ExtractFileDir(Name)) then raise Exception.Create('Cannot create asset directory'); Src:=Download(Url); try Dst:=TFileStream.Create(Name,fmCreate); try Dst.CopyFrom(Src,0); if Dst.Size<>Expected then raise Exception.Create('Size mismatch: '+Rel); Count:=Count+Dst.Size; finally Dst.Free;end; finally Src.Free;end; end; Report.Add('assets.native_adapter','PASS',Format('%d files, %d bytes copied to native paths; formats unchanged; NOT full loader proof',[Files.Count,Count])); finally Manifest.Free;end; end; end.