Editing PKG files

Jump to navigation Jump to search
Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.

Latest revision Your text
Line 1: Line 1:
See also [https://www.psdevwiki.com/ps5/PKG_files PS5 PKG files], [https://www.psdevwiki.com/ps3/PKG_files PSP, PS3 and PS Vita PKG files], [https://wiki.henkaku.xyz/vita/Packages PS Vita PKG files on henkaku wiki].
{{wikify}}
{{wikify}}
For more information, see {{talk}}


== Structure ==
== Package Structure ==
 
=== File Header ===
=== File Header ===
 
While most of the PS4 is little endian, the package file header still uses big endianness as the headers are based on their PS3 predecessors.
While most of the PS4 files are in little-endian, the package file header still uses big endianness as the headers are based on their PSP, PS3 and PS Vita predecessors.


   typedef struct {
   typedef struct {
     uint32_t pkg_magic;                     // 0x000 - 0x7F434E54
     uint32_t pkg_magic;         // 0x000
     uint32_t pkg_type;                       // 0x004
     uint16_t pkg_revision;      // 0x004
     uint32_t pkg_0x008;                     // 0x008 - unknown field
    uint16_t pkg_type;           // 0x006
     uint32_t pkg_file_count;                 // 0x00C
     uint32_t pkg_unk;           // 0x008 - unknown field
     uint32_t pkg_entry_count;               // 0x010
     uint32_t pkg_file_count;     // 0x00C
     uint16_t pkg_sc_entry_count;             // 0x014
     uint32_t pkg_entry_count;   // 0x010
     uint16_t pkg_entry_count_2;             // 0x016 - same as pkg_entry_count
     uint16_t pkg_sc_entry_count; // 0x014
     uint32_t pkg_table_offset;               // 0x018 - file table offset
     uint16_t pkg_entry_count_2; // 0x016 - same as pkg_entry_count
     uint32_t pkg_entry_data_size;           // 0x01C
     uint32_t pkg_table_offset;   // 0x018 - file table offset
     uint64_t pkg_body_offset;               // 0x020 - offset of PKG entries
     uint32_t pkg_ent_data_size; // 0x01C
     uint64_t pkg_body_size;                 // 0x028 - length of all PKG entries
     uint64_t pkg_body_offset;   // 0x020 - offset of PKG entries
     uint64_t pkg_content_offset;            // 0x030
     uint64_t pkg_body_size;     // 0x028 - length of all PKG entries
     uint64_t pkg_content_size;               // 0x038
     <br>
     unsigned char pkg_padding[0x10];         // 0x030 - 16 bytes padding
     unsigned char pkg_content_id[0x24];      // 0x040 - packages' content ID as a 36-byte string
     unsigned char pkg_content_id[0x24];      // 0x040 - packages' content ID as a 36-byte string
     unsigned char pkg_padding[0xC];         // 0x064 - padding
     unsigned char pad[0xC];                 // 0x064 - 0x070 - padding
     uint32_t pkg_drm_type;                  // 0x070 - DRM type
     uint32_t pkg_drm_type;                  // 0x070 - DRM type
     uint32_t pkg_content_type;              // 0x074 - Content type
     uint32_t pkg_content_type;              // 0x074 - Content type
     uint32_t pkg_content_flags;              // 0x078 - Content flags
     // other stuff... 0x78 - 0x100
    uint32_t pkg_promote_size;              // 0x07C
    uint32_t pkg_version_date;              // 0x080
    uint32_t pkg_version_hash;              // 0x084
    uint32_t pkg_0x088;                      // 0x088
    uint32_t pkg_0x08C;                      // 0x08C
    uint32_t pkg_0x090;                      // 0x090
    uint32_t pkg_0x094;                      // 0x094
    uint32_t pkg_iro_tag;                    // 0x098
    uint32_t pkg_drm_type_version;          // 0x09C
     <br>
     <br>
     /* Digest table */
     /* Digest table */
Line 43: Line 32:
     unsigned char digest_table_digest[0x20]; // 0x140 - sha256 digest for digest table
     unsigned char digest_table_digest[0x20]; // 0x140 - sha256 digest for digest table
     unsigned char digest_body_digest[0x20];  // 0x160 - sha256 digest for main table
     unsigned char digest_body_digest[0x20];  // 0x160 - sha256 digest for main table
     // ...
     // ... 0x180 - 0x400
     uint32_t pfs_image_count;                // 0x404 - count of PFS images
     uint32_t pfs_image_count;                // 0x404 - count of PFS images
     uint64_t pfs_image_flags;               // 0x408 - PFS flags
     uint64_t pfs_flags;                     // 0x408 - PFS flags
     uint64_t pfs_image_offset;              // 0x410 - offset to start of external PFS image
     uint64_t pfs_image_offset;              // 0x410 - offset to start of external PFS image
     uint64_t pfs_image_size;                // 0x418 - size of external PFS image
     uint64_t pfs_image_size;                // 0x418 - size of external PFS image
    uint64_t mount_image_offset;            // 0x420
    uint64_t mount_image_size;              // 0x428
    uint64_t pkg_size;                      // 0x430
    uint32_t pfs_signed_size;                // 0x438
    uint32_t pfs_cache_size;                // 0x43C
    unsigned char pfs_image_digest[0x20];    // 0x440
    unsigned char pfs_signed_digest[0x20];  // 0x460
    uint64_t pfs_split_size_nth_0;          // 0x480
    uint64_t pfs_split_size_nth_1;          // 0x488
     // ...
     // ...
    unsigned char pkg_digest[0x20];          // 0xFE0
   } pkg_header;
   } pkg_header;                             // 0x1000


=== Files ===
=== Files ===
The file table is a list of file entries:
The file table is a list of file entries:


Line 89: Line 67:


There are also files without plaintext filenames. These are identified by their ID in the file entry table.
There are also files without plaintext filenames. These are identified by their ID in the file entry table.
=== Table Entry Hashes (SHA-256) ===
The first entry in the index table points to a block of hashes.
Here is an example using an Amazon Instant Video package file (UP2064-CUSA00130_00-AIV00000000000US.pkg).
0x2A80  <span style="background:#ffff66;">00 00 00 01 00 00 00 00 40 00 00 00 00 00 00 00</span>  ........@....... <span style="background:#ffff66;">First Entry in Index Table</span>.
0x2A90  <span style="background:#ffff66;">00 00 2C A0 00 00 02 20 00 00 00 00 00 00 00 00</span>  .., ... ........
0x2AA0  00 00 00 10 00 00 00 00 60 00 00 00 00 00 00 00  ........`.......
0x2AB0  00 00 20 00 00 00 08 00 00 00 00 00 00 00 00 00  .. .............
0x2AC0  00 00 00 20 00 00 00 00 E0 00 00 00 00 00 30 00  ... ....à.....0.
0x2AD0  00 00 28 00 00 00 01 00 00 00 00 00 00 00 00 00  ..(.............
...
Offset: 0x00002CA0 Length: 0x00000220
0x2CA0  <span style="background:#ff6666;">00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span>  ................
0x2CB0  <span style="background:#ff6666;">00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</span>  ................
0x2CC0  <span style="background:#6666ff;">B4 34 DD 9C B3 C7 91 96 EC F7 D1 F6 8F 8A 2D 18</span>  ´4Ýœ³Ç‘–ì÷Ñö.Š-.
0x2CD0  <span style="background:#6666ff;">8D 07 8F 2D 33 E9 09 6B 1D 22 B5 4E 7A F2 6D 6A</span>  ...-3é.k."µNzòmj
0x2CE0  F0 F5 9E 95 F4 74 13 FE 9F 35 DB 44 57 76 DE 49  ðõž•ôt.þŸ5ÛDWvÞI
0x2CF0  90 B1 68 20 97 8F 70 79 9D 62 95 CD 97 67 5D B0  .±h —.py.b•Í—g]°
0x2D00  1D 6E EE E7 67 3D 7E B4 2F 78 F1 26 2C EE EC 7A  .nîçg=~´/xñ&,îìz
0x2D10  10 40 90 BA FD 0F F9 AF BE ED F1 BC DE 84 30 55  .@.ºý.ù¯¾íñ¼Þ„0U
0x2D20  57 65 D8 7B DD 85 5E D0 73 1B 78 4D A6 EE 00 CF  WeØ{Ý…^Ðs.xM¦î.Ï
0x2D30  A1 0C 3F C4 03 E0 19 5A 0B 36 E1 64 33 7A D1 C6  ¡.?Ä.à.Z.6ád3zÑÆ
0x2D40  1A 4E E9 FA 4F DD AD F4 63 FF 73 8F 9F 24 6F 0E  .NéúOÝ.ôcÿs.Ÿ$o.
0x2D50  DF 22 EB 3D 43 F1 A3 7D C6 D0 BD 97 49 03 EC C2  ß"ë=Cñ£}Æн—I.ìÂ
0x2D60  DB 04 17 61 81 6A 14 9B 0F A3 B6 D7 6D AA 48 5A  Û..a.j.›.£¶×mªHZ
0x2D70  1F 3E 95 6B 63 BD AE B2 A2 E0 AE 44 8D D0 05 EA  .>•kc½®²¢à®D.Ð.ê
0x2D80  93 BB 8F 3E 60 72 F8 0C BD BA DB 0E 4D 01 AA AA  “».>`rø.½ºÛ.M.ªª
0x2D90  65 C0 97 E3 89 18 BB A2 17 6E 49 EE 3A 36 CA 91  eÀ—ã‰.»¢.nIî:6Ê‘
0x2DA0  5B EE 4F 1B 1B 7F 52 17 04 99 DD 8C 19 3A 31 BB  [îO...R..™ÝŒ.:1»
0x2DB0  79 9D F4 70 38 D5 F6 DD FF AA 76 5E 10 F2 CC 8F  y.ôp8ÕöÝÿªv^.òÌ.
0x2DC0  0A D9 DC 1C BA 98 EB B3 4A 74 02 E9 F1 0A 0A 90  .ÙÜ.º˜ë³Jt.éñ...
0x2DD0  69 AC D0 29 9F 93 DF 45 80 35 6E FB AF D6 B1 A5  i¬Ð)Ÿ“ßE€5nû¯Ö±¥
0x2DE0  C6 13 74 C9 51 F7 BA A5 CF 0D DE 13 E3 BB 02 0D  Æ.tÉQ÷º¥Ï.Þ.ã»..
0x2DF0  06 6E 44 64 FF 2A CA 37 B0 20 4C 03 44 CA 5E C9  .nDdÿ*Ê7° L.DÊ^É
0x2E00  B4 D0 03 6B 54 4A 66 ED C7 32 CB D2 E0 34 CF 5F  ´Ð.kTJfíÇ2ËÒà4Ï_
0x2E10  5B 1F 46 B5 81 72 09 D3 33 B3 3E 5E FC 01 6B 11  [.Fµ.r.Ó3³>^ü.k.
0x2E20  9A DF 99 EE A2 2B 5E E2 72 B9 32 02 6B B7 E8 D1  šß™î¢+^âr¹2.k·èÑ
0x2E30  5A 9D B8 A9 97 17 47 4F 11 75 FA 41 6E 79 7A 1B  Z.¸©—.GO.uúAnyz.
0x2E40  94 A5 62 30 EA E0 99 89 3D BB 34 5D 0B F5 E3 17  ”¥b0êà™‰=»4].õã.
0x2E50  BE 2C EE 7B D5 EA 8F 05 FB 0E 07 A2 40 FF 7A 59  ¾,î{Õê..û..¢@ÿzY
0x2E60  6B FE F8 0B 1E 61 85 83 18 9A 53 3A F0 91 46 B7  kþø..a…ƒ.šS:ð‘F·
0x2E70  86 83 38 B8 C1 3E E8 74 C5 4F 4E E6 B6 28 7F 52  †ƒ8¸Á>ètÅONæ¶(.R
0x2E80  55 FE CE EA F4 9E 98 2A BC A5 C4 21 D6 44 17 C9  UþÎêôž˜*¼¥Ä!ÖD.É
0x2E90  76 D9 1C 02 FD 75 BB 37 C3 96 1A C3 1C 3E 5B 5F  vÙ..ýu»7Ö.Ã.>[_
0x2EA0  2B 37 CA 02 AB E2 B7 C6 FB 74 23 B9 A6 C2 C6 0B  +7Ê.«â·Æût#¹¦ÂÆ.
0x2EB0  70 6F 79 CB AE 80 D9 B9 62 1A D6 69 F6 47 FB F2  poyË®€Ù¹b.ÖiöGûò
<span style="background:#ff6666;">First hash</span> is blank, can't hash the table of hashes.<br />
The remaining hashes are for each of the remaining Index Table Entries.<br />
i.e the second entry in the Index Table should have a SHA-256 matching the following
<span style="background:#6666ff;">0xB434DD9CB3C79196ECF7D1F68F8A2D188D078F2D33E9096B1D22B54E7AF26D6A</span>


== PFS ==
== PFS ==
Main article: [[PFS]] ''(only explains un-encrypted, un-signed PFS)''


* Note that the main article [[PFS]] ''only explains un-encrypted, un-signed PFS''.
The main portion of a PKG file is its signed and encrypted PFS image. It is encrypted with XTS-AES with a block size of 0x1000. The key is derived from the HMAC-SHA256 of the concatenation of "0x01 0x00 0x00 0x00" in and the EKPFS. The HMAC key is the "crypt seed" which is at offset 0x370 in the PFS itself. The tweak key is the first 16 bytes of the HMAC result, while the data key is the second 16 bytes.


The main portion of a PKG file is its signed and encrypted [[PFS]] image. It is encrypted with XTS-AES with a block size of 0x1000. The key is derived from the HMAC-SHA256 of the concatenation of "0x01 0x00 0x00 0x00" in and the EKPFS. The HMAC key is the "crypt seed" which is at offset 0x370 in the PFS itself. The tweak key is the first 16 bytes of the HMAC result, while the data key is the second 16 bytes.
The PFS image in a PKG is different from the decrypted image exposed by the system in <code>/mnt/sandbox/pfsmnt/CUSA00001-app0-nest/pfs_image.dat</code>. This image uses <code>dinode_s32</code>, the signed inode variant, which includes a 32-byte signature for every direct and indirect block. Additionally, the indirect block blocks also have a 32-byte signature for each block within.
 
The PFS image in a PKG file is different from the decrypted image exposed by the system in <code>/mnt/sandbox/pfsmnt/CUSA00001-app0-nest/pfs_image.dat</code>. This image uses <code>dinode_s32</code>, the signed inode variant, which includes a 32-byte signature for every direct and indirect block. Additionally, the indirect block blocks also have a 32-byte signature for each block within.


Inside the PFS image in a PKG is a single file: <code>pfs_image.dat</code> which is a special type of compressed PFS image with a <code>PFSC</code> header. Inside <code>pfs_image.dat</code> is where the actual game/theme/DLC data lives.
Inside the PFS image in a PKG is a single file: <code>pfs_image.dat</code> which is a special type of compressed PFS image with a <code>PFSC</code> header. Inside <code>pfs_image.dat</code> is where the actual game/theme/DLC data lives.


== Delivery ==
== Delivery ==
=== Title XML ===
=== Title XML ===
 
The PS4 fetches information about pkg files (including where to download them) from an XML file.  
The PS4 fetches information about package files (including where to download them) from an XML file. This XML file contains information such as if the latest patch is mandatory, the latest package version, the manifest, as well as information in a param.sfo file. Below is an example of a typical title XML file:
This XML file contains information such as if the latest patch is mandatory, the latest package version, the manifest, as well as param.sfo information. Below is an example of a typical title XML file:


  <titlepatch titleid="CUSAXXXXX">
  <titlepatch titleid="CUSAXXXXX">
Line 182: Line 101:


=== Manifest ===
=== Manifest ===
 
It should be noted that PS4 package files have a maximum size of 4GB (or 4096MB), therefore large (most) games are split into chunks or pieces. This is kept track of in the manifest file, which contains json fields which document things such as the size of the final package after the chunks are spliced together, the digest of the final package, the number of chunks, as well as information for each chunk such as the pkg url, offset for splicing, size of the file, and the sha1 hash value of the individual chunk.
It should be noted that PS4 package files have a maximum size of 4GB (or 4096MB), therefore large (most) games are split into chunks or pieces. This is kept track of in the manifest file, which contains JSON fields which document things such as the size of the final package after the chunks are spliced together, the digest of the final package, the number of chunks, as well as information for each chunk such as the package file URL, offset for splicing, size of the file, and the sha1 hash value of the individual chunk.


  originalFileSize: [size]
  originalFileSize: [size]
Line 195: Line 113:
   fileSize: [size, often 4294967296 until last chunk]
   fileSize: [size, often 4294967296 until last chunk]
   hashValue: "[sha1 hash of chunk]"
   hashValue: "[sha1 hash of chunk]"
== Tools ==
=== UnPKG tool by flatz ===
UnPKG is an open source python2 library made by flatz to extract a PS4 ?release and/or debug? NPDRM PKG file. It maybe only extracts only partially the PKG or maybe requires a per-content key.
<source lang="python">
# UnPKG rev 0x00000008 (public edition), (c) flatz
import sys, os, hashlib, hmac, struct, math, traceback
from cStringIO import StringIO
# parse arguments
if len(sys.argv) < 3:
script_file_name = os.path.split(sys.argv[0])[1]
print 'usage: {0} <pkg file> <output dir>'.format(script_file_name)
sys.exit()
pkg_file_path = sys.argv[1]
if not os.path.isfile(pkg_file_path):
print 'error: invalid file specified'
sys.exit()
output_dir = sys.argv[2]
if os.path.exists(output_dir) and not os.path.isdir(output_dir):
print 'error: invalid directory specified'
sys.exit()
elif not os.path.exists(output_dir):
os.makedirs(output_dir)
# cryptography functions
def sha256(data):
return hashlib.sha256(data).digest()
# utility functions
uint64_fmt, uint32_fmt, uint16_fmt, uint8_fmt = '>Q', '>I', '>H', '>B'
int64_fmt, int32_fmt, int16_fmt, int8_fmt = '>q', '>i', '>h', '>b'
def read_string(f, length):
return f.read(length)
def read_cstring(f):
s = ''
while True:
c = f.read(1)
if not c:
return False
if ord(c) == 0:
break
s += c
return s
def read_uint8_le(f):
return struct.unpack('<B', f.read(struct.calcsize('<B')))[0]
def read_uint8_be(f):
return struct.unpack('>B', f.read(struct.calcsize('>B')))[0]
def read_uint16_le(f):
return struct.unpack('<H', f.read(struct.calcsize('<H')))[0]
def read_uint16_be(f):
return struct.unpack('>H', f.read(struct.calcsize('>H')))[0]
def read_uint32_le(f):
return struct.unpack('<I', f.read(struct.calcsize('<I')))[0]
def read_uint32_be(f):
return struct.unpack('>I', f.read(struct.calcsize('>I')))[0]
def read_uint64_le(f):
return struct.unpack('<Q', f.read(struct.calcsize('<Q')))[0]
def read_uint64_be(f):
return struct.unpack('>Q', f.read(struct.calcsize('>Q')))[0]
def read_int8_le(f):
return struct.unpack('<b', f.read(struct.calcsize('<b')))[0]
def read_int8_be(f):
return struct.unpack('>b', f.read(struct.calcsize('>b')))[0]
def read_int16_le(f):
return struct.unpack('<h', f.read(struct.calcsize('<h')))[0]
def read_int16_be(f):
return struct.unpack('>h', f.read(struct.calcsize('>h')))[0]
def read_int32_le(f):
return struct.unpack('<i', f.read(struct.calcsize('<i')))[0]
def read_int32_be(f):
return struct.unpack('>i', f.read(struct.calcsize('>i')))[0]
def read_int64_le(f):
return struct.unpack('<q', f.read(struct.calcsize('<q')))[0]
def read_int64_be(f):
return struct.unpack('>q', f.read(struct.calcsize('>q')))[0]
# main code
PKG_MAGIC = '\x7FCNT'
CONTENT_ID_SIZE = 0x24
SHA256_HASH_SIZE = 0x20
META_ENTRY_SIZE = 0x20
FILE_TYPE_FLAGS_RETAIL = 1 << 31
ENTRY_TYPE_DIGEST_TABLE = 0x0001
ENTRY_TYPE_0x800        = 0x0010
ENTRY_TYPE_0x200        = 0x0020
ENTRY_TYPE_0x180        = 0x0080
ENTRY_TYPE_META_TABLE  = 0x0100
ENTRY_TYPE_NAME_TABLE  = 0x0200
ENTRY_TYPE_LICENSE = 0x04
ENTRY_TYPE_FILE1  = 0x10
ENTRY_TYPE_FILE2  = 0x12
ENTRY_TABLE_MAP = {
ENTRY_TYPE_DIGEST_TABLE: '.digests',
ENTRY_TYPE_0x800: '.entry_0x800',
ENTRY_TYPE_0x200: '.entry_0x200',
ENTRY_TYPE_0x180: '.entry_0x180',
ENTRY_TYPE_META_TABLE: '.meta',
ENTRY_TYPE_NAME_TABLE: '.names',
0x0400: 'license.dat',
0x0401: 'license.info',
0x1000: 'param.sfo',
0x1001: 'playgo-chunk.dat',
0x1002: 'playgo-chunk.sha',
0x1003: 'playgo-manifest.xml',
0x1004: 'pronunciation.xml',
0x1005: 'pronunciation.sig',
0x1006: 'pic1.png',
0x1008: 'app/playgo-chunk.dat',
0x1200: 'icon0.png',
0x1220: 'pic0.png',
0x1240: 'snd0.at9',
0x1260: 'changeinfo/changeinfo.xml',
}
class MyError(Exception):
def __init__(self, message):
self.message = message
def __str__(self):
return repr(self.message)
class FileTableEntry:
entry_fmt = '>IIIIII8x'
def __init__(self):
pass
def read(self, f):
self.type, self.unk1, self.flags1, self.flags2, self.offset, self.size = struct.unpack(self.entry_fmt, f.read(struct.calcsize(self.entry_fmt)))
self.key_index = (self.flags2 & 0xF000) >> 12
self.name = None
try:
with open(pkg_file_path, 'rb') as pkg_file:
magic = read_string(pkg_file, 4)
if magic != PKG_MAGIC:
raise MyError('invalid file magic')
type = read_uint32_be(pkg_file)
is_retail = (type & FILE_TYPE_FLAGS_RETAIL) != 0
pkg_file.seek(0x10) # FIXME: or maybe uint16 at 0x16???
num_table_entries = read_uint32_be(pkg_file)
pkg_file.seek(0x14)
num_system_entries = read_uint16_be(pkg_file)
pkg_file.seek(0x18)
file_table_offset = read_uint32_be(pkg_file)
pkg_file.seek(0x1C)
main_entries_data_size = read_uint32_be(pkg_file)
pkg_file.seek(0x24)
body_offset = read_uint32_be(pkg_file)
pkg_file.seek(0x2C)
body_size = read_uint32_be(pkg_file)
pkg_file.seek(0x414)
content_offset = read_uint32_be(pkg_file)
pkg_file.seek(0x41C)
content_size = read_uint32_be(pkg_file)
pkg_file.seek(0x40)
content_id = read_cstring(pkg_file)
if len(content_id) != CONTENT_ID_SIZE:
raise MyError('invalid content id')
pkg_file.seek(0x100)
main_entries1_digest = pkg_file.read(SHA256_HASH_SIZE)
main_entries2_digest = pkg_file.read(SHA256_HASH_SIZE)
digest_table_digest = pkg_file.read(SHA256_HASH_SIZE)
body_digest = pkg_file.read(SHA256_HASH_SIZE)
pkg_file.seek(0x440)
content_digest = pkg_file.read(SHA256_HASH_SIZE)
content_one_block_digest = pkg_file.read(SHA256_HASH_SIZE)
table_entries = []
table_entries_map = {}
pkg_file.seek(file_table_offset)
for i in xrange(num_table_entries):
entry = FileTableEntry()
entry.read(pkg_file)
table_entries_map[entry.type] = len(table_entries)
table_entries.append(entry)
entry_names = None
entry_digests = None
for i in xrange(num_table_entries):
entry = table_entries[i]
if entry.type == ENTRY_TYPE_NAME_TABLE:
pkg_file.seek(entry.offset)
data = pkg_file.read(entry.size)
if data and len(data) > 0:
data = StringIO(data)
entry_names = []
c = data.read(1)
if ord(c) == 0:
while True:
name = read_cstring(data)
if not name:
break
entry_names.append(name)
else:
raise MyError('weird name table format')
break
entry_name_index = 0
for i in xrange(num_table_entries):
entry = table_entries[i]
type, index = (entry.type >> 8) & 0xFF, entry.type & 0xFF
if type == ENTRY_TYPE_FILE1 or type == ENTRY_TYPE_FILE2:
if entry_name_index < len(entry_names):
entry.name = entry_names[entry_name_index]
entry_name_index += 1
else:
raise MyError('entry name index out of bounds')
elif entry.type in ENTRY_TABLE_MAP:
entry.name = ENTRY_TABLE_MAP[entry.type]
if entry.type == ENTRY_TYPE_DIGEST_TABLE and entry_digests is None:
pkg_file.seek(entry.offset)
entry_digests = pkg_file.read(entry.size)
data = ''
for entry_type in [ENTRY_TYPE_0x800, ENTRY_TYPE_0x200, ENTRY_TYPE_0x180, ENTRY_TYPE_META_TABLE, ENTRY_TYPE_DIGEST_TABLE]:
entry = table_entries[table_entries_map[entry_type]]
pkg_file.seek(entry.offset)
data += pkg_file.read(entry.size)
computed_main_entries1_digest = sha256(data)
data = ''
for entry_type in [ENTRY_TYPE_0x800, ENTRY_TYPE_0x200, ENTRY_TYPE_0x180, ENTRY_TYPE_META_TABLE]:
entry = table_entries[table_entries_map[entry_type]]
pkg_file.seek(entry.offset)
size = entry.size if entry_type != ENTRY_TYPE_META_TABLE else num_system_entries * META_ENTRY_SIZE
data += pkg_file.read(size)
computed_main_entries2_digest = sha256(data)
entry = table_entries[table_entries_map[ENTRY_TYPE_DIGEST_TABLE]]
pkg_file.seek(entry.offset)
data = pkg_file.read(entry.size)
computed_digest_table_digest = sha256(data)
pkg_file.seek(body_offset)
body = pkg_file.read(body_size)
computed_body_digest = sha256(body)
computed_entry_digests = '\x00' * SHA256_HASH_SIZE
for i in xrange(num_table_entries):
entry = table_entries[i]
if entry.type == ENTRY_TYPE_DIGEST_TABLE:
continue
pkg_file.seek(entry.offset)
data = pkg_file.read(entry.size)
computed_entry_digests += sha256(data)
for i in xrange(num_table_entries):
entry = table_entries[i]
name = entry.name if entry.name is not None else 'entry_{0:03}.bin'.format(i)
file_path = os.path.join(output_dir, name)
file_dir = os.path.split(file_path)[0]
if not os.path.exists(file_dir):
os.makedirs(file_dir)
with open(file_path, 'wb') as entry_file:
pkg_file.seek(entry.offset)
data = pkg_file.read(entry.size)
entry_file.write(data)
block_size = 0x10000
num_blocks = 1 + int((content_size - 1) / block_size) if content_size > 0 else 0
pkg_file.seek(content_offset)
data = pkg_file.read(block_size)
computed_content_one_block_digest = sha256(data)
hash_context = hashlib.sha256()
pkg_file.seek(content_offset)
bytes_left = content_size
for i in xrange(num_blocks):
current_size = block_size if bytes_left > block_size else bytes_left
data = pkg_file.read(current_size)
hash_context.update(data)
bytes_left -= block_size
computed_content_digest = hash_context.digest()
is_digests_valid = computed_main_entries1_digest == main_entries1_digest
is_digests_valid = is_digests_valid and computed_main_entries2_digest == main_entries2_digest
is_digests_valid = is_digests_valid and computed_digest_table_digest == digest_table_digest
is_digests_valid = is_digests_valid and computed_body_digest == body_digest
is_digests_valid = is_digests_valid and computed_entry_digests == entry_digests
is_digests_valid = is_digests_valid and computed_content_digest == content_digest
is_digests_valid = is_digests_valid and computed_content_one_block_digest == content_one_block_digest
print 'File information:'
print '            Magic: 0x{0}'.format(magic.encode('hex').upper())
print '              Type: 0x{0:08X}'.format(type), '(retail)' if is_retail else ''
print '        Content ID: {0}'.format(content_id)
print ' Num table entries: {0}'.format(num_table_entries)
print 'Entry table offset: 0x{0:08X}'.format(file_table_offset)
print '    Digest status: {0}'.format('OK' if is_digests_valid else 'FAIL')
print
if num_table_entries > 0:
print 'Table entries:'
for i in xrange(num_table_entries):
entry = table_entries[i]
print '  Entry #{0:03}:'.format(i)
print '        Type: 0x{0:08X}'.format(entry.type)
print '        Unk1: 0x{0:08X}'.format(entry.unk1)
if entry.name is not None:
print '        Name: {0}'.format(entry.name)
print '      Offset: 0x{0:08X}'.format(entry.offset)
print '        Size: 0x{0:08X}'.format(entry.size)
print '      Flags 1: 0x{0:08X}'.format(entry.flags1)
print '      Flags 2: 0x{0:08X}'.format(entry.flags2)
print '    Key index: {0}'.format('N/A' if entry.key_index == 0 else entry.key_index)
print
except IOError:
print 'error: i/o error during processing'
except MyError as e:
print 'error: {0}', e.message
except:
print 'error: unexpected error:', sys.exc_info()[0]
traceback.print_exc(file=sys.stdout)
</source>


== Sample Packages ==
== Sample Packages ==
=== Apps/Games ===
=== Apps/Games ===
* Amazon/LOVEFiLM App
* Amazon/LOVEFiLM App
  http://gs2.ww.prod.dl.playstation.net/gs2/appkgo/prod/CUSA00126_00/1/f_012ccf9936265867696e3906c9bd9f0fd1869111fa372b2d1fad0ca4127ba67b/f/EP4183-CUSA00126_00-AIV00000000000EU.pkg
  http://gs2.ww.prod.dl.playstation.net/gs2/appkgo/prod/CUSA00126_00/1/f_012ccf9936265867696e3906c9bd9f0fd1869111fa372b2d1fad0ca4127ba67b/f/EP4183-CUSA00126_00-AIV00000000000EU.pkg
Line 562: Line 134:


=== Themes ===
=== Themes ===
* 20th Anniversary Dynamic Theme
* 20th Anniversary Dynamic Theme
  http://gs2.ww.prod.dl.playstation.net/gs2/acpkgo/prod/CUSA01501_00/5/f_3074fd8eb8322540c8742865a722c6773b031f68d517df3e18d7037fe58af7b0/f/EP9000-CUSA01501_00-20THANNITHEME001.pkg
  http://gs2.ww.prod.dl.playstation.net/gs2/acpkgo/prod/CUSA01501_00/5/f_3074fd8eb8322540c8742865a722c6773b031f68d517df3e18d7037fe58af7b0/f/EP9000-CUSA01501_00-20THANNITHEME001.pkg
Line 577: Line 148:
* Spiralen Dynamic Theme
* Spiralen Dynamic Theme
  http://gs2.ww.prod.dl.playstation.net/gs2/acpkgo/prod/CUSA01501_00/1/f_a6fb07c75a9776ca2f71e557a98620318bf11378c69d812f147c9f0fe12ee1c6/f/EP9000-CUSA01501_00-0000000000000003.pkg
  http://gs2.ww.prod.dl.playstation.net/gs2/acpkgo/prod/CUSA01501_00/1/f_a6fb07c75a9776ca2f71e557a98620318bf11378c69d812f147c9f0fe12ee1c6/f/EP9000-CUSA01501_00-0000000000000003.pkg


'''Source:''' https://boerse.to/thema/datenbank-fuer-ps4-psn-links.1979635/
'''Source:''' https://boerse.to/thema/datenbank-fuer-ps4-psn-links.1979635/


See also: [[Package Files/Raw List 1]], [[Package Files/Raw List 2]].
See also: [[PKG_files/rawlist1]], [[PKG_files/rawlist2]]


{{File Formats}}
{{File Formats}}
<noinclude>[[Category:Main]]</noinclude>
<noinclude>[[Category:Main]]</noinclude>
Please note that all contributions to PS4 Developer wiki are considered to be released under the GNU Free Documentation License 1.2 (see PS4 Developer wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To protect the wiki against automated edit spam, we kindly ask you to solve the following hCaptcha:

Cancel Editing help (opens in new window)