Editing PKG files
Jump to navigation
Jump to search
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 {{talk}} page | See also {{talk}} page | ||
= Firmware Packages = | |||
Firmware packages are encrypted into .spkg, a sort of [https://wiki.henkaku.xyz/vita/Certified_File Certified File]. | |||
= Game Packages = | |||
All game packages are signed with the ECDSA signature. The public key for it can be found in '''download_plugin.prx''' or in '''nas_plugin.prx''' (this also applies to NPDRM SELFs). Usually game packages are signed with two signatures - one for the header and the other for the entry table. | |||
== Security == | == Security == | ||
Line 20: | Line 12: | ||
The security of .pkg consists in some hashes and signatures. | The security of .pkg consists in some hashes and signatures. | ||
PS3: file SHA-1 + QA digest + ECDSA signature | |||
PS3: file SHA-1 + QA digest + ECDSA signature | |||
PSP: PS3 securities + Extended Header (3 [[PKG_files#PKG_HMAC_algorithm|PKG HMAC]] hashes of the headers) | |||
PSVita: PSP securities + more Metadata (0xD-0x12) that embed some hashes of some files embedded into the .pkg | |||
=== 0x40 digest === | |||
{| class="wikitable" | {| class="wikitable" | ||
Line 36: | Line 24: | ||
! Name !! Offset !! Size !! Remark | ! Name !! Offset !! Size !! Remark | ||
|- | |- | ||
| cmac_hash || 0x0 || 0x10 || | | cmac_hash || 0x0 || 0x10 || CMAC OMAC hash. PS3 gpkg_key used as key. | ||
|- | |- | ||
| npdrm_signature || 0x10 || 0x28 || PKG header NPDRM ECDSA | | npdrm_signature || 0x10 || 0x28 || PKG header NPDRM ECDSA (R_sig, S_sig) | ||
|- | |- | ||
| sha1_hash || 0x38 || 0x8 || | | sha1_hash || 0x38 || 0x8 || last 8 bytes of sha1 hash | ||
|} | |} | ||
<source lang="C"> | <source lang="C"> | ||
typedef struct | typedef struct { // size is 0x40 | ||
u8 cmac_hash[0x10]; | u8 cmac_hash[0x10]; | ||
u8 npdrm_signature[0x28]; | u8 npdrm_signature[0x28]; | ||
u8 sha1_hash[0x8]; | u8 sha1_hash[0x8]; | ||
} | } digest; | ||
</source> | </source> | ||
Line 86: | Line 74: | ||
} | } | ||
</source> | </source> | ||
== File Header == | == File Header == | ||
Header consists of main header that points to metadata header. PSP and | Header consists of main header that points to metadata header. PSP and PSVita .pkg also have .ext header right after main header. | ||
=== Example from a | === Example from a retail PS3 .pkg === | ||
*Example: Scott Pilgrim VS. The World Unlock .pkg [http://zeus.dl.playstation.net/cdn/UP0001/NPUB30162_00/bG751I62Aych0U2E7hsxD5vKS28NurYg8CmJln6oQV4LUDAfXGSOyQHE45reFxIuwD5Qjo1xnQleHqulmmx9HmjNnXX8P5O5jcXlD.pkg file]: | *Example: Scott Pilgrim VS. The World Unlock .pkg [http://zeus.dl.playstation.net/cdn/UP0001/NPUB30162_00/bG751I62Aych0U2E7hsxD5vKS28NurYg8CmJln6oQV4LUDAfXGSOyQHE45reFxIuwD5Qjo1xnQleHqulmmx9HmjNnXX8P5O5jcXlD.pkg file]: | ||
Line 182: | Line 97: | ||
=== Header === | === Header === | ||
{| class="wikitable" | {| class="wikitable" | ||
Line 191: | Line 104: | ||
| <span style="background:#ff0000;">magic || 0x00 || 0x04 || 7F 50 4B 47 || ".PKG" | | <span style="background:#ff0000;">magic || 0x00 || 0x04 || 7F 50 4B 47 || ".PKG" | ||
|- | |- | ||
| <span style="background-color:#008000; color:#FFFFFF;">pkg_revision || 0x04 || 0x02 || 80 00 || 80 00 for finalized | | <span style="background-color:#008000; color:#FFFFFF;">pkg_revision || 0x04 || 0x02 || 80 00 || 80 00 for retail (finalized), 00 00 for debug (non finalized) | ||
|- | |- | ||
| <span style="background-color:#3F48CC; color:#FFFFFF;">pkg_type || 0x06 || 0x02 || 00 01 || 00 01 for PS3, 00 02 for PSP and | | <span style="background-color:#3F48CC; color:#FFFFFF;">pkg_type || 0x06 || 0x02 || 00 01 || 00 01 for PS3, 00 02 for PSP and PSVita | ||
|- | |- | ||
| <span style="background-color:#B5E61D;">pkg_metadata_offset || 0x08 || 0x04 || 00 00 00 C0 || usually 0xC0 for PS3, usually 0x280 for PSP and | | <span style="background-color:#B5E61D;">pkg_metadata_offset || 0x08 || 0x04 || 00 00 00 C0 || usually 0xC0 for PS3, usually 0x280 for PSP and PSVita | ||
|- | |- | ||
| <span style="background-color:#B97A57;">pkg_metadata_count || 0x0C || 0x04 || 00 00 00 08 || metadata item count | | <span style="background-color:#B97A57;">pkg_metadata_count || 0x0C || 0x04 || 00 00 00 08 || metadata item count | ||
|- | |- | ||
| <span style="background-color:#ff7f27;">pkg_metadata_size || 0x10 || 0x04 || 00 00 00 C0 || usually 0xC0 for PSP and PS3, usually 0x160 for | | <span style="background-color:#ff7f27;">pkg_metadata_size || 0x10 || 0x04 || 00 00 00 C0 || usually 0xC0 for PSP and PS3, usually 0x160 for PSVita | ||
|- | |- | ||
| <span style="background-color:#fff200;">item_count || 0x14 || 0x04 || 00 00 00 02 || files and folders into the encrypted data | | <span style="background-color:#fff200;">item_count || 0x14 || 0x04 || 00 00 00 02 || files and folders into the encrypted data | ||
Line 215: | Line 128: | ||
| <span style="background-color:#000000; color:#ffffff;">digest || 0x60 || 0x10 || 09 8B A2 CA 2D 30 30 1F 8B 5B 82 79 C6 70 35 F3 || sha1 from debug files and attributes together merged in one block | | <span style="background-color:#000000; color:#ffffff;">digest || 0x60 || 0x10 || 09 8B A2 CA 2D 30 30 1F 8B 5B 82 79 C6 70 35 F3 || sha1 from debug files and attributes together merged in one block | ||
|- | |- | ||
| <span style="background-color:#ed1c24;">pkg_data_riv || 0x70 || 0x10 || D5 FA 15 9E 7F AC 82 70 BB 3E 0C EB 97 3D 30 11 || | | <span style="background-color:#ed1c24;">pkg_data_riv || 0x70 || 0x10 || D5 FA 15 9E 7F AC 82 70 BB 3E 0C EB 97 3D 30 11 || aes-128-ctr iv. Used with gpkg_key to decrypt data. | ||
|- | |||
| <span style="background-color:#808000;">header_cmac_hash || 0x80 || 0x10 || 48 0D 86 60 9F 26 8E 7F 4F B4 DA A4 33 1E 9A A1 || CMAC OMAC hash from 0x00-0x7F. PS3 gpkg_key used as key. | |||
|- | |- | ||
| <span style="background-color:# | | <span style="background-color:#3f48cc; color:#FFFFFF;">header_npdrm_signature || 0x90 || 0x28 || || PKG header NPDRM ECDSA (R_sig, S_sig) | ||
|- | |||
| <span style="background-color:#a349a4; color:#FFFFFF;">header_sha1_hash || 0xB8 || 0x08 || EC 61 40 39 A6 5B DC BD || last 8 bytes of sha1 hash of 0x00-0x7F | |||
|} | |} | ||
All values are in big endian format. | |||
<source lang="C"> | <source lang="C"> | ||
Line 235: | Line 155: | ||
u8 digest[0x10]; | u8 digest[0x10]; | ||
u8 pkg_data_riv[0x10]; | u8 pkg_data_riv[0x10]; | ||
u8 header_cmac_hash[0x10]; | |||
u8 header_npdrm_signature[0x28]; | |||
u8 header_sha1_hash[0x8]; | |||
} PKG_HEADER; | } PKG_HEADER; | ||
</source> | </source> | ||
Line 241: | Line 163: | ||
=== Extended Header === | === Extended Header === | ||
Not present on PS3 .pkg. Only present on PSP and | Not present on PS3 .pkg. Only present on PSP and PSVita .pkg. It was certainly a security improvement implemented in PSVita Pkg Installer. | ||
HMAC pointed by this header are generated using [[PKG_files#PKG_HMAC_algorithm|PKG HMAC algorithm]]. | HMAC pointed by this header are generated using [[PKG_files#PKG_HMAC_algorithm|PKG HMAC algorithm]]. | ||
<source lang="C"> | <source lang="C"> | ||
typedef struct { | typedef struct{ | ||
u32 magic; // 0x7F657874 (".ext") | u32 magic; // 0x7F657874 (".ext") | ||
u32 unknown_1; // | u32 unknown_1; // always 1 | ||
u32 | u32 ext_header_size; // ex: 0x40 | ||
u32 ext_data_size; // ex: 0x180 | u32 ext_data_size; // ex: 0x180 | ||
u32 main_and_ext_headers_hmac_offset; // ex: 0x100 | u32 main_and_ext_headers_hmac_offset; // ex: 0x100 | ||
u32 metadata_header_hmac_offset; // ex: 0x360, 0x390, 0x490 | u32 metadata_header_hmac_offset; // ex: 0x360, 0x390, 0x490 | ||
u64 tail_offset; // | u64 tail_offset; // tail size seams to be always 0x1A0 | ||
u32 padding1; | u32 padding1; | ||
u32 pkg_key_id; // | u32 pkg_key_id; // PSP = 0x1, PSVita = 0xC0000002, PSM = 0xC0000004 | ||
u32 full_header_hmac_offset; // ex: none (old pkg): 0, 0x930 | u32 full_header_hmac_offset; // ex: none (old pkg): 0, 0x930 | ||
u8 padding2[0x14]; | u8 padding2[0x14]; | ||
Line 273: | Line 195: | ||
} ... | } ... | ||
// u8 padding[variable]; | // u8 padding[variable]; | ||
u8 digest[0x40]; // [[PKG_files#0x40_digest]] | |||
} PKG_METADATA; | } PKG_METADATA; | ||
</source> | </source> | ||
Line 280: | Line 202: | ||
! Identifier !! PKG type !! Category name !! Possible size !! Possible values | ! Identifier !! PKG type !! Category name !! Possible size !! Possible values | ||
|- | |- | ||
|0x1 || All || | |0x1 || All || DRM Type || 4 || 0/5/6/7/8/9/0xA/0xB/0xC (unknown), 0x1 (network), 0x2 (local), 0x3 (DRMfree no license), 0x4 (PSP), 0xD (DRMfree with license), 0xE (PSM) | ||
|- | |- | ||
|0x2 || All || [[PKG_files#ContentType|ContentType]] || 4 || 00 00 00 07 | |0x2 || All || [[PKG_files#ContentType|ContentType]] || 4 || 00 00 00 07 | ||
|- | |- | ||
|0x3 || All || [[PKG_files#PackageType|PackageType]] / [[PKG_files#PackageFlag|PackageFlag]] / [[PKG_files#StorageType|StorageType]] ( | |0x3 || All || [[PKG_files#PackageType|PackageType]] / [[PKG_files#PackageFlag|PackageFlag]] / [[PKG_files#StorageType|StorageType]] (PSVita) || 4 || | ||
|- | |- | ||
|0x4 || All || Package Size || 8 || 00 00 00 00 0E 77 40 00 | |0x4 || All || Package Size || 8 || 00 00 00 00 0E 77 40 00 | ||
Line 292: | Line 214: | ||
|0x6 || PSP || Version + App Version / [[TitleID]] (on size 0xC) || 0xC || "NPEG00005\0\0\0" | |0x6 || PSP || Version + App Version / [[TitleID]] (on size 0xC) || 0xC || "NPEG00005\0\0\0" | ||
|- | |- | ||
|0x7 || PSP | |0x7 || PSP and PS3 || QA Digest (described as "This is a digest of packaged files and attributes.")|| 0x18 || 00 00 00 00 00 00 00 00 B5 76 E1 D9 00 EE 9B BC 8E 24 17 91 5D BC 5A CB | ||
|- | |- | ||
|0x8 || All || unk (1 byte) + PS3/PSP/PSP2 System Version (3 bytes) + Package Version (2 bytes) + App Version (2 bytes) || 8 || 00 00 00 00 00 00 00 00, 00 00 00 00 01 00 01 00, 44 01 70 00 01 01 00 00, 81 01 90 00 01 00 01 00, 81 04 75 00 01 02 01 00 (4.7500, 1.02, 1.00) --> unk: 44 maybe PSP and 02/80/81 PS3 | |0x8 || All || unk (1 byte) + PS3/PSP/PSP2 System Version (3 bytes) + Package Version (2 bytes) + App Version (2 bytes) || 8 || 00 00 00 00 00 00 00 00, 00 00 00 00 01 00 01 00, 44 01 70 00 01 01 00 00, 81 01 90 00 01 00 01 00, 81 04 75 00 01 02 01 00 (4.7500, 1.02, 1.00) --> unk: 44 maybe PSP and 02/80/81 PS3 | ||
Line 304: | Line 226: | ||
|0xC || All || unk || || | |0xC || All || unk || || | ||
|- | |- | ||
|0xD || | |0xD || PSVita || [[PKG_files#PKG Item Record|Item Entries+Names Info]] || 0x28 || Offset in encrypted data (4 Bytes) + Size (4 Bytes) + SHA256 of decrypted data (32 Bytes)<br /> 00 00 00 00 / 00 00 05 A0 / 18 8B B1 8A E5 E8 40 FD EE 71 E2 27 60 55 E6 39 06 94 5B 65 89 04 B2 88 61 DA BB A3 B2 55 C5 C1 | ||
|- | |- | ||
|0xE || | |0xE || PSVita || [[PKG_files#sfo_info|PARAM.SFO Info]] || 0x38 || Offset (4 Bytes) + Size (4 Bytes) + Flags? (4 Bytes) + Firmware (?4 Bytes?) + Unknown (?8 Bytes?) + SHA256 (32 Bytes)<br />00 00 0C 10 / 00 00 05 0C / 00 00 00 14 / 01 67 00 00 / 00 00 00 00 00 00 00 00 / FC 3D 1E 32 FA F7 A6 11 90 46 A6 34 BE 98 9A 78 65 A8 92 25 3C 49 D7 69 73 53 32 E7 8E 06 1E 60 | ||
|- | |- | ||
|0xF || | |0xF || PSVita || [[PKG_files#unknown_data_info|unknown_data_info]] || 0x48 || 00 00 05 60 00 00 03 20 00 00 00 00 00 00 00 00 00 00 00 00 01 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 27 0F 26 BD 5C 2F 13 95 EB 16 E6 62 07 8D 9A E3 DC 0B AC 25 11 1C 19 39 B9 40 06 4E 82 AF 1E C3 | ||
00 00 06 10 00 00 03 20 00 00 01 F4 00 00 01 F7 00 00 00 00 01 01 01 01 A5 74 D5 71 00 03 00 01 00 00 00 00 00 00 00 00 79 52 B2 3F 78 3B 9D F4 C0 BB 26 C0 EC 14 1B 37 8B 9B 69 71 1C 22 C3 49 57 97 B6 DE 78 0A D0 00 | 00 00 06 10 00 00 03 20 00 00 01 F4 00 00 01 F7 00 00 00 00 01 01 01 01 A5 74 D5 71 00 03 00 01 00 00 00 00 00 00 00 00 79 52 B2 3F 78 3B 9D F4 C0 BB 26 C0 EC 14 1B 37 8B 9B 69 71 1C 22 C3 49 57 97 B6 DE 78 0A D0 00 | ||
|- | |- | ||
|0x10 || | |0x10 || PSVita || [[PKG_files#entirety_info|Entirety Info]] || 0x38 || 00 00 0A 00 00 00 00 A0 D3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 4D AD EB 44 67 51 C1 2D 69 95 46 8C 82 A9 3D 74 AD F4 62 49 90 BD EE F0 75 97 A3 B6 5B 17 48 | ||
00 00 0A B0 00 00 01 60 FF D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 D2 7F 9E EF 38 F7 61 6E C1 C5 CC B1 E1 83 12 0C 6F 38 4C 8C 22 84 8B A4 3B 7B 47 57 77 C9 F9 C9 | 00 00 0A B0 00 00 01 60 FF D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 D2 7F 9E EF 38 F7 61 6E C1 C5 CC B1 E1 83 12 0C 6F 38 4C 8C 22 84 8B A4 3B 7B 47 57 77 C9 F9 C9 | ||
|- | |- | ||
|0x11 || | |0x11 || PSVita || PublishingTools version (4 Bytes) + PFSBuilder version (4 Bytes) + padding (0x20 Bytes) || 0x28 || 01 60 00 00 00 06 23 47 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
02 07 00 00 00 07 60 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 02 07 00 00 00 07 60 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
|- | |- | ||
|0x12 || | |0x12 || PSVita || [[PKG_files#self_info|self_info]] || 0x38 || 00 00 11 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 CF 74 D5 30 04 99 F0 57 E8 65 20 5C 3D 71 12 36 F1 E4 EF BC 98 EA 52 F1 F0 60 E8 7C 8A 53 B8 A4 | ||
|} | |} | ||
Line 323: | Line 245: | ||
{| class="wikitable" | {| class="wikitable" | ||
! | ! content type !! type name !! install path (on ps3) !! notes | ||
|- | |- | ||
|0x1 || ? || /dev_hdd0/game/ | |0x1 || ? || /dev_hdd0/game/ || Found in UP0700-NPUJ00213_00-0000000000000002 (so old that it doesn't embed make_package_npdrm revision) | ||
|- | |- | ||
|0x2 || | |0x2 || || || | ||
|- | |- | ||
|0x3 || | |0x3 || || || | ||
|- | |- | ||
|0x4 || GameData || /dev_hdd0/game/ || | |0x4 || GameData (also patches) || /dev_hdd0/game/ || | ||
|- | |- | ||
|0x5 || GameExec || /dev_hdd0/game/ | |0x5 || GameExec || /dev_hdd0/game/ || | ||
|- | |- | ||
|0x6 || PS1emu || /dev_hdd0/game | |0x6 || PS1emu || /dev_hdd0/game/ || | ||
|- | |- | ||
|0x7 || PSP || /dev_hdd0/game/ || | |0x7 || PSP & PCEngine || /dev_hdd0/game/ || | ||
|- | |- | ||
|0x8 || | |0x8 || || || | ||
|- | |- | ||
|0x9 || Theme || /dev_hdd0/theme || | |0x9 || Theme || /dev_hdd0/theme || | ||
|- | |- | ||
|0xA || Widget || /dev_hdd0/widget | |0xA || Widget || /dev_hdd0/widget || | ||
|- | |- | ||
|0xB || License || /dev_hdd0/home/<current user>/exdata || | |0xB || License || /dev_hdd0/home/<current user>/exdata || | ||
|- | |- | ||
|0xC || VSHModule || /dev_hdd0/vsh/modules/ | |0xC || VSHModule || /dev_hdd0/vsh/modules/ || | ||
|- | |- | ||
|0xD || PSN Avatar || /dev_hdd0/home/<current user>/psn_avatar || | |0xD || PSN Avatar || /dev_hdd0/home/<current user>/psn_avatar || | ||
|- | |- | ||
|0xE || PSPgo || /dev_hdd0/game/ | |0xE || PSPgo || /dev_hdd0/game/ || Displayed as Unknown Album: Corrupted Data | ||
|- | |- | ||
|0xF || minis || /dev_hdd0/game | |0xF || minis || /dev_hdd0/game/ || | ||
|- | |- | ||
|0x10 || NEOGEO || /dev_hdd0/game | |0x10 || NEOGEO || /dev_hdd0/game/ || | ||
|- | |- | ||
|0x11 || VMC || /dev_hdd0/tmp/vmc/ | |0x11 || VMC || /dev_hdd0/tmp/vmc/ || | ||
|- | |- | ||
|0x12 || ?PS2Classic? Seen on PS2 classic | |0x12 || ?PS2Classic? Seen on PS2 classic || /dev_hdd0/game/ || | ||
|- | |- | ||
|0x13 || | |0x13 || || || | ||
|- | |- | ||
|0x14 || Seen on PSP remastered || /dev_hdd0/game/ | |0x14 || Seen on PSP remastered || /dev_hdd0/game/ || | ||
|- | |- | ||
|0x15 || PSP2GD ( | |0x15 || PSP2GD (PSVita Game Data) || || | ||
|- | |- | ||
|0x16 || PSP2AC ( | |0x16 || PSP2AC (PSVita Additional Content) || || | ||
|- | |- | ||
|0x17 || PSP2LA ( | |0x17 || PSP2LA (PSVita LiveArea) || || | ||
|- | |- | ||
|0x18 || PSM ( | |0x18 || ?PSM? (PSVita PSM) || || [https://www.playstation.com/en-ca/explore/playstation-mobile/ PlayStation Mobile] | ||
|- | |- | ||
|0x19 || WT || /dev_hdd0/game/ || | |0x19 || WT (Web TV?) || /dev_hdd0/game/ || | ||
|- | |- | ||
|0x1D || PSM | |0x1D || ?PSM? (PSVita PSM) || || | ||
|- | |- | ||
|0x1F || PSP2Theme ( | |0x1F || PSP2Theme (PSVita Theme) || || | ||
|} | |} | ||
Line 385: | Line 307: | ||
! Identifier !! type name !! usage !! notes | ! Identifier !! type name !! usage !! notes | ||
|- | |- | ||
| | | 4th byte, bit: xxx1 xxxx, ex: 00 00 00 1E (+CumulativePatch), 00 00 20 1E (+VC-MC+CumulativePatch) || Patch || for PSP game updates, to use with CumulativePatch, IncrementalPatch, HybridPatch || | ||
|- | |- | ||
| | | ex: 00 00 08 5A (+RenameDirectory) || DiscGamePatch || for PS3 BD games || | ||
|- | |- | ||
| | | 4th byte, bit: xxx1 xxxx, ex: 00 00 00 5E (+Finalized+RenameDirectory) || HDDGamePatch || for PS3 HDD games (NPDRM) || | ||
|- | |- | ||
| | | unk || NoEBOOTBIN || || | ||
|- | |- | ||
| ex: 00 00 00 | | 4th byte, bit: xxxx 1x1x, ex: 00 00 00 4A (+RenameDirectory) || Demo || || | ||
|- | |- | ||
| ex: 00 00 00 0C || Key || || | | 4th byte, bit: xxxx 11xx, ex: 00 00 00 0C || Key || || | ||
|- | |- | ||
| | | ex: 00 00 30 1E (+VC-MC+CumulativePatch+Patch) || Upgradable || || | ||
|} | |} | ||
To class in PackageType or PackageFlag : DiscBinded | |||
=== PackageFlag === | === PackageFlag === | ||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Identifier !! type name !! usage !! notes | ||
|- | |- | ||
| | | ex: 00 00 00 00 || NO FLAGS || || yes it exists, it was found in IP9100-NPIA00001_00-PS2HDDSYSDAT0001 that was builded with revision 960 (very old) !!! | ||
or even UP0700-NPUJ00213_00-0000000000000002 (so old that it doesn't embed make_package_npdrm revision) | |||
|- | |- | ||
| | | 4rd byte, bit: x1xx xxxx, ex: 00 00 00 4E, 00 00 00 5E || RenameDirectory || || | ||
|- | |- | ||
| | | when RenameDirectory flag is not set (default), ex: 00 00 00 0E || NoRenameDirectory || || | ||
|- | |- | ||
| | | 4rd byte, bit: xxxx x11x, ex: 00 00 00 0E, 00 00 00 1E, 00 00 00 4E, 00 00 00 5E, warning: don't mistake with the finalized flag in main header || Finalized || || PS1 and PSP games have not this flag: 00 00 02 0C, PS3 game that has not this flag : 00 00 00 08 | ||
|- | |- | ||
| | | ex: 00 00 00 08 || NON Finalized flaged PS3 package (DLC) || || | ||
|- | |- | ||
| | | ex: 00 00 02 0C, 00 00 02 1C (+Patch) || NON Finalized flaged package (all PS1 and PSP packages) || || | ||
|- | |- | ||
| | | ex: 00 00 02 4E (+RenameDirectory) || NON Finalized flaged minis package || || | ||
|- | |- | ||
| | | ex: 00 00 04 8C || Theme (.p3t.edat) / License package (.edat) || -> ????noEBOOOT?????? || | ||
|- | |- | ||
| | | ex: 00 00 04 0C || VSH Module package (PS3 only) || || | ||
|- | |- | ||
| | | 4rd byte, bit: xxxx ?111x?, ex: 00 00 20 1E, 00 00 00 1E || CumulativePatch || Embeds the previous patches || to use with Patch PackageType | ||
|- | |- | ||
| | | unk || IncrementalPatch || Requires the previous patches to be installed first || to use with Patch PackageType | ||
|- | |- | ||
| | | unk || HybridPatch || || to use with Patch PackageType | ||
|- | |- | ||
| | | 3rd byte, bit: x1xx xxxx, ex: 00 00 40 0E || NonGame || used for most of PSVita NPDRM apps || | ||
|} | |} | ||
=== StorageType === | === StorageType === | ||
Seams to be PSVita only. | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Identifier !! type name !! usage !! notes | ||
|- | |- | ||
| | | 3rd byte, bit: xx1x xxxx, ex: 00 00 20 0E, 00 00 20 1E || VC-MC || | ||
[VC-MC] | |||
This application is distributed by PS Vita card(VC). | |||
This application may be distributed by network and installed onto memory card(MC). | |||
[In case of VC distribution] | |||
The VC does NOT have rewritable(R/W) area, and Patches/Additional Contents/Save Data are stored on MC. | |||
MC is REQUIRED to run this application. | |||
|| There exists 2 variants : 2GB VC and 4GB VC (it is still unknown how to recognize this difference) | |||
|- | |- | ||
| | | default (when VC-MC flag is not set), ex: 00 00 00 0E, 00 00 00 1E || VC-VC || [No VC/MC-MC] | ||
This application is NOT distributed by PS Vita card(VC). | |||
This application is distributed by network and installed onto memory card(MC). | |||
|| | |||
|} | |} | ||
Line 474: | Line 403: | ||
=== entirety_info === | === entirety_info === | ||
typedef struct { // size is 0x38 | typedef struct { // size is 0x38 | ||
u32 entirety_data_offset; // located just before SFO | u32 entirety_data_offset; // located just before SFO | ||
Line 482: | Line 410: | ||
u32 unk2_int; // ex: 1, 0 | u32 unk2_int; // ex: 1, 0 | ||
u8 unk3[0x8]; | u8 unk3[0x8]; | ||
u8 entirety_digest[0x20]; | u8 entirety_digest[0x20]; // SHA256 of entirety_data. Called EntiretyDigest: This is a digest of above digests. | ||
} entirety_info; | } entirety_info; | ||
{| class="wikitable" | {| class="wikitable" | ||
! Official Name !! Official Description !! Description !! Flags | ! Official Name !! Official Description !! Description !! Flags | ||
|- | |- | ||
| ContentDigest || This is a digest of content unique value. || May be located at offset 0x0 under encrypted form... || | | ContentDigest || This is a digest of content unique value. || May be located at offset 0x0 under encrypted form... || | ||
Line 519: | Line 442: | ||
| OriginDigest || This is the ContentDigest of base game package. || || | | OriginDigest || This is the ContentDigest of base game package. || || | ||
|- | |- | ||
| | | OriginGameDigest || unk || || | ||
|- | |- | ||
| | | TargetDigest || unk || || | ||
|- | |- | ||
| TargetGameDigest || | | TargetGameDigest || unk || || | ||
|} | |} | ||
typedef struct { // size is 0xA0 | typedef struct { // size is 0xA0 | ||
u8 enc_content_digest[0x20]; | u8 enc_content_digest[0x20]; | ||
Line 534: | Line 454: | ||
u8 system_digest[0x20]; | u8 system_digest[0x20]; | ||
u8 header_digest[0x20]; | u8 header_digest[0x20]; | ||
u8 livearea_digest[0x20]; // ?SHA256? of ?? | u8 livearea_digest[0x20]; // ?SHA256? of ??. Called LiveareaDigest: This is a digest of sce_sys/livearea(&retail) files. | ||
} entirety_data_A0_NOPARAM_LA_D300; // 1101 0011 0000 0000 -> 5 | } entirety_data_A0_NOPARAM_LA_D300; // 1101 0011 0000 0000 -> 5 | ||
Line 555: | Line 475: | ||
u8 param_digest[0x20]; | u8 param_digest[0x20]; | ||
u8 header_digest[0x20]; | u8 header_digest[0x20]; | ||
u8 others_digest[0x20]; // ?SHA256? of ?? | u8 others_digest[0x20]; // ?SHA256? of ??. Called OthersDigest: This is a digest of sce_sys files except above files. | ||
} entirety_data_100_OTH_FE10; // 1111 1110 0001 0000 -> 8 | } entirety_data_100_OTH_FE10; // 1111 1110 0001 0000 -> 8 | ||
Line 566: | Line 486: | ||
u8 param_digest[0x20]; | u8 param_digest[0x20]; | ||
u8 header_digest[0x20]; | u8 header_digest[0x20]; | ||
u8 livearea_digest[0x20]; // ?SHA256? of ?? | u8 livearea_digest[0x20]; // ?SHA256? of ??. Called LiveareaDigest: This is a digest of sce_sys/livearea(&retail) files. | ||
u8 others_digest[0x20]; // ?SHA256? of ?? | u8 others_digest[0x20]; // ?SHA256? of ??. Called OthersDigest: This is a digest of sce_sys files except above files. | ||
} entirety_data_120_LA_OTH_FF10; // 1111 1111 0001 0000 -> 9 | } entirety_data_120_LA_OTH_FF10; // 1111 1111 0001 0000 -> 9 | ||
Line 578: | Line 498: | ||
u8 param_digest[0x20]; | u8 param_digest[0x20]; | ||
u8 header_digest[0x20]; | u8 header_digest[0x20]; | ||
u8 change_info_digest[0x20]; // ?SHA256? of ?? | u8 change_info_digest[0x20]; // ?SHA256? of ??. Called ChangeInfoDigest: This is a digest of sce_sys/changeinfo files. | ||
u8 others_digest[0x20]; // ?SHA256? of ?? | u8 others_digest[0x20]; // ?SHA256? of ??. Called OthersDigest: This is a digest of sce_sys files except above files. | ||
u8 enc_origin_digest[0x20]; // ?SHA256? of ??. ENCRYPTED FORM !! | u8 enc_origin_digest[0x20]; // ?SHA256? of ??. ENCRYPTED FORM !! May be: Called OriginDigest: This is the ContentDigest of base game package. | ||
} entirety_data_140_CHG_OTH_ORI_FE38; // 1111 1110 0011 1000 -> 10 | } entirety_data_140_CHG_OTH_ORI_FE38; // 1111 1110 0011 1000 -> 10 | ||
Line 591: | Line 511: | ||
u8 param_digest[0x20]; | u8 param_digest[0x20]; | ||
u8 header_digest[0x20]; | u8 header_digest[0x20]; | ||
u8 livearea_digest[0x20]; // ?SHA256? of ?? | u8 livearea_digest[0x20]; // ?SHA256? of ??. Called LiveareaDigest: This is a digest of sce_sys/livearea(&retail) files. | ||
u8 manual_digest[0x20]; // ?SHA256? of ?? | u8 manual_digest[0x20]; // ?SHA256? of ??. Called ManualDigest: This is a digest of sce_sys/manual files. | ||
u8 others_digest[0x20]; // ?SHA256? of ?? | u8 others_digest[0x20]; // ?SHA256? of ??. Called OthersDigest: This is a digest of sce_sys files except above files. | ||
} entirety_data_140_LA_MAN_OTH_FF90; // 1111 1110 1001 0000 -> 9 | } entirety_data_140_LA_MAN_OTH_FF90; // 1111 1110 1001 0000 -> 9 | ||
Line 604: | Line 524: | ||
u8 param_digest[0x20]; | u8 param_digest[0x20]; | ||
u8 header_digest[0x20]; | u8 header_digest[0x20]; | ||
u8 livearea_digest[0x20]; // ?SHA256? of ?? | u8 livearea_digest[0x20]; // ?SHA256? of ??. Called LiveareaDigest: This is a digest of sce_sys/livearea(&retail) files. | ||
u8 manual_digest[0x20]; // ?SHA256? of ?? | u8 manual_digest[0x20]; // ?SHA256? of ??. Called ManualDigest: This is a digest of sce_sys/manual files. | ||
u8 trophy_digest[0x20]; // ?SHA256? of ?? | u8 trophy_digest[0x20]; // ?SHA256? of ??. Called TrophyDigest: This is a digest of sce_sys/trophy files. | ||
u8 others_digest[0x20]; // ?SHA256? of ?? | u8 others_digest[0x20]; // ?SHA256? of ??. Called OthersDigest: This is a digest of sce_sys files except above files. | ||
} entirety_data_160_LA_MAN_TRP_OTH_FFD0; // 1111 1111 1101 0000 -> 11 | } entirety_data_160_LA_MAN_TRP_OTH_FFD0; // 1111 1111 1101 0000 -> 11 | ||
Line 618: | Line 538: | ||
u8 param_digest[0x20]; | u8 param_digest[0x20]; | ||
u8 header_digest[0x20]; | u8 header_digest[0x20]; | ||
u8 trophy_digest[0x20]; // ?SHA256? of ?? | u8 trophy_digest[0x20]; // ?SHA256? of ??. Called TrophyDigest: This is a digest of sce_sys/trophy files. | ||
u8 change_info_digest[0x20]; // ?SHA256? of ?? | u8 change_info_digest[0x20]; // ?SHA256? of ??. Called ChangeInfoDigest: This is a digest of sce_sys/changeinfo files. | ||
u8 others_digest[0x20]; // ?SHA256? of ?? | u8 others_digest[0x20]; // ?SHA256? of ??. Called OthersDigest: This is a digest of sce_sys files except above files. | ||
u8 enc_origin_digest[0x20]; // ?SHA256? of ??. ENCRYPTED FORM !! | u8 enc_origin_digest[0x20]; // ?SHA256? of ??. ENCRYPTED FORM !! May be: Called OriginDigest: This is the ContentDigest of base game package. | ||
} entirety_data_160_TRP_CHG_OTH_ORI_FE78; // 1111 1110 0111 1000 -> 11 | } entirety_data_160_TRP_CHG_OTH_ORI_FE78; // 1111 1110 0111 1000 -> 11 | ||
Line 632: | Line 552: | ||
u8 param_digest[0x20]; | u8 param_digest[0x20]; | ||
u8 header_digest[0x20]; | u8 header_digest[0x20]; | ||
u8 manual_digest[0x20]; // ?SHA256? of ?? | u8 manual_digest[0x20]; // ?SHA256? of ??. Called ManualDigest: This is a digest of sce_sys/manual files. | ||
u8 trophy_digest[0x20]; // ?SHA256? of ?? | u8 trophy_digest[0x20]; // ?SHA256? of ??. Called TrophyDigest: This is a digest of sce_sys/trophy files. | ||
u8 change_info_digest[0x20]; // ?SHA256? of ?? | u8 change_info_digest[0x20]; // ?SHA256? of ??. Called ChangeInfoDigest: This is a digest of sce_sys/changeinfo files. | ||
u8 others_digest[0x20]; // ?SHA256? of ?? | u8 others_digest[0x20]; // ?SHA256? of ??. Called OthersDigest: This is a digest of sce_sys files except above files. | ||
u8 enc_origin_digest[0x20]; // ?SHA256? of ??. ENCRYPTED FORM !! | u8 enc_origin_digest[0x20]; // ?SHA256? of ??. ENCRYPTED FORM !! May be: Called OriginDigest: This is the ContentDigest of base game package. | ||
} entirety_data_180_MAN_TRP_CHG_OTH_ORI_FEF8; // 1111 1110 1111 1000 -> 12 | } entirety_data_180_MAN_TRP_CHG_OTH_ORI_FEF8; // 1111 1110 1111 1000 -> 12 | ||
= | <source lang="text"> | ||
Entirety Bitflags temporary table: | |||
1000 0000 0000 0000 : Content | 1000 0000 0000 0000 : Content | ||
0100 0000 0000 0000 : Game | 0100 0000 0000 0000 : Game | ||
Line 668: | Line 587: | ||
=== self_info === | === self_info === | ||
typedef struct { // size is 0x38 | typedef struct { // size is 0x38 | ||
u32 self_info_offset; | u32 self_info_offset; | ||
u32 self_info_size; // usually 0x10 or 0x20 | u32 self_info_size; // usually 0x10 or 0x20 | ||
u32 unk_int; // ex: 00 00 00 43 | u32 unk_int; // ex: 00 00 00 43 | ||
Line 679: | Line 597: | ||
typedef struct { // size is 0x10 | typedef struct { // size is 0x10 | ||
u64 unk; // ex: 00 00 00 00 00 00 00 01, 80 00 00 00 00 00 00 00 | u64 unk; // ex: 00 00 00 00 00 00 00 01, 80 00 00 00 00 00 00 00 | ||
u64 | u64 big_endian_authid; | ||
} self_info_data_element; | } self_info_data_element; | ||
== File Body == | == File Body == | ||
Line 709: | Line 626: | ||
| data_size || 0x10 || u64 || | | data_size || 0x10 || u64 || | ||
|- | |- | ||
| flags || 0x18 || u32 || The file type | | flags || 0x18 || u32 || The file type | ||
|- | |- | ||
| padding || 0x1C || u32 || zero | | padding || 0x1C || u32 || zero | ||
Line 718: | Line 635: | ||
=== PackageDigest === | === PackageDigest === | ||
This is SHA1 digest of package without last 32 bytes. | |||
The last | The last 0x20 bytes of the package contains a hash (sha1sum) of the package (sha1 of the whole file minus the last 0x20 bytes). Or in other words... to verify the integrity of a package is needed to crop the 0x20 bytes at the end, then calculate the SHA1 of the resulting file, then compare with the SHA1 that was cropped. | ||
It is also used as part of the package identification info inside the '''TITLE_ID-ver.xml''' files in PSN servers that lists the available game patches for each game. See: [[Online_Connections#Game_Updating_Procedure|Game Updating Procedure]] page and the '''sha1sum''' in the .xml examples | It is also used as part of the package identification info inside the '''TITLE_ID-ver.xml''' files in PSN servers that lists the available game patches for each game. See: [[Online_Connections#Game_Updating_Procedure|Game Updating Procedure]] page and the '''sha1sum''' in the .xml examples | ||
*Notes | *Notes | ||
**This footer area is considered part of the package and is included when counting the '''total_size''' of the package (in the header at offset 0x18), and the header is hashed too by '''header_sha1_hash ''' (at offset 0xB8), this is important in the sequence of actions needed when building the package and updating/adding the | **This footer area is considered part of the package and is included when counting the '''total_size''' of the package (in the header at offset 0x18), and the header is hashed too by '''header_sha1_hash ''' (at offset 0xB8), this is important in the sequence of actions needed when building the package and updating/adding the sha1 footer at the end | ||
** | **Homebrew packages (at least some of them) doesnt includes this footer | ||
== Package Tools == | == Package Tools == | ||
Line 732: | Line 649: | ||
=== PKG builder / infos / extractor === | === PKG builder / infos / extractor === | ||
=== package.conf (PS3/PSVita/PSP) === | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Config name !! usage !! expected value !! example | ||
|- | |- | ||
| | | Content_ID || needed || XXYYYY-XXXXYYYYY_00-XXXXXXXXXXXXXXXX || EP4153-NPEZ00314_00-0000111122223333 | ||
|- | |- | ||
| | | K_licensee (PS3) || needed (can be set to 0x00000...) || 0x00000000000000000000000000000000 || 0x0123456789ABCDEF0123456789ABCDEF | ||
|- | |- | ||
| | | DRMType || needed || Network / Free / Local || Free | ||
|- | |- | ||
| | | ContentType || needed || See [[PKG_files#ContentType]] || minis | ||
|- | |- | ||
| | | PackageVersion || needed || XX.XX || 01.02 | ||
|- | |- | ||
| | | PackageType || || See [[PKG_files#PackageType]] || DiscGamePatch | ||
|- | |- | ||
| | | PackageFlag || || See [[PKG_files#PackageFlag]] || RenameDirectory | ||
|- | |- | ||
| | | StorageType || needed for PSVita gamedata || See [[PKG_files#StorageType]] || VC-MC | ||
|- | |- | ||
| | | TitleID || used only for PSP and PS1 games || XXXXYYYYY || NPEZ00314 | ||
|- | |- | ||
| | | LimitedTimeStart || || YYYY-MM-DDThh:mmTZD || 2018-10-30T11:55T32 | ||
|- | |- | ||
| | | LimitedTimeEnd || || YYYY-MM-DDThh:mmTZD || 2019-10-30T11:55T32 | ||
|- | |- | ||
| | | InstallDirectory || used in PSP patches ?and DLCs? || ? || UCUS98744_LBPPDLCSONYPA002 | ||
|- | |- | ||
| | | ForcedInstallTo || Allows installing packages to any hdd directory (and even flash memory if it's mounted RW). || || | ||
|} | |} | ||
=== NPDRM.CONF (PSP) === | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! Config name !! usage !! expected value !! example | ||
|- | |- | ||
| Content-ID || needed || XXYYYY-XXXXYYYYY_00-XXXXXXXXXXXXXXXX || EP4153-NPEZ00314_00-0000111122223333 | | Content-ID || needed || XXYYYY-XXXXYYYYY_00-XXXXXXXXXXXXXXXX || EP4153-NPEZ00314_00-0000111122223333 | ||
|- | |- | ||
| K-Licensee || needed (can be set to 0x00000...) || 0x00000000000000000000000000000000 || 0x0123456789ABCDEF0123456789ABCDEF | | K-Licensee || needed (can be set to 0x00000...) || 0x00000000000000000000000000000000 || 0x0123456789ABCDEF0123456789ABCDEF | ||
|- | |- | ||
| PackageVersion || needed || XX.XX || 01.02 | | PackageVersion || needed || XX.XX || 01.02 | ||
|- | |- | ||
| ProtectDocument || used in PSP Downloadable Game Package (PSP .pkg) | | ProtectDocument || used in PSP Downloadable Game Package (PSP .pkg) | ||
Line 788: | Line 699: | ||
to purchase the downloadable game package. If "ProtectDocument = FALSE" is specified or | to purchase the downloadable game package. If "ProtectDocument = FALSE" is specified or | ||
if no specification is made, the game manual will not be protected by NPDRM. | if no specification is made, the game manual will not be protected by NPDRM. | ||
|| FALSE/TRUE || FALSE | || FALSE/TRUE || FALSE | ||
|- | |- | ||
| [[PKG_files#HealthWarning|HealthWarning]] || used in PSP Downloadable Game Package (PSP .pkg) | | [[PKG_files#HealthWarning|HealthWarning]] || used in PSP Downloadable Game Package (PSP .pkg) | ||
Line 798: | Line 709: | ||
If "HealthWarning = NONE" is specified or if no specification is made, no startup warning | If "HealthWarning = NONE" is specified or if no specification is made, no startup warning | ||
will be displayed. | will be displayed. | ||
|| NONE/[[PKG_files#HealthWarning|specification word]] || NONE | || NONE/[[PKG_files#HealthWarning|specification word]] || NONE | ||
|- | |- | ||
| ZIPThreshold || used in PSP Downloadable Game Package (PSP .pkg) | | ZIPThreshold || used in PSP Downloadable Game Package (PSP .pkg) | ||
Line 810: | Line 721: | ||
download the package. Unless you specifically need to change the compression rate, do not | download the package. Unless you specifically need to change the compression rate, do not | ||
include this specification. | include this specification. | ||
|| 0 to 100 || 80 | || 0 to 100 || 80 | ||
|- | |- | ||
| OptimizeLevel || used in PSP Downloadable Game Package (PSP .pkg) | | OptimizeLevel || used in PSP Downloadable Game Package (PSP .pkg) | ||
Line 821: | Line 732: | ||
When the authoring service changes, refer to details that are publically available in the | When the authoring service changes, refer to details that are publically available in the | ||
Technical Notes. | Technical Notes. | ||
|| ?0 to 100? || ?80? | || ?0 to 100? || ?80? | ||
|- | |- | ||
| BootablePlatform || used in PSP minis Package (PSP minis .pkg) | | BootablePlatform || used in PSP minis Package (PSP minis .pkg) | ||
Line 827: | Line 738: | ||
games, "BootablePlatform = minis" must be set in NPDRM.CONF that is a configuration file | games, "BootablePlatform = minis" must be set in NPDRM.CONF that is a configuration file | ||
for packaging. | for packaging. | ||
|| minis || minis | || minis || minis | ||
|- | |- | ||
| BootablePlatformDetails || used in PSP minis Package (PSP minis .pkg) | | BootablePlatformDetails || used in PSP minis Package (PSP minis .pkg) | ||
Line 835: | Line 746: | ||
"BootablePlatformDetails=onlyPSP" to NPDRM.CONF in addition to the setting | "BootablePlatformDetails=onlyPSP" to NPDRM.CONF in addition to the setting | ||
"BootablePlatform = minis". | "BootablePlatform = minis". | ||
|| onlyPSP || onlyPSP | || onlyPSP || onlyPSP | ||
|} | |} | ||
==== PSP SDK official tool: make_package_npdrm (2006-2014) ==== | ==== PSP SDK official tool : make_package_npdrm (2006-2014) ==== | ||
* latest version: rev 1642 | * latest version: rev 1642 from 6.60 PSP SDK | ||
==== | ==== PSVita SDK official tool : make_pkg (2011-2015) ==== | ||
* latest version: rev 1972 from 3.55 PSVita SDK | |||
* latest version: rev 1972 | |||
==== PS3 SDK official tool : make_package_npdrm (2006-2017) ==== | |||
make_package_npdrm | |||
* latest version: rev 1972 from 4.75 PS3 SDK | |||
* common version: rev 1732 from 3.40 PS3 SDK (also found under patched form) | |||
* oldest leaked version: rev 1061 from X.XX PS3 SDK | |||
* | |||
* | |||
* | |||
==== Make Package Npdrm Patcher (revision 1732) by SubZero Dezigns (2012) ==== | ==== Make Package Npdrm Patcher (revision 1732) by SubZero Dezigns (2012) ==== | ||
Line 1,022: | Line 855: | ||
==== PkgDecrypt by St4rk (2017) ==== | ==== PkgDecrypt by St4rk (2017) ==== | ||
PSVita .pkg | |||
==== PSN_get_pkg_info.py by windsurfer1122 (2018 | ==== PSN_get_pkg_info.py by windsurfer1122 (2018) ==== | ||
[[PSN_get_pkg_info.py]] Extracts information from package file as text or JSON for further analysis.<br /> | |||
Extracts information from package file as text or JSON for further analysis.<br /> | |||
Provides Linux shell scripts for mass creation of analysis data and to quickly grep through them.<br /> | Provides Linux shell scripts for mass creation of analysis data and to quickly grep through them.<br /> | ||
Analysis data of 10.000+ packages already directly downloadable [https://mega.nz/#F!cyxSVACA!Lp8VFpFz4K-DQLb221d83A here].<br /> | Analysis data of 10.000+ packages already directly downloadable [https://mega.nz/#F!cyxSVACA!Lp8VFpFz4K-DQLb221d83A here].<br /> | ||
Decrypts package to an unencrypted raw package.<br /> | Decrypts package to an unencrypted raw package.<br /> | ||
Planned feature is package extraction like PkgDecrypt and pkg2zip. | Planned feature is package extraction like PkgDecrypt and pkg2zip.<br /> | ||
PS3/PSX/PSP/PSV/PSM .pkg | |||
== Package links == | == Package links == | ||
Line 1,110: | Line 916: | ||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
{{File Formats}}<noinclude>[[Category:Main]]</noinclude> | {{File Formats}}<noinclude>[[Category:Main]]</noinclude> |