diff --git a/README.md b/README.md index edc056f..d837252 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,15 @@ [![Travis CI Build Status][img_travis]][travis] [![AppVeyor Build Status][img_appveyor]][appveyor] [![Downloads][img_downloads]][downloads] [![Release][img_latest]][latest] [![License][img_license]][license] -Utility that decrypts PlayStation Vita pkg file and creates zip package. Supports also PSX pkg files for use with [Adrenaline](https://github.com/TheOfficialFloW/Adrenaline). +Utility that decrypts PlayStation Vita pkg file and creates zip package. Supported pkg files - main application, DLC, patch and PSM files. Supports also PSX files for use with [Adrenaline](https://github.com/TheOfficialFloW/Adrenaline). -Optionally saves [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm) license into work.bin file. You must provide license key. +Optionally writes [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm) or [NoPsmDrm](https://github.com/frangarcj/NoPsmDrm) fake license file from zRIF string. You must provide license key. # Requirements * [Henkaku](https://henkaku.xyz/) / [Enso](https://enso.henkaku.xyz/) * [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm) +* [NoPsmDrm](https://github.com/frangarcj/NoPsmDrm) for PSM titles * to use DLC pkg files, [VitaShell](https://github.com/TheOfficialFloW/VitaShell) **v1.76** or newer required * [Adrenaline](https://github.com/TheOfficialFloW/Adrenaline) for PSX titles @@ -19,12 +20,13 @@ Optionally saves [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm) license i * **small**, has no external library dependencies and uses very minimal dynamic memory allocations. * **fast**, uses AESNI hardware accelerated AES decryption if supported by CPU (requires [AESNI](https://en.wikipedia.org/wiki/AES_instruction_set) and [SSSE3](https://en.wikipedia.org/wiki/SSSE3) instructions). * **simple**, creates zip package with same folder structure that Vita expects (just drag & drop all file from zip archive to ux0:). Zip file is created directly from pkg without any intermediate temporary files. -* **DLC** and **PATCH** pkg unpacking. +* **DLC**, **PATCH** and **PSM** pkg unpacking. * **PSX** pkg unpacking. Limitations: -* currently no PSM, PSP or PSP Mini pkg files are supported. +* currently no PSP or PSP Mini pkg files are supported. +* currently no actual title name is extracted for PSM pkg files. # Usage diff --git a/pkg2zip.c b/pkg2zip.c index d4cb836..fc58de7 100644 --- a/pkg2zip.c +++ b/pkg2zip.c @@ -236,22 +236,25 @@ static void find_psp_sfo(const aes128_key* key, const aes128_key* ps3_key, const static const char* get_region(const char* id) { - if (memcmp(id, "PCSE", 4) == 0 || memcmp(id, "PCSA", 4) == 0) + if (memcmp(id, "PCSE", 4) == 0 || memcmp(id, "PCSA", 4) == 0 || + memcmp(id, "NPNA", 4) == 0) { return "USA"; } - else if (memcmp(id, "PCSF", 4) == 0 || memcmp(id, "PCSB", 4) == 0) + else if (memcmp(id, "PCSF", 4) == 0 || memcmp(id, "PCSB", 4) == 0 || + memcmp(id, "NPOA", 4) == 0) { return "EUR"; } else if (memcmp(id, "PCSC", 4) == 0 || memcmp(id, "VCJS", 4) == 0 || memcmp(id, "PCSG", 4) == 0 || memcmp(id, "VLJS", 4) == 0 || - memcmp(id, "VLJM", 4) == 0) + memcmp(id, "VLJM", 4) == 0 || memcmp(id, "NPPA", 4) == 0) { return "JPN"; } else if (memcmp(id, "VCAS", 4) == 0 || memcmp(id, "PCSH", 4) == 0 || - memcmp(id, "VLAS", 4) == 0 || memcmp(id, "PCSD", 4) == 0) + memcmp(id, "VLAS", 4) == 0 || memcmp(id, "PCSD", 4) == 0 || + memcmp(id, "NPQA", 4) == 0) { return "ASA"; } @@ -434,9 +437,10 @@ int main(int argc, char* argv[]) meta_offset += 2 * sizeof(uint32_t) + size; } - int psx = content_type == 6; - int psp = content_type == 7 || content_type == 0xf; // PSX, PSP, PSPMini - int dlc = content_type == 0x16; // APP + int psx = content_type == 6; // PSX + int psp = content_type == 7 || content_type == 0xf; // PSP, PSPMini + int dlc = content_type == 0x16; // Vita APP + int psm = content_type == 0x18; // Vita PSM aes128_key ps3_key; uint8_t main_key[16]; @@ -475,23 +479,36 @@ int main(int argc, char* argv[]) const char* id = content + 7; const char* id2 = id + 13; - // https://github.com/TheOfficialFloW/NoNpDrm/blob/v1.1/src/main.c#L42 - uint8_t rif[512]; + // first 512 - for vita games - https://github.com/TheOfficialFloW/NoNpDrm/blob/v1.1/src/main.c#L42 + // 1024 is used for PSM + uint8_t rif[1024]; + uint32_t rif_size = 0; if (psp || psx) { find_psp_sfo(&key, &ps3_key, iv, pkg, pkg_size, enc_offset, items_offset, item_count, title); + id = (char*)pkg_header + 0x37; } else { - parse_sfo(pkg, sfo_offset, sfo_size, &patch, title, content, min_version, pkg_version); + if (psm) + { + memcpy(content, pkg_header + 0x30, 0x30); + rif_size = 1024; + } + else + { + parse_sfo(pkg, sfo_offset, sfo_size, &patch, title, content, min_version, pkg_version); + rif_size = 512; + } if (!patch && zrif_arg != NULL) { - zrif_decode(zrif_arg, rif); - if (strncmp((char*)rif + 0x10, content, 0x30) != 0) + zrif_decode(zrif_arg, rif, rif_size); + const char* rif_contentid = (char*)rif + (psm ? 0x50 : 0x10); + if (strncmp(rif_contentid, content, 0x30) != 0) { - fatal("ERROR: zRIF content id '%s' doesn't match pkg '%s'\n", rif + 0x10, content); + fatal("ERROR: zRIF content id '%s' doesn't match pkg '%s'\n", rif_contentid, content); } } } @@ -501,14 +518,12 @@ int main(int argc, char* argv[]) char root[1024]; if (psp) { - id = (char*)pkg_header + 0x37; const char* type_str = content_type == 7 ? "PSP" : "PSPMini"; snprintf(root, sizeof(root), "%s [%.9s] [%s]%s", title, id, type_str, ext); printf("[*] unpacking %s\n", type_str); } else if (psx) { - id = (char*)pkg_header + 0x37; snprintf(root, sizeof(root), "%s [%.9s] [PSX]%s", title, id, ext); printf("[*] unpacking PSX\n"); } @@ -522,6 +537,11 @@ int main(int argc, char* argv[]) snprintf(root, sizeof(root), "%s [%.9s] [%s] [PATCH] [v%s]%s", title, id, get_region(id), pkg_version, ext); printf("[*] unpacking PATCH\n"); } + else if (psm) + { + snprintf(root, sizeof(root), "%.9s [%s]%s", id, get_region(id), ext); + printf("[*] unpacking PSM\n"); + } else { snprintf(root, sizeof(root), "%s [%.9s] [%s]%s", title, id, get_region(id), ext); @@ -583,6 +603,16 @@ int main(int argc, char* argv[]) sys_vstrncat(root, sizeof(root), "/"); } + else if (psm) + { + sys_vstrncat(root, sizeof(root), "psm"); + out_add_folder(root); + + sys_vstrncat(root, sizeof(root), "/%.9s", id); + out_add_folder(root); + + sys_vstrncat(root, sizeof(root), "/"); + } else { sys_vstrncat(root, sizeof(root), "app"); @@ -644,7 +674,17 @@ int main(int argc, char* argv[]) if (flags == 4 || flags == 18) { - if (!psx) + if (psm) + { + // skip "content/" prefix + char* slash = strchr(name, '/'); + if (slash != NULL) + { + snprintf(path, sizeof(path), "%sRO/%s/", root, name + 8); + out_add_folder(path); + } + } + else if (!psx) { snprintf(path, sizeof(path), "%s%s/", root, name); out_add_folder(path); @@ -674,6 +714,11 @@ int main(int argc, char* argv[]) continue; } } + else if (psm) + { + // skip "content/" prefix + snprintf(path, sizeof(path), "%sRO/%s", root, name + 8); + } else { snprintf(path, sizeof(path), "%s%s", root, name); @@ -702,9 +747,9 @@ int main(int argc, char* argv[]) } } - if (!psp && !psx) + if (!psp && !psx && !psm) { - printf("[*] creating head.bin\n"); + printf("[*] creating sce_sys/package/head.bin\n"); snprintf(path, sizeof(path), "%ssce_sys/package/head.bin", root); out_begin_file(path); @@ -722,9 +767,9 @@ int main(int argc, char* argv[]) out_end_file(); } - if (!psp && !psx) + if (!psp && !psx && !psm) { - printf("[*] creating tail.bin\n"); + printf("[*] creating sce_sys/package/tail.bin\n"); snprintf(path, sizeof(path), "%ssce_sys/package/tail.bin", root); out_begin_file(path); @@ -740,9 +785,9 @@ int main(int argc, char* argv[]) out_end_file(); } - if (!psp && !psx) + if (!psp && !psx && !psm) { - printf("[*] creating stat.bin\n"); + printf("[*] creating sce_sys/package/stat.bin\n"); snprintf(path, sizeof(path), "%ssce_sys/package/stat.bin", root); uint8_t stat[768] = { 0 }; @@ -753,17 +798,62 @@ int main(int argc, char* argv[]) if (!psp && !psx && !patch && zrif_arg != NULL) { - printf("[*] creating work.bin\n"); - snprintf(path, sizeof(path), "%ssce_sys/package/work.bin", root); + if (psm) + { + printf("[*] creating RO/License\n"); + snprintf(path, sizeof(path), "%sRO/License", root); + out_add_folder(path); + + printf("[*] creating RO/License/FAKE.rif\n"); + snprintf(path, sizeof(path), "%sRO/License/FAKE.rif", root); + } + else + { + printf("[*] creating sce_sys/package/work.bin\n"); + snprintf(path, sizeof(path), "%ssce_sys/package/work.bin", root); + } out_begin_file(path); - out_write(rif, sizeof(rif)); + out_write(rif, rif_size); + out_end_file(); + } + + if (psm) + { + printf("[*] creating RW\n"); + snprintf(path, sizeof(path), "%sRW", root); + out_add_folder(path); + + printf("[*] creating RW/Documents\n"); + snprintf(path, sizeof(path), "%sRW/Documents", root); + out_add_folder(path); + + printf("[*] creating RW/Temp\n"); + snprintf(path, sizeof(path), "%sRW/Temp", root); + out_add_folder(path); + + printf("[*] creating RW/System\n"); + snprintf(path, sizeof(path), "%sRW/System", root); + out_add_folder(path); + + printf("[*] creating RW/System/content_id\n"); + snprintf(path, sizeof(path), "%sRW/System/content_id", root); + out_begin_file(path); + out_write(pkg_header + 0x30, 0x30); + out_end_file(); + + printf("[*] creating RW/System/pm.dat\n"); + snprintf(path, sizeof(path), "%sRW/System/pm.dat", root); + + uint8_t pm[1 << 16] = { 0 }; + out_begin_file(path); + out_write(pm, sizeof(pm)); out_end_file(); } out_end(); - if (!psp && !psx && !dlc) + if (!psp && !psx && !dlc && !psm) { printf("[*] minimum fw version required: %s\n", min_version); } diff --git a/pkg2zip_zrif.c b/pkg2zip_zrif.c index 11f457f..b674970 100644 --- a/pkg2zip_zrif.c +++ b/pkg2zip_zrif.c @@ -171,17 +171,17 @@ static uint32_t zlib_inflate(const uint8_t* in, uint32_t inlen, uint8_t* out, ui return dlen; } -void zrif_decode(const char* str, uint8_t* rif) +void zrif_decode(const char* str, uint8_t* rif, uint32_t rif_size) { - uint8_t raw[512]; + uint8_t raw[1024]; uint32_t len = base64_decode(str, raw); - uint8_t out[512 + sizeof(zrif_dict)]; + uint8_t out[sizeof(zrif_dict) + 1024]; len = zlib_inflate(raw, len, out, sizeof(out)); - if (len != 512) + if (len != rif_size) { fatal("ERROR: wrong size of zRIF, is it corrupted?\n"); } - memcpy(rif, out, 512); + memcpy(rif, out, rif_size); } diff --git a/pkg2zip_zrif.h b/pkg2zip_zrif.h index 3c1a2e4..893cb73 100644 --- a/pkg2zip_zrif.h +++ b/pkg2zip_zrif.h @@ -2,4 +2,4 @@ #include -void zrif_decode(const char* str, uint8_t* rif); +void zrif_decode(const char* str, uint8_t* rif, uint32_t rif_size);