Skip to content

Commit

Permalink
Update unpkg_vita.dpr
Browse files Browse the repository at this point in the history
Added optional '-key' command for fake 'work.bin' file generation
  • Loading branch information
RikuKH3 authored Sep 22, 2017
1 parent 4af0053 commit 211df40
Showing 1 changed file with 145 additions and 23 deletions.
168 changes: 145 additions & 23 deletions unpkg_vita.dpr
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ program unpkg_vita;
{$R *.res}

uses
Windows, System.SysUtils, System.Classes, DCPrijndael, DCPcrypt2;
Windows, System.SysUtils, System.Classes, IOUtils, DCPrijndael, DCPcrypt2;

{$SETPEFLAGS IMAGE_FILE_RELOCS_STRIPPED}

Expand All @@ -25,31 +25,98 @@ begin
Result := Swap(Value shr 16) or (Swap(Value) shl 16);
end;

function HexToByte(HexStr: string): Byte;
var
i: Byte;
begin
HexStr:=UpperCase(HexStr);
Result:=0;
for i:=1 to Length(HexStr) do begin
Result:=Result shl 4;
if HexStr[i] in ['0'..'9'] then
Result:=Result+(Byte(HexStr[i])-48)
else
if HexStr[i] in ['A'..'F'] then
Result:=Result+(Byte(HexStr[i])-55)
else begin
Result:=0;
Break;
end;
end;
end;

function ProcessSFO(MemoryStream1: TMemoryStream): UTF8String;
var
NameStart, ValueStart, NumOfEntries, LongWord1: LongWord;
Word1: Word;
begin
MemoryStream1.ReadBuffer(LongWord1, 4);
if LongWord1<>$46535000 then exit;
MemoryStream1.Position := 8;
MemoryStream1.ReadBuffer(NameStart, 4);
MemoryStream1.ReadBuffer(ValueStart, 4);
MemoryStream1.ReadBuffer(NumOfEntries, 4);

SetLength(Result, 8);
for LongWord1:=0 to NumOfEntries-1 do begin
MemoryStream1.Position := ($10 * LongWord1) + $14;
MemoryStream1.ReadBuffer(Word1, 2);
MemoryStream1.Position := Word1 + NameStart;
MemoryStream1.ReadBuffer(Result[1], 8);
if Result='CATEGORY' then break;
end;
MemoryStream1.Position := ($10 * LongWord1) + $20;
MemoryStream1.ReadBuffer(LongWord1, 4);
MemoryStream1.Position := LongWord1 + ValueStart;
SetLength(Result, 2);
MemoryStream1.ReadBuffer(Result[1], 2);
end;

const
PkgKeyPSP: array[0..15] of Byte = ($07,$F2,$C6,$82,$90,$B5,$0D,$2C,$33,$81,$8D,$70,$9B,$60,$E6,$2B);
PkgVita2: array[0..15] of Byte = ($E3,$1A,$70,$C9,$CE,$1D,$D7,$2B,$F3,$C0,$62,$29,$63,$F2,$EC,$CB);
PkgVita3: array[0..15] of Byte = ($42,$3A,$CA,$3A,$2B,$D5,$64,$9F,$96,$86,$AB,$AD,$6F,$D8,$80,$1F);
PkgVita4: array[0..15] of Byte = ($AF,$07,$FD,$59,$65,$25,$27,$BA,$F1,$33,$89,$66,$8B,$17,$D9,$EA);
RifHdr: array[0..15] of Byte = (0,1,0,1,0,1,0,2,239,205,171,137,103,69,35,1);
ZeroByte: Byte=0;
var
Cipher: TDCP_rijndael;
FileStream1, FileStream2: TFileStream;
MemoryStream1: TMemoryStream;
PkgKey, CtrKey: array [0..15] of Byte;
PkgKey, CtrKey, WorkbinKey: array [0..15] of Byte;
ItemCnt, NumOfFiles, NameOffset, NameSize, ListPos, LongWord1, HeadSize: LongWord;
DataOffset, ItemOffset, ItemSize, Int641: Int64;
KeyType, ItemFlags: Byte;
utf8s: UTF8String;
WorkbinExist: Boolean;
OutFolder, s, s2: String;
i: Integer;
begin
try
Writeln('PS Vita PKG Unpacker v1.0 by RikuKH3');
Writeln('PS Vita PKG Unpacker v1.1 by RikuKH3');
Writeln('------------------------------------');
if ParamCount=0 then begin Writeln('Usage: '+ExtractFileName(ParamStr(0))+' <input pkg file> [output folder]'); Readln; exit end;
if ParamCount=0 then begin Writeln('Usage: '+ExtractFileName(ParamStr(0))+' <input pkg file> [output folder] [-key=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF]'); Readln; exit end;

WorkbinExist := False;
if ParamCount>1 then begin
OutFolder := ExpandFileName(ParamStr(2));
repeat if OutFolder[Length(OutFolder)]='\' then SetLength(OutFolder, Length(OutFolder)-1) until not (OutFolder[Length(OutFolder)]='\');
if Pos('-key=', LowerCase(ParamStr(2)))=1 then begin
WorkbinExist := True;
s := Copy(ParamStr(2), 6);
if Length(s) mod 2 > 0 then s:='0'+s;
if Length(s)>0 then begin
i:=1;
LongWord1:=0;
repeat
WorkbinKey[LongWord1] := HexToByte(Copy(s,i,2));
Inc(i, 2);
Inc(LongWord1);
until i>=Length(s);
end;
OutFolder:=ExpandFileName(Copy(ParamStr(1),1,Length(ParamStr(1))-Length(ExtractFileExt(ParamStr(1)))));
end else begin
OutFolder := ExpandFileName(ParamStr(2));
repeat if OutFolder[Length(OutFolder)]='\' then SetLength(OutFolder, Length(OutFolder)-1) until not (OutFolder[Length(OutFolder)]='\');
end;
end else OutFolder:=ExpandFileName(Copy(ParamStr(1),1,Length(ParamStr(1))-Length(ExtractFileExt(ParamStr(1)))));

FileStream1:=TFileStream.Create(ParamStr(1), fmOpenRead or fmShareDenyWrite);
Expand All @@ -62,6 +129,11 @@ begin
FileStream1.ReadBuffer(DataOffset, 8);
DataOffset := Swap64(DataOffset);

FileStream1.Position := $37;
SetLength(utf8s, 9);
FileStream1.ReadBuffer(utf8s[1], 9);
OutFolder := OutFolder+'\'+string(utf8s)+'\';

FileStream1.Position := $70;
FileStream1.ReadBuffer(PkgKey[0], $10);

Expand Down Expand Up @@ -120,7 +192,7 @@ begin
s := IntToStr(NumOfFiles);
i := Length(s);

CreateDir(OutFolder);
ForceDirectories(OutFolder);

for LongWord1:=0 to ItemCnt-1 do begin
MemoryStream1.Position := LongWord1 * $20;
Expand All @@ -140,42 +212,92 @@ begin
MemoryStream1.Position := NameOffset;
SetLength(utf8s, NameSize);
MemoryStream1.ReadBuffer(utf8s[1], NameSize);
CreateDir(OutFolder+'\'+string(utf8s));
CreateDir(OutFolder+string(utf8s));
end;
0,1,3,14..17,19,21,22: begin
MemoryStream1.Position := NameOffset;
SetLength(utf8s, NameSize);
MemoryStream1.ReadBuffer(utf8s[1], NameSize);
FileStream1.Position := DataOffset + ItemOffset;
FileStream2:=TFileStream.Create(OutFolder+'\'+string(utf8s), fmCreate or fmOpenWrite or fmShareDenyWrite);
try
Cipher.DecryptStream(FileStream1, FileStream2, ItemSize);
finally FileStream2.Free end;
s2 := IntToStr(ListPos);
s2 := StringOfChar('0', i-Length(s2)) + s2;
Writeln('['+s2+'/'+s+'] ', utf8s);
Inc(ListPos);
FileStream2:=TFileStream.Create(OutFolder+string(utf8s), fmCreate or fmOpenWrite or fmShareDenyWrite);
try
Cipher.DecryptStream(FileStream1, FileStream2, ItemSize);
finally FileStream2.Free end;
end;
end;
end;
finally Cipher.Free end;
HeadSize := MemoryStream1.Size + DataOffset;
finally MemoryStream1.Free end;

if DirectoryExists(OutFolder+'\sce_sys\package\') then begin
FileStream1.Position := FileStream1.Size - $1E0;
FileStream2:=TFileStream.Create(OutFolder+'\sce_sys\package\tail.bin', fmCreate or fmOpenWrite or fmShareDenyWrite);
if FileExists(OutFolder+'sce_sys\param.sfo') then begin
MemoryStream1:=TMemoryStream.Create;
try
FileStream2.CopyFrom(FileStream1, $1E0);
finally FileStream2.Free end;
MemoryStream1.LoadFromFile(OutFolder+'sce_sys\param.sfo');
utf8s := ProcessSFO(MemoryStream1);
finally MemoryStream1.Free end;

FileStream1.Position := 0;
FileStream2:=TFileStream.Create(OutFolder+'\sce_sys\package\head.bin', fmCreate or fmOpenWrite or fmShareDenyWrite);
try
FileStream2.CopyFrom(FileStream1, HeadSize);
finally FileStream2.Free end;
if (utf8s='gd') or (utf8s='gp') then begin // gd=app, ac=addcont, gp=patch
if DirectoryExists(OutFolder+'sce_sys\package\') then begin
FileStream1.Position := FileStream1.Size - $1E0;
FileStream2:=TFileStream.Create(OutFolder+'sce_sys\package\tail.bin', fmCreate or fmOpenWrite or fmShareDenyWrite);
try
FileStream2.CopyFrom(FileStream1, $1E0);
finally FileStream2.Free end;

FileStream1.Position := 0;
FileStream2:=TFileStream.Create(OutFolder+'sce_sys\package\head.bin', fmCreate or fmOpenWrite or fmShareDenyWrite);
try
FileStream2.CopyFrom(FileStream1, HeadSize);
finally FileStream2.Free end;
end;

if WorkbinExist=False then if ParamCount>2 then if Pos('-key=', LowerCase(ParamStr(3)))=1 then begin
WorkbinExist := True;
s := Copy(ParamStr(3), 6);
if Length(s) mod 2 > 0 then s:='0'+s;
if Length(s)>0 then begin
i:=1;
LongWord1:=0;
repeat
WorkbinKey[LongWord1] := HexToByte(Copy(s,i,2));
Inc(i, 2);
Inc(LongWord1);
until i>=Length(s);
end;
end;

if WorkbinExist then begin
ItemFlags := 0;
MemoryStream1:=TMemoryStream.Create;
try
if FileExists(OutFolder+'sce_sys\package\temp.bin') then begin
MemoryStream1.LoadFromFile(OutFolder+'sce_sys\package\temp.bin');
MemoryStream1.Position := $FC;
MemoryStream1.ReadBuffer(LongWord1, 4);
if LongWord1=$1000000 then ItemFlags:=3;
MemoryStream1.Clear;
end;
MemoryStream1.WriteBuffer(RifHdr[0], $10);
FileStream1.Position := $30;
MemoryStream1.CopyFrom(FileStream1, $30);
for i:=0 to 15 do MemoryStream1.WriteBuffer(ZeroByte, 1);
MemoryStream1.WriteBuffer(WorkbinKey, $10);
for i:=0 to 158 do MemoryStream1.WriteBuffer(ZeroByte, 1);
MemoryStream1.WriteBuffer(ItemFlags, 1);
for i:=0 to 255 do MemoryStream1.WriteBuffer(ZeroByte, 1);
MemoryStream1.SaveToFile(OutFolder+'sce_sys\package\work.bin');
finally MemoryStream1.Free end;
end;
end else if utf8s='ac' then begin
if DirectoryExists(OutFolder+'sce_sys\package\') then TDirectory.Delete(OutFolder+'sce_sys\package\', True);
end;
end;
finally FileStream1.Free end;
DeleteFile(OutFolder+'\sce_pfs\pflist');
DeleteFile(OutFolder+'sce_pfs\pflist');
except on E: Exception do begin Writeln(E.Message); Readln end end;
end.

0 comments on commit 211df40

Please sign in to comment.