2017-09-23 10:18:46 -07:00
|
|
|
#include "pkg2zip_aes.h"
|
|
|
|
|
#include "pkg2zip_zip.h"
|
|
|
|
|
#include "pkg2zip_utils.h"
|
2017-09-24 18:17:33 -07:00
|
|
|
#include "pkg2zip_zrif.h"
|
2017-09-23 10:18:46 -07:00
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
2017-09-24 14:38:22 -07:00
|
|
|
#define PKG_HEADER_SIZE 192
|
|
|
|
|
#define PKG_HEADER_EXT_SIZE 64
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-09-24 14:38:22 -07:00
|
|
|
// http://vitadevwiki.com/vita/Packages_(.PKG)#Keys
|
2017-10-28 02:37:41 -07:00
|
|
|
static const uint8_t pkg_ps3_key[] = { 0x2e, 0x7b, 0x71, 0xd7, 0xc9, 0xc9, 0xa1, 0x4e, 0xa3, 0x22, 0x1f, 0x18, 0x88, 0x28, 0xb8, 0xf8 };
|
2017-09-24 14:38:22 -07:00
|
|
|
static const uint8_t pkg_psp_key[] = { 0x07, 0xf2, 0xc6, 0x82, 0x90, 0xb5, 0x0d, 0x2c, 0x33, 0x81, 0x8d, 0x70, 0x9b, 0x60, 0xe6, 0x2b };
|
|
|
|
|
static const uint8_t pkg_vita_2[] = { 0xe3, 0x1a, 0x70, 0xc9, 0xce, 0x1d, 0xd7, 0x2b, 0xf3, 0xc0, 0x62, 0x29, 0x63, 0xf2, 0xec, 0xcb };
|
|
|
|
|
static const uint8_t pkg_vita_3[] = { 0x42, 0x3a, 0xca, 0x3a, 0x2b, 0xd5, 0x64, 0x9f, 0x96, 0x86, 0xab, 0xad, 0x6f, 0xd8, 0x80, 0x1f };
|
|
|
|
|
static const uint8_t pkg_vita_4[] = { 0xaf, 0x07, 0xfd, 0x59, 0x65, 0x25, 0x27, 0xba, 0xf1, 0x33, 0x89, 0x66, 0x8b, 0x17, 0xd9, 0xea };
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-09-24 15:36:24 -07:00
|
|
|
// http://vitadevwiki.com/vita/System_File_Object_(SFO)_(PSF)#Internal_Structure
|
|
|
|
|
// https://github.com/TheOfficialFloW/VitaShell/blob/1.74/sfo.h#L29
|
2017-10-28 02:37:41 -07:00
|
|
|
static void parse_sfo_content(const uint8_t* sfo, uint32_t sfo_size, int* patch, char* title, char* content, char* min_version, char* pkg_version)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-09-24 14:38:22 -07:00
|
|
|
|
|
|
|
|
if (get32le(sfo) != 0x46535000)
|
|
|
|
|
{
|
|
|
|
|
fatal("ERROR: incorrect sfo signature\n");
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
|
2017-09-24 14:38:22 -07:00
|
|
|
uint32_t keys = get32le(sfo + 8);
|
|
|
|
|
uint32_t values = get32le(sfo + 12);
|
|
|
|
|
uint32_t count = get32le(sfo + 16);
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-09-24 14:38:22 -07:00
|
|
|
int title_index = -1;
|
|
|
|
|
int content_index = -1;
|
2017-10-26 01:04:21 -07:00
|
|
|
int category_index = -1;
|
2017-09-28 23:54:23 -07:00
|
|
|
int minver_index = -1;
|
2017-10-26 01:04:21 -07:00
|
|
|
int pkgver_index = -1;
|
|
|
|
|
for (uint32_t i = 0; i < count; i++)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-10-26 01:04:21 -07:00
|
|
|
if (i * 16 + 20 + 2 > sfo_size)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-09-24 14:38:22 -07:00
|
|
|
fatal("ERROR: sfo information is too small\n");
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-26 01:04:21 -07:00
|
|
|
char* key = (char*)sfo + keys + get16le(sfo + i * 16 + 20);
|
2017-09-23 10:18:46 -07:00
|
|
|
if (strcmp(key, "TITLE") == 0)
|
|
|
|
|
{
|
2017-09-24 14:38:22 -07:00
|
|
|
if (title_index < 0)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-09-24 14:38:22 -07:00
|
|
|
title_index = (int)i;
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(key, "STITLE") == 0)
|
|
|
|
|
{
|
2017-09-24 14:38:22 -07:00
|
|
|
title_index = (int)i;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(key, "CONTENT_ID") == 0)
|
|
|
|
|
{
|
|
|
|
|
content_index = (int)i;
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
2017-10-26 01:04:21 -07:00
|
|
|
else if (strcmp(key, "CATEGORY") == 0)
|
|
|
|
|
{
|
|
|
|
|
category_index = (int)i;
|
|
|
|
|
}
|
2017-09-28 23:54:23 -07:00
|
|
|
else if (strcmp(key, "PSP2_DISP_VER") == 0)
|
|
|
|
|
{
|
|
|
|
|
minver_index = (int)i;
|
|
|
|
|
}
|
2017-10-26 01:04:21 -07:00
|
|
|
else if (strcmp(key, "APP_VER") == 0)
|
|
|
|
|
{
|
|
|
|
|
pkgver_index = (int)i;
|
|
|
|
|
}
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-28 02:37:41 -07:00
|
|
|
if (title_index < 0)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-10-28 02:37:41 -07:00
|
|
|
fatal("ERROR: cannot find title from sfo file, pkg is probably corrupted\n");
|
2017-09-24 14:38:22 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-28 02:37:41 -07:00
|
|
|
char* value = (char*)sfo + values + get32le(sfo + title_index * 16 + 20 + 12);
|
2017-09-24 14:38:22 -07:00
|
|
|
size_t i;
|
|
|
|
|
size_t max = 255;
|
2017-10-28 02:37:41 -07:00
|
|
|
for (i = 0; i<max && *value; i++, value++)
|
2017-09-24 14:38:22 -07:00
|
|
|
{
|
2017-09-28 23:49:32 -07:00
|
|
|
if ((*value >= 32 && *value < 127 && strchr("<>\"/\\|?*", *value) == NULL) || (uint8_t)*value >= 128)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-09-24 14:38:22 -07:00
|
|
|
if (*value == ':')
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
|
|
|
|
*title++ = ' ';
|
2017-09-24 14:38:22 -07:00
|
|
|
*title++ = '-';
|
|
|
|
|
max--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*title++ = *value;
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
}
|
2017-09-24 14:38:22 -07:00
|
|
|
else if (*value == 10)
|
|
|
|
|
{
|
|
|
|
|
*title++ = ' ';
|
|
|
|
|
}
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
2017-09-24 14:38:22 -07:00
|
|
|
*title = 0;
|
|
|
|
|
|
2017-10-31 00:56:46 -07:00
|
|
|
if (content_index >= 0 && content)
|
2017-09-24 14:38:22 -07:00
|
|
|
{
|
2017-09-28 23:54:23 -07:00
|
|
|
|
2017-10-28 02:37:41 -07:00
|
|
|
value = (char*)sfo + values + get32le(sfo + content_index * 16 + 20 + 12);
|
|
|
|
|
while (*value)
|
|
|
|
|
{
|
|
|
|
|
*content++ = *value++;
|
|
|
|
|
}
|
|
|
|
|
*content = 0;
|
2017-10-26 01:04:21 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-31 00:56:46 -07:00
|
|
|
if (category_index >= 0 && patch)
|
2017-09-28 23:54:23 -07:00
|
|
|
{
|
2017-10-28 02:37:41 -07:00
|
|
|
char* category = value = (char*)sfo + values + get32le(sfo + category_index * 16 + 20 + 12);
|
|
|
|
|
while (*value++)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
*value = 0;
|
|
|
|
|
|
|
|
|
|
if (strcmp(category, "gp") == 0)
|
|
|
|
|
{
|
|
|
|
|
*patch = 1;
|
|
|
|
|
}
|
2017-09-28 23:54:23 -07:00
|
|
|
}
|
2017-10-26 01:04:21 -07:00
|
|
|
|
2017-10-31 00:56:46 -07:00
|
|
|
if (minver_index >= 0 && min_version)
|
2017-10-26 01:04:21 -07:00
|
|
|
{
|
2017-10-28 02:37:41 -07:00
|
|
|
value = (char*)sfo + values + get32le(sfo + minver_index * 16 + 20 + 12);
|
|
|
|
|
if (*value == '0')
|
|
|
|
|
{
|
|
|
|
|
value++;
|
|
|
|
|
}
|
|
|
|
|
while (*value)
|
|
|
|
|
{
|
|
|
|
|
*min_version++ = *value++;
|
|
|
|
|
}
|
|
|
|
|
if (min_version[-1] == '0')
|
|
|
|
|
{
|
|
|
|
|
min_version[-1] = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*min_version = 0;
|
|
|
|
|
}
|
2017-10-26 01:04:21 -07:00
|
|
|
}
|
2017-10-28 02:37:41 -07:00
|
|
|
|
2017-10-31 00:56:46 -07:00
|
|
|
if (pkgver_index >= 0 && pkg_version)
|
2017-10-26 01:04:21 -07:00
|
|
|
{
|
2017-10-28 02:37:41 -07:00
|
|
|
value = (char*)sfo + values + get32le(sfo + pkgver_index * 16 + 20 + 12);
|
|
|
|
|
if (*value == '0')
|
|
|
|
|
{
|
|
|
|
|
value++;
|
|
|
|
|
}
|
|
|
|
|
while (*value)
|
|
|
|
|
{
|
|
|
|
|
*pkg_version++ = *value++;
|
|
|
|
|
}
|
|
|
|
|
*pkg_version = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void parse_sfo(sys_file f, uint64_t sfo_offset, uint32_t sfo_size, int* patch, char* title, char* content, char* min_version, char* pkg_version)
|
|
|
|
|
{
|
|
|
|
|
uint8_t sfo[16 * 1024];
|
|
|
|
|
if (sfo_size < 16)
|
|
|
|
|
{
|
|
|
|
|
fatal("ERROR: sfo information is too small\n");
|
|
|
|
|
}
|
|
|
|
|
if (sfo_size > sizeof(sfo))
|
|
|
|
|
{
|
|
|
|
|
fatal("ERROR: sfo information is too big, pkg file is probably corrupted\n");
|
|
|
|
|
}
|
|
|
|
|
sys_read(f, sfo_offset, sfo, sfo_size);
|
|
|
|
|
|
|
|
|
|
parse_sfo_content(sfo, sfo_size, patch, title, content, min_version, pkg_version);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void find_psp_sfo(const aes128_key* key, const aes128_key* ps3_key, const uint8_t* iv, sys_file pkg, uint64_t pkg_size, uint64_t enc_offset, uint64_t items_offset, uint32_t item_count, char* title)
|
|
|
|
|
{
|
|
|
|
|
for (uint32_t item_index = 0; item_index < item_count; item_index++)
|
|
|
|
|
{
|
|
|
|
|
uint8_t item[32];
|
|
|
|
|
uint64_t item_offset = items_offset + item_index * 32;
|
|
|
|
|
sys_read(pkg, enc_offset + item_offset, item, sizeof(item));
|
|
|
|
|
aes128_ctr_xor(key, iv, item_offset / 16, item, sizeof(item));
|
|
|
|
|
|
|
|
|
|
uint32_t name_offset = get32be(item + 0);
|
|
|
|
|
uint32_t name_size = get32be(item + 4);
|
|
|
|
|
uint64_t data_offset = get64be(item + 8);
|
|
|
|
|
uint64_t data_size = get64be(item + 16);
|
|
|
|
|
uint8_t psp_type = item[24];
|
|
|
|
|
|
|
|
|
|
assert(name_offset % 16 == 0);
|
|
|
|
|
assert(data_offset % 16 == 0);
|
|
|
|
|
|
|
|
|
|
if (pkg_size < enc_offset + name_offset + name_size ||
|
|
|
|
|
pkg_size < enc_offset + data_offset + data_size)
|
|
|
|
|
{
|
|
|
|
|
fatal("ERROR: pkg file is too short, possible corrupted\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const aes128_key* item_key = psp_type == 0x90 ? key : ps3_key;
|
|
|
|
|
|
|
|
|
|
char name[ZIP_MAX_FILENAME];
|
|
|
|
|
sys_read(pkg, enc_offset + name_offset, name, name_size);
|
|
|
|
|
aes128_ctr_xor(item_key, iv, name_offset / 16, (uint8_t*)name, name_size);
|
|
|
|
|
name[name_size] = 0;
|
|
|
|
|
|
|
|
|
|
if (strcmp(name, "PARAM.SFO") == 0)
|
|
|
|
|
{
|
|
|
|
|
uint8_t sfo[16 * 1024];
|
|
|
|
|
if (data_size < 16)
|
|
|
|
|
{
|
|
|
|
|
fatal("ERROR: sfo information is too small\n");
|
|
|
|
|
}
|
|
|
|
|
if (data_size > sizeof(sfo))
|
|
|
|
|
{
|
|
|
|
|
fatal("ERROR: sfo information is too big, pkg file is probably corrupted\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sys_read(pkg, enc_offset + data_offset, sfo, (uint32_t)data_size);
|
|
|
|
|
aes128_ctr_xor(item_key, iv, data_offset / 16, sfo, data_size);
|
|
|
|
|
|
|
|
|
|
parse_sfo_content(sfo, (uint32_t)data_size, NULL, title, NULL, NULL, NULL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-10-26 01:04:21 -07:00
|
|
|
}
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char* get_region(const char* id)
|
|
|
|
|
{
|
2017-10-31 00:28:00 -07:00
|
|
|
if (memcmp(id, "PCSE", 4) == 0 || memcmp(id, "PCSA", 4) == 0 ||
|
|
|
|
|
memcmp(id, "NPNA", 4) == 0)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
|
|
|
|
return "USA";
|
|
|
|
|
}
|
2017-10-31 00:28:00 -07:00
|
|
|
else if (memcmp(id, "PCSF", 4) == 0 || memcmp(id, "PCSB", 4) == 0 ||
|
|
|
|
|
memcmp(id, "NPOA", 4) == 0)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
|
|
|
|
return "EUR";
|
|
|
|
|
}
|
|
|
|
|
else if (memcmp(id, "PCSC", 4) == 0 || memcmp(id, "VCJS", 4) == 0 ||
|
|
|
|
|
memcmp(id, "PCSG", 4) == 0 || memcmp(id, "VLJS", 4) == 0 ||
|
2017-10-31 00:28:00 -07:00
|
|
|
memcmp(id, "VLJM", 4) == 0 || memcmp(id, "NPPA", 4) == 0)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
|
|
|
|
return "JPN";
|
|
|
|
|
}
|
|
|
|
|
else if (memcmp(id, "VCAS", 4) == 0 || memcmp(id, "PCSH", 4) == 0 ||
|
2017-10-31 00:28:00 -07:00
|
|
|
memcmp(id, "VLAS", 4) == 0 || memcmp(id, "PCSD", 4) == 0 ||
|
|
|
|
|
memcmp(id, "NPQA", 4) == 0)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
|
|
|
|
return "ASA";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return "unknown region";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
static zip out_zip;
|
|
|
|
|
static int out_zipped;
|
|
|
|
|
static sys_file out_file;
|
|
|
|
|
static uint64_t out_file_offset;
|
|
|
|
|
|
|
|
|
|
static void out_begin(const char* name, int zipped)
|
|
|
|
|
{
|
|
|
|
|
if (zipped)
|
|
|
|
|
{
|
|
|
|
|
zip_create(&out_zip, name);
|
|
|
|
|
}
|
|
|
|
|
out_zipped = zipped;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void out_end(void)
|
|
|
|
|
{
|
|
|
|
|
if (out_zipped)
|
|
|
|
|
{
|
|
|
|
|
zip_close(&out_zip);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void out_add_folder(const char* path)
|
|
|
|
|
{
|
|
|
|
|
if (out_zipped)
|
|
|
|
|
{
|
|
|
|
|
zip_add_folder(&out_zip, path);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sys_mkdir(path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void out_begin_file(const char* name)
|
|
|
|
|
{
|
|
|
|
|
if (out_zipped)
|
|
|
|
|
{
|
|
|
|
|
zip_begin_file(&out_zip, name);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
out_file = sys_create(name);
|
|
|
|
|
out_file_offset = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void out_end_file(void)
|
|
|
|
|
{
|
|
|
|
|
if (out_zipped)
|
|
|
|
|
{
|
|
|
|
|
zip_end_file(&out_zip);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sys_close(out_file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void out_write(const void* buffer, uint32_t size)
|
|
|
|
|
{
|
|
|
|
|
if (out_zipped)
|
|
|
|
|
{
|
|
|
|
|
zip_write_file(&out_zip, buffer, size);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sys_write(out_file, out_file_offset, buffer, size);
|
|
|
|
|
out_file_offset += size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-31 00:50:23 -07:00
|
|
|
typedef enum {
|
|
|
|
|
PKG_TYPE_VITA_APP,
|
|
|
|
|
PKG_TYPE_VITA_DLC,
|
|
|
|
|
PKG_TYPE_VITA_PATCH,
|
|
|
|
|
PKG_TYPE_VITA_PSM,
|
|
|
|
|
PKG_TYPE_PSP,
|
|
|
|
|
PKG_TYPE_PSX,
|
|
|
|
|
} pkg_type;
|
|
|
|
|
|
2017-09-23 10:18:46 -07:00
|
|
|
int main(int argc, char* argv[])
|
|
|
|
|
{
|
2017-10-31 23:33:40 -07:00
|
|
|
printf("pkg2zip v1.7\n");
|
2017-10-30 23:15:25 -07:00
|
|
|
|
|
|
|
|
int zipped = 1;
|
|
|
|
|
const char* pkg_arg = NULL;
|
|
|
|
|
const char* zrif_arg = NULL;
|
|
|
|
|
for (int i = 1; i < argc; i++)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-10-30 23:15:25 -07:00
|
|
|
if (strcmp(argv[i], "-x") == 0)
|
|
|
|
|
{
|
|
|
|
|
zipped = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (pkg_arg != NULL)
|
|
|
|
|
{
|
|
|
|
|
zrif_arg = argv[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pkg_arg = argv[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pkg_arg == NULL)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "ERROR: no pkg file specified\n");
|
|
|
|
|
fatal("Usage: %s [-x] file.pkg [zRIF]\n", argv[0]);
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("[*] loading...\n");
|
|
|
|
|
|
|
|
|
|
uint64_t pkg_size;
|
2017-10-30 23:15:25 -07:00
|
|
|
sys_file pkg = sys_open(pkg_arg, &pkg_size);
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-09-24 14:38:22 -07:00
|
|
|
uint8_t pkg_header[PKG_HEADER_SIZE + PKG_HEADER_EXT_SIZE];
|
2017-09-23 10:18:46 -07:00
|
|
|
sys_read(pkg, 0, pkg_header, sizeof(pkg_header));
|
|
|
|
|
|
2017-09-24 14:38:22 -07:00
|
|
|
if (get32be(pkg_header) != 0x7f504b47 || get32be(pkg_header + PKG_HEADER_SIZE) != 0x7F657874)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-09-24 14:38:22 -07:00
|
|
|
fatal("ERROR: not a pkg file\n");
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
|
2017-09-24 14:38:22 -07:00
|
|
|
// http://www.psdevwiki.com/ps3/PKG_files
|
|
|
|
|
uint64_t meta_offset = get32be(pkg_header + 8);
|
|
|
|
|
uint32_t meta_count = get32be(pkg_header + 12);
|
2017-09-23 10:18:46 -07:00
|
|
|
uint32_t item_count = get32be(pkg_header + 20);
|
2017-09-24 14:38:22 -07:00
|
|
|
uint64_t total_size = get64be(pkg_header + 24);
|
2017-09-23 10:18:46 -07:00
|
|
|
uint64_t enc_offset = get64be(pkg_header + 32);
|
2017-10-27 00:53:40 -07:00
|
|
|
uint64_t enc_size = get64be(pkg_header + 40);
|
2017-09-24 14:38:22 -07:00
|
|
|
const uint8_t* iv = pkg_header + 0x70;
|
|
|
|
|
int key_type = pkg_header[0xe7] & 7;
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-09-24 14:38:22 -07:00
|
|
|
if (pkg_size < total_size)
|
|
|
|
|
{
|
|
|
|
|
fatal("ERROR: pkg file is too small\n");
|
|
|
|
|
}
|
2017-09-23 10:18:46 -07:00
|
|
|
if (pkg_size < enc_offset + item_count * 32)
|
|
|
|
|
{
|
2017-09-24 14:38:22 -07:00
|
|
|
fatal("ERROR: pkg file is too small\n");
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
|
2017-09-24 14:38:22 -07:00
|
|
|
uint32_t content_type = 0;
|
|
|
|
|
uint32_t sfo_offset = 0;
|
|
|
|
|
uint32_t sfo_size = 0;
|
|
|
|
|
uint32_t items_offset = 0;
|
|
|
|
|
uint32_t items_size = 0;
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < meta_count; i++)
|
|
|
|
|
{
|
|
|
|
|
uint8_t block[16];
|
|
|
|
|
sys_read(pkg, meta_offset, block, sizeof(block));
|
|
|
|
|
|
|
|
|
|
uint32_t type = get32be(block + 0);
|
|
|
|
|
uint32_t size = get32be(block + 4);
|
|
|
|
|
|
2017-10-26 00:09:15 -07:00
|
|
|
if (type == 2)
|
2017-09-24 14:38:22 -07:00
|
|
|
{
|
|
|
|
|
content_type = get32be(block + 8);
|
|
|
|
|
}
|
|
|
|
|
else if (type == 13)
|
|
|
|
|
{
|
|
|
|
|
items_offset = get32be(block + 8);
|
|
|
|
|
items_size = get32be(block + 12);
|
|
|
|
|
}
|
|
|
|
|
else if (type == 14)
|
|
|
|
|
{
|
|
|
|
|
sfo_offset = get32be(block + 8);
|
|
|
|
|
sfo_size = get32be(block + 12);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
meta_offset += 2 * sizeof(uint32_t) + size;
|
|
|
|
|
}
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-10-31 00:50:23 -07:00
|
|
|
pkg_type type;
|
|
|
|
|
|
|
|
|
|
if (content_type == 6)
|
|
|
|
|
{
|
|
|
|
|
type = PKG_TYPE_PSX;
|
|
|
|
|
}
|
|
|
|
|
else if (content_type == 7 || content_type == 0xf)
|
|
|
|
|
{
|
|
|
|
|
// PSP or PSPMini
|
|
|
|
|
type = PKG_TYPE_PSP;
|
|
|
|
|
}
|
|
|
|
|
else if (content_type == 0x15)
|
|
|
|
|
{
|
|
|
|
|
type = PKG_TYPE_VITA_APP;
|
|
|
|
|
}
|
|
|
|
|
else if (content_type == 0x16)
|
|
|
|
|
{
|
|
|
|
|
type = PKG_TYPE_VITA_DLC;
|
|
|
|
|
}
|
|
|
|
|
else if (content_type == 0x18)
|
|
|
|
|
{
|
|
|
|
|
type = PKG_TYPE_VITA_PSM;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fatal("ERROR: unsupported content type 0x%x", content_type);
|
|
|
|
|
}
|
2017-09-24 15:36:24 -07:00
|
|
|
|
2017-10-28 02:37:41 -07:00
|
|
|
aes128_key ps3_key;
|
2017-09-23 10:18:46 -07:00
|
|
|
uint8_t main_key[16];
|
|
|
|
|
if (key_type == 1)
|
|
|
|
|
{
|
|
|
|
|
memcpy(main_key, pkg_psp_key, sizeof(main_key));
|
2017-10-28 02:37:41 -07:00
|
|
|
aes128_init(&ps3_key, pkg_ps3_key);
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
else if (key_type == 2)
|
|
|
|
|
{
|
|
|
|
|
aes128_key key;
|
|
|
|
|
aes128_init(&key, pkg_vita_2);
|
|
|
|
|
aes128_ecb_encrypt(&key, iv, main_key);
|
|
|
|
|
}
|
|
|
|
|
else if (key_type == 3)
|
|
|
|
|
{
|
|
|
|
|
aes128_key key;
|
|
|
|
|
aes128_init(&key, pkg_vita_3);
|
|
|
|
|
aes128_ecb_encrypt(&key, iv, main_key);
|
|
|
|
|
}
|
|
|
|
|
else if (key_type == 4)
|
|
|
|
|
{
|
|
|
|
|
aes128_key key;
|
|
|
|
|
aes128_init(&key, pkg_vita_4);
|
|
|
|
|
aes128_ecb_encrypt(&key, iv, main_key);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-28 02:37:41 -07:00
|
|
|
aes128_key key;
|
|
|
|
|
aes128_init(&key, main_key);
|
|
|
|
|
|
2017-09-24 14:38:22 -07:00
|
|
|
char content[256];
|
|
|
|
|
char title[256];
|
2017-09-28 23:54:23 -07:00
|
|
|
char min_version[256];
|
2017-10-26 01:04:21 -07:00
|
|
|
char pkg_version[256];
|
2017-09-24 15:36:24 -07:00
|
|
|
const char* id = content + 7;
|
|
|
|
|
const char* id2 = id + 13;
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-10-31 00:28:00 -07:00
|
|
|
// 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;
|
2017-10-28 02:37:41 -07:00
|
|
|
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_PSP || type == PKG_TYPE_PSX)
|
2017-09-24 23:24:59 -07:00
|
|
|
{
|
2017-10-28 02:37:41 -07:00
|
|
|
find_psp_sfo(&key, &ps3_key, iv, pkg, pkg_size, enc_offset, items_offset, item_count, title);
|
2017-10-31 00:28:00 -07:00
|
|
|
id = (char*)pkg_header + 0x37;
|
2017-10-28 02:37:41 -07:00
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else // Vita
|
2017-10-28 02:37:41 -07:00
|
|
|
{
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_VITA_PSM)
|
2017-10-31 00:28:00 -07:00
|
|
|
{
|
|
|
|
|
memcpy(content, pkg_header + 0x30, 0x30);
|
|
|
|
|
rif_size = 1024;
|
|
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else // Vita APP, DLC or PATCH
|
2017-10-31 00:28:00 -07:00
|
|
|
{
|
2017-10-31 13:38:34 -07:00
|
|
|
int patch = 0;
|
2017-10-31 00:28:00 -07:00
|
|
|
parse_sfo(pkg, sfo_offset, sfo_size, &patch, title, content, min_version, pkg_version);
|
|
|
|
|
rif_size = 512;
|
2017-10-31 00:50:23 -07:00
|
|
|
|
|
|
|
|
if (patch && type == PKG_TYPE_VITA_APP)
|
|
|
|
|
{
|
|
|
|
|
type = PKG_TYPE_VITA_PATCH;
|
|
|
|
|
}
|
2017-10-31 00:28:00 -07:00
|
|
|
}
|
2017-10-28 02:37:41 -07:00
|
|
|
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type != PKG_TYPE_VITA_PATCH && zrif_arg != NULL)
|
2017-09-24 23:24:59 -07:00
|
|
|
{
|
2017-10-31 00:28:00 -07:00
|
|
|
zrif_decode(zrif_arg, rif, rif_size);
|
2017-10-31 00:50:23 -07:00
|
|
|
const char* rif_contentid = (char*)rif + (type == PKG_TYPE_VITA_PSM ? 0x50 : 0x10);
|
2017-10-31 00:28:00 -07:00
|
|
|
if (strncmp(rif_contentid, content, 0x30) != 0)
|
2017-10-28 02:37:41 -07:00
|
|
|
{
|
2017-10-31 00:28:00 -07:00
|
|
|
fatal("ERROR: zRIF content id '%s' doesn't match pkg '%s'\n", rif_contentid, content);
|
2017-10-28 02:37:41 -07:00
|
|
|
}
|
2017-09-24 23:24:59 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
const char* ext = zipped ? ".zip" : "";
|
|
|
|
|
|
|
|
|
|
char root[1024];
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_PSP)
|
2017-10-28 02:37:41 -07:00
|
|
|
{
|
2017-10-30 22:03:31 -07:00
|
|
|
const char* type_str = content_type == 7 ? "PSP" : "PSPMini";
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(root, sizeof(root), "%s [%.9s] [%s]%s", title, id, type_str, ext);
|
2017-10-28 12:23:16 -07:00
|
|
|
printf("[*] unpacking %s\n", type_str);
|
2017-10-28 02:37:41 -07:00
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_PSX)
|
2017-10-30 22:03:31 -07:00
|
|
|
{
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(root, sizeof(root), "%s [%.9s] [PSX]%s", title, id, ext);
|
2017-10-30 22:03:31 -07:00
|
|
|
printf("[*] unpacking PSX\n");
|
|
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_VITA_DLC)
|
2017-09-24 15:47:00 -07:00
|
|
|
{
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(root, sizeof(root), "%s [%.9s] [%s] [DLC-%s]%s", title, id, get_region(id), id2, ext);
|
2017-10-26 00:09:15 -07:00
|
|
|
printf("[*] unpacking DLC\n");
|
2017-09-24 15:47:00 -07:00
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_VITA_PATCH)
|
2017-10-26 01:04:21 -07:00
|
|
|
{
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(root, sizeof(root), "%s [%.9s] [%s] [PATCH] [v%s]%s", title, id, get_region(id), pkg_version, ext);
|
2017-10-26 01:04:21 -07:00
|
|
|
printf("[*] unpacking PATCH\n");
|
|
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_VITA_PSM)
|
2017-10-31 00:28:00 -07:00
|
|
|
{
|
|
|
|
|
snprintf(root, sizeof(root), "%.9s [%s]%s", id, get_region(id), ext);
|
|
|
|
|
printf("[*] unpacking PSM\n");
|
|
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_VITA_APP)
|
2017-09-24 15:47:00 -07:00
|
|
|
{
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(root, sizeof(root), "%s [%.9s] [%s]%s", title, id, get_region(id), ext);
|
2017-10-26 00:09:15 -07:00
|
|
|
printf("[*] unpacking APP\n");
|
2017-09-24 15:47:00 -07:00
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
assert(0);
|
|
|
|
|
fatal("ERROR: unsupported type");
|
|
|
|
|
}
|
2017-10-26 00:09:15 -07:00
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
out_begin(root, zipped);
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
if (zipped)
|
2017-10-28 02:37:41 -07:00
|
|
|
{
|
2017-10-30 23:15:25 -07:00
|
|
|
printf("[*] creating '%s' archive\n", root);
|
2017-10-30 22:03:31 -07:00
|
|
|
root[0] = 0;
|
|
|
|
|
}
|
2017-09-24 15:36:24 -07:00
|
|
|
else
|
|
|
|
|
{
|
2017-10-30 23:15:25 -07:00
|
|
|
printf("[*] output to '%s' folder\n", root);
|
2017-10-31 15:40:32 -07:00
|
|
|
if (type == PKG_TYPE_PSP)
|
|
|
|
|
{
|
|
|
|
|
// until PSP support is done
|
|
|
|
|
sys_mkdir(root);
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
root[0] = 0;
|
|
|
|
|
}
|
2017-10-30 23:15:25 -07:00
|
|
|
}
|
2017-09-24 15:36:24 -07:00
|
|
|
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_PSP)
|
2017-10-30 23:15:25 -07:00
|
|
|
{
|
|
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_PSX)
|
2017-10-30 23:15:25 -07:00
|
|
|
{
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "pspemu");
|
|
|
|
|
out_add_folder(root);
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
sys_vstrncat(root, sizeof(root), "/PSP");
|
|
|
|
|
out_add_folder(root);
|
|
|
|
|
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/GAME");
|
|
|
|
|
out_add_folder(root);
|
|
|
|
|
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/%.9s", id);
|
|
|
|
|
out_add_folder(root);
|
|
|
|
|
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/");
|
|
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_VITA_DLC)
|
2017-10-30 23:15:25 -07:00
|
|
|
{
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "addcont");
|
|
|
|
|
out_add_folder(root);
|
|
|
|
|
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/%.9s", id);
|
|
|
|
|
out_add_folder(root);
|
|
|
|
|
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/%s", id2);
|
|
|
|
|
out_add_folder(root);
|
|
|
|
|
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/");
|
|
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_VITA_PATCH)
|
2017-10-30 23:15:25 -07:00
|
|
|
{
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "patch");
|
|
|
|
|
out_add_folder(root);
|
|
|
|
|
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/%.9s", id);
|
|
|
|
|
out_add_folder(root);
|
|
|
|
|
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/");
|
|
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_VITA_PSM)
|
2017-10-31 00:28:00 -07:00
|
|
|
{
|
|
|
|
|
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), "/");
|
|
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_VITA_APP)
|
2017-10-30 23:15:25 -07:00
|
|
|
{
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "app");
|
|
|
|
|
out_add_folder(root);
|
|
|
|
|
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/%.9s", id);
|
|
|
|
|
out_add_folder(root);
|
|
|
|
|
|
|
|
|
|
sys_vstrncat(root, sizeof(root), "/");
|
2017-10-26 00:09:15 -07:00
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
assert(0);
|
|
|
|
|
fatal("ERROR: unsupported type");
|
|
|
|
|
}
|
2017-10-26 00:09:15 -07:00
|
|
|
|
2017-09-23 10:18:46 -07:00
|
|
|
printf("[*] decrypting...\n");
|
2017-10-30 23:15:25 -07:00
|
|
|
char path[1024];
|
2017-09-24 14:38:22 -07:00
|
|
|
|
2017-10-31 23:32:04 -07:00
|
|
|
int sce_sys_package_created = 0;
|
|
|
|
|
|
2017-10-26 00:09:15 -07:00
|
|
|
for (uint32_t item_index = 0; item_index < item_count; item_index++)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-09-24 14:38:22 -07:00
|
|
|
uint8_t item[32];
|
2017-09-26 09:47:41 -07:00
|
|
|
uint64_t item_offset = items_offset + item_index * 32;
|
|
|
|
|
sys_read(pkg, enc_offset + item_offset, item, sizeof(item));
|
2017-10-26 00:09:15 -07:00
|
|
|
aes128_ctr_xor(&key, iv, item_offset / 16, item, sizeof(item));
|
2017-09-23 10:18:46 -07:00
|
|
|
|
|
|
|
|
uint32_t name_offset = get32be(item + 0);
|
|
|
|
|
uint32_t name_size = get32be(item + 4);
|
|
|
|
|
uint64_t data_offset = get64be(item + 8);
|
|
|
|
|
uint64_t data_size = get64be(item + 16);
|
2017-10-28 02:37:41 -07:00
|
|
|
uint8_t psp_type = item[24];
|
2017-09-23 10:18:46 -07:00
|
|
|
uint8_t flags = item[27];
|
|
|
|
|
|
|
|
|
|
assert(name_offset % 16 == 0);
|
|
|
|
|
assert(data_offset % 16 == 0);
|
|
|
|
|
|
|
|
|
|
if (pkg_size < enc_offset + name_offset + name_size ||
|
|
|
|
|
pkg_size < enc_offset + data_offset + data_size)
|
|
|
|
|
{
|
2017-09-24 15:36:24 -07:00
|
|
|
fatal("ERROR: pkg file is too short, possible corrupted\n");
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-26 01:47:07 -07:00
|
|
|
if (name_size >= ZIP_MAX_FILENAME)
|
|
|
|
|
{
|
|
|
|
|
fatal("ERROR: pkg file contains file with very long name\n");
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-30 22:03:31 -07:00
|
|
|
const aes128_key* item_key;
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_PSP || type == PKG_TYPE_PSX)
|
2017-10-30 22:03:31 -07:00
|
|
|
{
|
|
|
|
|
item_key = psp_type == 0x90 ? &key : &ps3_key;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
item_key = &key;
|
|
|
|
|
}
|
2017-10-28 02:37:41 -07:00
|
|
|
|
2017-09-23 10:18:46 -07:00
|
|
|
char name[ZIP_MAX_FILENAME];
|
|
|
|
|
sys_read(pkg, enc_offset + name_offset, name, name_size);
|
2017-10-28 02:37:41 -07:00
|
|
|
aes128_ctr_xor(item_key, iv, name_offset / 16, (uint8_t*)name, name_size);
|
2017-09-23 10:18:46 -07:00
|
|
|
name[name_size] = 0;
|
|
|
|
|
|
2017-10-26 00:09:15 -07:00
|
|
|
printf("[%u/%u] %s\n", item_index + 1, item_count, name);
|
2017-09-23 10:18:46 -07:00
|
|
|
|
|
|
|
|
if (flags == 4 || flags == 18)
|
|
|
|
|
{
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_VITA_PSM)
|
2017-10-31 00:28:00 -07:00
|
|
|
{
|
|
|
|
|
// skip "content/" prefix
|
|
|
|
|
char* slash = strchr(name, '/');
|
|
|
|
|
if (slash != NULL)
|
|
|
|
|
{
|
2017-10-31 23:32:04 -07:00
|
|
|
snprintf(path, sizeof(path), "%sRO/%s", root, name + 8);
|
2017-10-31 00:28:00 -07:00
|
|
|
out_add_folder(path);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-31 00:56:46 -07:00
|
|
|
else if (type != PKG_TYPE_PSX)
|
2017-10-30 22:03:31 -07:00
|
|
|
{
|
2017-10-31 23:32:04 -07:00
|
|
|
snprintf(path, sizeof(path), "%s%s", root, name);
|
2017-10-30 23:15:25 -07:00
|
|
|
out_add_folder(path);
|
2017-10-31 23:32:04 -07:00
|
|
|
|
|
|
|
|
if (strcmp("sce_sys/package", name) == 0)
|
|
|
|
|
{
|
|
|
|
|
sce_sys_package_created = 1;
|
|
|
|
|
}
|
2017-10-30 22:03:31 -07:00
|
|
|
}
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
2017-10-26 00:09:15 -07:00
|
|
|
else
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-10-26 01:04:21 -07:00
|
|
|
int decrypt = 1;
|
2017-10-31 23:32:04 -07:00
|
|
|
if ((type == PKG_TYPE_VITA_APP || type == PKG_TYPE_VITA_DLC || type == PKG_TYPE_VITA_PATCH) && strcmp("sce_sys/package/digs.bin", name) == 0)
|
2017-10-26 01:04:21 -07:00
|
|
|
{
|
2017-10-31 00:56:46 -07:00
|
|
|
// TODO: is this really needed?
|
2017-10-31 23:32:04 -07:00
|
|
|
if (!sce_sys_package_created)
|
|
|
|
|
{
|
|
|
|
|
snprintf(path, sizeof(path), "%ssce_sys/package", root);
|
|
|
|
|
out_add_folder(path);
|
|
|
|
|
|
|
|
|
|
sce_sys_package_created = 1;
|
|
|
|
|
}
|
2017-10-26 01:04:21 -07:00
|
|
|
snprintf(name, sizeof(name), "%s", "sce_sys/package/body.bin");
|
|
|
|
|
decrypt = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_PSX)
|
2017-10-30 22:03:31 -07:00
|
|
|
{
|
|
|
|
|
if (strcmp("USRDIR/CONTENT/DOCUMENT.DAT", name) == 0)
|
|
|
|
|
{
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(path, sizeof(path), "%sDOCUMENT.DAT", root);
|
2017-10-30 22:03:31 -07:00
|
|
|
}
|
|
|
|
|
else if (strcmp("USRDIR/CONTENT/EBOOT.PBP", name) == 0)
|
|
|
|
|
{
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(path, sizeof(path), "%sEBOOT.PBP", root);
|
2017-10-30 22:03:31 -07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-31 00:50:23 -07:00
|
|
|
else if (type == PKG_TYPE_VITA_PSM)
|
2017-10-31 00:28:00 -07:00
|
|
|
{
|
|
|
|
|
// skip "content/" prefix
|
|
|
|
|
snprintf(path, sizeof(path), "%sRO/%s", root, name + 8);
|
|
|
|
|
}
|
2017-10-30 22:03:31 -07:00
|
|
|
else
|
|
|
|
|
{
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(path, sizeof(path), "%s%s", root, name);
|
2017-10-30 22:03:31 -07:00
|
|
|
}
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-09-24 15:36:24 -07:00
|
|
|
uint64_t offset = data_offset;
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
out_begin_file(path);
|
2017-09-24 15:36:24 -07:00
|
|
|
while (data_size != 0)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buffer[1 << 16];
|
|
|
|
|
uint32_t size = (uint32_t)min64(data_size, sizeof(buffer));
|
|
|
|
|
sys_read(pkg, enc_offset + offset, buffer, size);
|
|
|
|
|
|
2017-10-26 00:09:15 -07:00
|
|
|
if (decrypt)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-10-28 02:37:41 -07:00
|
|
|
aes128_ctr_xor(item_key, iv, offset / 16, buffer, size);
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
2017-10-26 00:09:15 -07:00
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
out_write(buffer, size);
|
2017-09-24 15:36:24 -07:00
|
|
|
offset += size;
|
|
|
|
|
data_size -= size;
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
2017-09-24 15:36:24 -07:00
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
out_end_file();
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_VITA_APP || type == PKG_TYPE_VITA_DLC || type == PKG_TYPE_VITA_PATCH)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-10-31 23:32:04 -07:00
|
|
|
if (!sce_sys_package_created)
|
2017-10-31 00:56:46 -07:00
|
|
|
{
|
|
|
|
|
printf("[*] creating sce_sys/package\n");
|
|
|
|
|
snprintf(path, sizeof(path), "%ssce_sys/package", root);
|
|
|
|
|
out_add_folder(path);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-31 00:28:00 -07:00
|
|
|
printf("[*] creating sce_sys/package/head.bin\n");
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(path, sizeof(path), "%ssce_sys/package/head.bin", root);
|
2017-09-24 15:36:24 -07:00
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
out_begin_file(path);
|
2017-09-24 15:36:24 -07:00
|
|
|
uint64_t head_size = enc_offset + items_size;
|
|
|
|
|
uint64_t head_offset = 0;
|
|
|
|
|
while (head_size != 0)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buffer[1 << 16];
|
|
|
|
|
uint32_t size = (uint32_t)min64(head_size, sizeof(buffer));
|
|
|
|
|
sys_read(pkg, head_offset, buffer, size);
|
2017-10-30 23:15:25 -07:00
|
|
|
out_write(buffer, size);
|
2017-09-24 15:36:24 -07:00
|
|
|
head_size -= size;
|
|
|
|
|
head_offset += size;
|
|
|
|
|
}
|
2017-10-30 23:15:25 -07:00
|
|
|
out_end_file();
|
2017-09-24 15:36:24 -07:00
|
|
|
|
2017-10-31 00:28:00 -07:00
|
|
|
printf("[*] creating sce_sys/package/tail.bin\n");
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(path, sizeof(path), "%ssce_sys/package/tail.bin", root);
|
2017-09-24 15:36:24 -07:00
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
out_begin_file(path);
|
2017-10-27 00:53:40 -07:00
|
|
|
uint64_t tail_offset = enc_offset + enc_size;
|
|
|
|
|
while (tail_offset != pkg_size)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buffer[1 << 16];
|
|
|
|
|
uint32_t size = (uint32_t)min64(pkg_size - tail_offset, sizeof(buffer));
|
|
|
|
|
sys_read(pkg, tail_offset, buffer, size);
|
2017-10-30 23:15:25 -07:00
|
|
|
out_write(buffer, size);
|
2017-10-27 00:53:40 -07:00
|
|
|
tail_offset += size;
|
|
|
|
|
}
|
2017-10-30 23:15:25 -07:00
|
|
|
out_end_file();
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-10-31 00:28:00 -07:00
|
|
|
printf("[*] creating sce_sys/package/stat.bin\n");
|
2017-10-30 23:15:25 -07:00
|
|
|
snprintf(path, sizeof(path), "%ssce_sys/package/stat.bin", root);
|
2017-09-24 15:36:24 -07:00
|
|
|
|
2017-10-26 00:09:15 -07:00
|
|
|
uint8_t stat[768] = { 0 };
|
2017-10-30 23:15:25 -07:00
|
|
|
out_begin_file(path);
|
|
|
|
|
out_write(stat, sizeof(stat));
|
|
|
|
|
out_end_file();
|
2017-09-24 15:36:24 -07:00
|
|
|
}
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-10-31 00:50:23 -07:00
|
|
|
if ((type == PKG_TYPE_VITA_APP || type == PKG_TYPE_VITA_DLC || type == PKG_TYPE_VITA_PSM) && zrif_arg != NULL)
|
2017-09-23 10:18:46 -07:00
|
|
|
{
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_VITA_PSM)
|
2017-10-31 00:28:00 -07:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2017-09-24 18:17:33 -07:00
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
out_begin_file(path);
|
2017-10-31 00:28:00 -07:00
|
|
|
out_write(rif, rif_size);
|
|
|
|
|
out_end_file();
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_VITA_PSM)
|
2017-10-31 00:28:00 -07:00
|
|
|
{
|
|
|
|
|
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));
|
2017-10-30 23:15:25 -07:00
|
|
|
out_end_file();
|
2017-09-23 10:18:46 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-30 23:15:25 -07:00
|
|
|
out_end();
|
2017-09-23 10:18:46 -07:00
|
|
|
|
2017-10-31 00:50:23 -07:00
|
|
|
if (type == PKG_TYPE_VITA_APP || type == PKG_TYPE_VITA_PATCH)
|
2017-10-26 00:09:15 -07:00
|
|
|
{
|
|
|
|
|
printf("[*] minimum fw version required: %s\n", min_version);
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-23 10:18:46 -07:00
|
|
|
printf("[*] done!\n");
|
|
|
|
|
}
|