mirror of
https://github.com/mmozeiko/pkg2zip.git
synced 2026-09-02 19:23:09 +03:00
unpack PSP pkg files to CSO
This commit is contained in:
@@ -275,6 +275,7 @@ int main(int argc, char* argv[])
|
||||
printf("pkg2zip v1.8\n");
|
||||
|
||||
int zipped = 1;
|
||||
int cso = 5;
|
||||
const char* pkg_arg = NULL;
|
||||
const char* zrif_arg = NULL;
|
||||
for (int i = 1; i < argc; i++)
|
||||
@@ -283,6 +284,14 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
zipped = 0;
|
||||
}
|
||||
else if (strncmp(argv[i], "-c", 2) == 0)
|
||||
{
|
||||
if (argv[i][2] != 0)
|
||||
{
|
||||
cso = atoi(argv[i] + 2);
|
||||
cso = cso > 9 ? 9 : cso < 0 ? 0 : cso;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pkg_arg != NULL)
|
||||
@@ -300,7 +309,7 @@ int main(int argc, char* argv[])
|
||||
if (pkg_arg == NULL)
|
||||
{
|
||||
fprintf(stderr, "ERROR: no pkg file specified\n");
|
||||
fatal("Usage: %s [-x] file.pkg [zRIF]\n", argv[0]);
|
||||
fatal("Usage: %s [-x] [-c[N]] file.pkg [zRIF]\n", argv[0]);
|
||||
}
|
||||
|
||||
printf("[*] loading...\n");
|
||||
@@ -706,8 +715,8 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
if (strcmp("USRDIR/CONTENT/EBOOT.PBP", name) == 0)
|
||||
{
|
||||
snprintf(path, sizeof(path), "pspemu/ISO/%s [%.9s].iso", title, id);
|
||||
unpack_psp_eboot(path, item_key, iv, pkg, enc_offset, data_offset, data_size);
|
||||
snprintf(path, sizeof(path), "pspemu/ISO/%s [%.9s].%s", title, id, cso ? "cso" : "iso");
|
||||
unpack_psp_eboot(path, item_key, iv, pkg, enc_offset, data_offset, data_size, cso);
|
||||
continue;
|
||||
}
|
||||
else if (strcmp("USRDIR/CONTENT/PSP-KEY.EDAT", name) == 0)
|
||||
|
||||
@@ -258,3 +258,70 @@ uint32_t crc32_done(crc32_ctx* ctx)
|
||||
|
||||
return ~ctx->crc[0];
|
||||
}
|
||||
|
||||
static uint32_t crc32_gfmulv(const uint32_t* m, uint32_t v)
|
||||
{
|
||||
uint32_t r = 0;
|
||||
while (v)
|
||||
{
|
||||
if (v & 1)
|
||||
{
|
||||
r ^= *m;
|
||||
}
|
||||
v >>= 1;
|
||||
m++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static void crc32_gfsq(const uint32_t* m, uint32_t* r)
|
||||
{
|
||||
for (size_t i = 0; i < 32; i++)
|
||||
{
|
||||
r[i] = crc32_gfmulv(m, m[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t crc32_combine(uint32_t a, uint32_t b, uint32_t blen)
|
||||
{
|
||||
uint32_t m1[32];
|
||||
uint32_t m2[32];
|
||||
|
||||
m1[0] = 0xedb88320;
|
||||
for (uint32_t i = 1; i < 32; i++)
|
||||
{
|
||||
m1[i] = 1 << (i - 1);
|
||||
}
|
||||
|
||||
crc32_gfsq(m1, m2);
|
||||
crc32_gfsq(m2, m1);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
crc32_gfsq(m1, m2);
|
||||
if (blen & 1)
|
||||
{
|
||||
a = crc32_gfmulv(m2, a);
|
||||
}
|
||||
blen >>= 1;
|
||||
|
||||
if (blen == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
crc32_gfsq(m2, m1);
|
||||
if (blen & 1)
|
||||
{
|
||||
a = crc32_gfmulv(m1, a);
|
||||
}
|
||||
blen >>= 1;
|
||||
|
||||
if (blen == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return a ^ b;
|
||||
}
|
||||
|
||||
@@ -9,3 +9,8 @@ typedef struct {
|
||||
void crc32_init(crc32_ctx* ctx);
|
||||
void crc32_update(crc32_ctx* ctx, const void* buffer, size_t size);
|
||||
uint32_t crc32_done(crc32_ctx* ctx);
|
||||
|
||||
// returns crc32(x||y)
|
||||
// where a=crc32(x) and b=crc32(y)
|
||||
// crc32(x||y) = crc32(x||z) ^ crc32(y), where z=00...00 (same length as y)
|
||||
uint32_t crc32_combine(uint32_t a, uint32_t b, uint32_t blen);
|
||||
|
||||
+45
-2
@@ -36,16 +36,17 @@ void out_add_folder(const char* path)
|
||||
}
|
||||
}
|
||||
|
||||
void out_begin_file(const char* name, int compress)
|
||||
uint64_t out_begin_file(const char* name, int compress)
|
||||
{
|
||||
if (out_zipped)
|
||||
{
|
||||
zip_begin_file(&out_zip, name, compress);
|
||||
return zip_begin_file(&out_zip, name, compress);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_file = sys_create(name);
|
||||
out_file_offset = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,3 +74,45 @@ void out_write(const void* buffer, uint32_t size)
|
||||
out_file_offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
void out_write_at(uint64_t offset, const void* buffer, uint32_t size)
|
||||
{
|
||||
if (out_zipped)
|
||||
{
|
||||
zip_write_file_at(&out_zip, offset, buffer, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_write(out_file, offset, buffer, size);
|
||||
}
|
||||
}
|
||||
|
||||
void out_set_offset(uint64_t offset)
|
||||
{
|
||||
if (out_zipped)
|
||||
{
|
||||
zip_set_offset(&out_zip, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_file_offset = offset;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t out_zip_get_crc32(void)
|
||||
{
|
||||
if (out_zipped)
|
||||
{
|
||||
return zip_get_crc32(&out_zip);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void out_zip_set_crc32(uint32_t crc)
|
||||
{
|
||||
if (out_zipped)
|
||||
{
|
||||
zip_set_crc32(&out_zip, crc);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -5,6 +5,12 @@
|
||||
void out_begin(const char* name, int zipped);
|
||||
void out_end(void);
|
||||
void out_add_folder(const char* path);
|
||||
void out_begin_file(const char* name, int compress);
|
||||
uint64_t out_begin_file(const char* name, int compress);
|
||||
void out_end_file(void);
|
||||
void out_write(const void* buffer, uint32_t size);
|
||||
|
||||
// hacky solution to be able to write cso header after the data is written
|
||||
void out_write_at(uint64_t offset, const void* buffer, uint32_t size);
|
||||
void out_set_offset(uint64_t offset);
|
||||
uint32_t out_zip_get_crc32(void);
|
||||
void out_zip_set_crc32(uint32_t crc);
|
||||
|
||||
+131
-15
@@ -1,12 +1,15 @@
|
||||
#include "pkg2zip_psp.h"
|
||||
#include "pkg2zip_out.h"
|
||||
#include "pkg2zip_crc32.h"
|
||||
#include "pkg2zip_utils.h"
|
||||
#include "miniz_tdef.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PSP_ISO_BLOCK_SIZE 16
|
||||
#define PSP_ISO_SECTOR_SIZE 2048
|
||||
#define ISO_SECTOR_SIZE 2048
|
||||
|
||||
#define CSO_HEADER_SIZE 24
|
||||
|
||||
// https://vitadevwiki.com/vita/Keys_NonVita#PSPAESKirk4.2F7
|
||||
static const uint8_t kirk7_key38[] = { 0x12, 0x46, 0x8d, 0x7e, 0x1c, 0x42, 0x20, 0x9b, 0xba, 0x54, 0x26, 0x83, 0x5e, 0xb0, 0x33, 0x03 };
|
||||
@@ -308,7 +311,7 @@ static void init_psp_decrypt(aes128_key* key, uint8_t* iv, int eboot, const uint
|
||||
}
|
||||
}
|
||||
|
||||
void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size)
|
||||
void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size, int cso)
|
||||
{
|
||||
if (item_size < 0x28)
|
||||
{
|
||||
@@ -341,9 +344,9 @@ void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t
|
||||
}
|
||||
|
||||
uint32_t iso_block = get32le(psar_header + 0x0c);
|
||||
if (iso_block != PSP_ISO_BLOCK_SIZE)
|
||||
if (iso_block > 16)
|
||||
{
|
||||
fatal("ERROR: unsupported data.psar block size %u, only %u supported!", iso_block, PSP_ISO_BLOCK_SIZE);
|
||||
fatal("ERROR: unsupported data.psar block size %u, max %u supported!\b", iso_block, 16);
|
||||
}
|
||||
|
||||
uint8_t mac[16];
|
||||
@@ -357,16 +360,37 @@ void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t
|
||||
uint32_t iso_start = get32le(psar_header + 0x54);
|
||||
uint32_t iso_end = get32le(psar_header + 0x64);
|
||||
uint32_t iso_total = iso_end - iso_start - 1;
|
||||
uint32_t block_count = (iso_total + PSP_ISO_BLOCK_SIZE - 1) / PSP_ISO_BLOCK_SIZE;
|
||||
uint32_t block_count = (iso_total + iso_block - 1) / iso_block;
|
||||
|
||||
uint32_t iso_table = get32le(psar_header + 0x6c);
|
||||
|
||||
if (iso_table + block_count * 32 > item_size)
|
||||
{
|
||||
fatal("ERROR: offset table in data.psar file is too large!");
|
||||
fatal("ERROR: offset table in data.psar file is too large!\n");
|
||||
}
|
||||
|
||||
mz_uint cso_compress_flags = 0;
|
||||
uint32_t cso_index = 0;
|
||||
uint32_t cso_offset = 0;
|
||||
uint64_t cso_size = 0;
|
||||
uint32_t* cso_block = NULL;
|
||||
uint32_t initial_size = 0;
|
||||
|
||||
uint64_t file_offset = out_begin_file(path, !cso);
|
||||
if (cso)
|
||||
{
|
||||
cso_size = block_count * iso_block * ISO_SECTOR_SIZE;
|
||||
cso_compress_flags = tdefl_create_comp_flags_from_zip_params(cso, -MZ_DEFAULT_WINDOW_BITS, MZ_DEFAULT_STRATEGY);
|
||||
|
||||
uint32_t cso_block_count = (uint32_t)(1 + (cso_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE);
|
||||
cso_block = sys_realloc(NULL, cso_block_count * sizeof(uint32_t));
|
||||
|
||||
initial_size = CSO_HEADER_SIZE + cso_block_count * sizeof(uint32_t);
|
||||
out_set_offset(file_offset + initial_size);
|
||||
|
||||
cso_offset = initial_size;
|
||||
}
|
||||
|
||||
out_begin_file(path, 1);
|
||||
for (uint32_t i = 0; i < block_count; i++)
|
||||
{
|
||||
uint64_t table_offset = item_offset + psar_offset + iso_table + 32 * i;
|
||||
@@ -390,7 +414,7 @@ void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t
|
||||
fatal("ERROR: iso block size/offset is to large!");
|
||||
}
|
||||
|
||||
uint8_t data[PSP_ISO_BLOCK_SIZE * PSP_ISO_SECTOR_SIZE];
|
||||
uint8_t data[16 * ISO_SECTOR_SIZE];
|
||||
|
||||
uint64_t abs_offset = item_offset + psar_offset + block_offset;
|
||||
sys_read(pkg, enc_offset + abs_offset, data, block_size);
|
||||
@@ -402,22 +426,114 @@ void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t
|
||||
}
|
||||
|
||||
uint32_t out_size;
|
||||
if (block_size == sizeof(data))
|
||||
if (block_size == iso_block * ISO_SECTOR_SIZE)
|
||||
{
|
||||
out_write(data, (uint32_t)block_size);
|
||||
if (cso)
|
||||
{
|
||||
for (size_t n = 0; n < iso_block * ISO_SECTOR_SIZE; n += ISO_SECTOR_SIZE)
|
||||
{
|
||||
cso_block[cso_index] = cso_offset;
|
||||
|
||||
uint8_t PKG_ALIGN(16) output[ISO_SECTOR_SIZE];
|
||||
size_t insize = ISO_SECTOR_SIZE;
|
||||
size_t outsize = sizeof(output);
|
||||
|
||||
tdefl_compressor c;
|
||||
tdefl_init(&c, cso_compress_flags);
|
||||
tdefl_status st = tdefl_compress(&c, data + n, &insize, output, &outsize, TDEFL_FINISH);
|
||||
if (st == TDEFL_STATUS_DONE)
|
||||
{
|
||||
out_write(output, (uint32_t)outsize);
|
||||
cso_offset += (uint32_t)outsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
cso_block[cso_index] |= 0x80000000;
|
||||
out_write(data + n, ISO_SECTOR_SIZE);
|
||||
cso_offset += ISO_SECTOR_SIZE;
|
||||
}
|
||||
cso_index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out_write(data, (uint32_t)block_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t uncompressed[PSP_ISO_BLOCK_SIZE * PSP_ISO_SECTOR_SIZE];
|
||||
uint8_t PKG_ALIGN(16) uncompressed[16 * ISO_SECTOR_SIZE];
|
||||
out_size = lzrc_decompress(uncompressed, sizeof(uncompressed), data, block_size);
|
||||
if (out_size != sizeof(uncompressed))
|
||||
if (out_size != iso_block * ISO_SECTOR_SIZE)
|
||||
{
|
||||
fatal("ERROR: internal error - lzrc decompression failed! pkg may be corrupted?");
|
||||
fatal("ERROR: internal error - lzrc decompression failed! pkg may be corrupted?\n");
|
||||
}
|
||||
if (cso)
|
||||
{
|
||||
for (size_t n = 0; n < iso_block * ISO_SECTOR_SIZE; n += ISO_SECTOR_SIZE)
|
||||
{
|
||||
cso_block[cso_index] = cso_offset;
|
||||
|
||||
uint8_t output[ISO_SECTOR_SIZE];
|
||||
size_t insize = ISO_SECTOR_SIZE;
|
||||
size_t outsize = sizeof(output);
|
||||
|
||||
tdefl_compressor c;
|
||||
tdefl_init(&c, cso_compress_flags);
|
||||
tdefl_status st = tdefl_compress(&c, uncompressed + n, &insize, output, &outsize, TDEFL_FINISH);
|
||||
if (st == TDEFL_STATUS_DONE)
|
||||
{
|
||||
out_write(output, (uint32_t)outsize);
|
||||
cso_offset += (uint32_t)outsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
cso_block[cso_index] |= 0x80000000;
|
||||
out_write(uncompressed + n, ISO_SECTOR_SIZE);
|
||||
cso_offset += ISO_SECTOR_SIZE;
|
||||
}
|
||||
cso_index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out_write(uncompressed, (uint32_t)out_size);
|
||||
}
|
||||
out_write(uncompressed, (uint32_t)out_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (cso)
|
||||
{
|
||||
cso_block[cso_index++] = cso_offset;
|
||||
|
||||
uint8_t cso_header[CSO_HEADER_SIZE] = { 0x43, 0x49, 0x53, 0x4f };
|
||||
// header size
|
||||
set32le(cso_header + 4, sizeof(cso_header));
|
||||
// original size
|
||||
set64le(cso_header + 8, cso_size);
|
||||
// block size
|
||||
set32le(cso_header + 16, ISO_SECTOR_SIZE);
|
||||
// version
|
||||
cso_header[20] = 1;
|
||||
|
||||
out_write_at(file_offset, cso_header, sizeof(cso_header));
|
||||
out_write_at(file_offset + sizeof(cso_header), cso_block, cso_index * sizeof(uint32_t));
|
||||
|
||||
crc32_ctx cheader;
|
||||
crc32_init(&cheader);
|
||||
crc32_update(&cheader, cso_header, sizeof(cso_header));
|
||||
crc32_update(&cheader, cso_block, cso_index * sizeof(uint32_t));
|
||||
|
||||
uint32_t header_crc32 = crc32_done(&cheader);
|
||||
uint32_t data_crc32 = out_zip_get_crc32();
|
||||
uint32_t data_len = (uint32_t)(cso_offset - initial_size);
|
||||
|
||||
uint32_t crc32 = crc32_combine(header_crc32, data_crc32, data_len);
|
||||
out_zip_set_crc32(crc32);
|
||||
|
||||
sys_realloc(cso_block, 0);
|
||||
}
|
||||
|
||||
out_end_file();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#include "pkg2zip_aes.h"
|
||||
#include "pkg2zip_sys.h"
|
||||
|
||||
void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size);
|
||||
void unpack_psp_eboot(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size, int cso);
|
||||
void unpack_psp_key(const char* path, const aes128_key* pkg_key, const uint8_t* pkg_iv, sys_file* pkg, uint64_t enc_offset, uint64_t item_offset, uint64_t item_size);
|
||||
|
||||
+36
-2
@@ -108,7 +108,7 @@ void zip_add_folder(zip* z, const char* name)
|
||||
z->total += 1;
|
||||
}
|
||||
|
||||
void zip_begin_file(zip* z, const char* name, int compress)
|
||||
uint64_t zip_begin_file(zip* z, const char* name, int compress)
|
||||
{
|
||||
size_t name_length = strlen(name);
|
||||
if (name_length > ZIP_MAX_FILENAME)
|
||||
@@ -124,6 +124,7 @@ void zip_begin_file(zip* z, const char* name, int compress)
|
||||
z->current = f;
|
||||
|
||||
crc32_init(&z->crc32);
|
||||
z->crc32_set = 0;
|
||||
|
||||
uint8_t header[ZIP_LOCAL_HEADER_SIZE] = { 0x50, 0x4b, 0x03, 0x04 };
|
||||
// version needed to extract
|
||||
@@ -150,6 +151,8 @@ void zip_begin_file(zip* z, const char* name, int compress)
|
||||
int flags = tdefl_create_comp_flags_from_zip_params(MZ_BEST_SPEED, -MZ_DEFAULT_WINDOW_BITS, MZ_DEFAULT_STRATEGY);
|
||||
tdefl_init(&z->tdefl, flags);
|
||||
}
|
||||
|
||||
return z->total - f->offset;
|
||||
}
|
||||
|
||||
void zip_write_file(zip* z, const void* data, uint32_t size)
|
||||
@@ -211,7 +214,10 @@ void zip_end_file(zip* z)
|
||||
}
|
||||
}
|
||||
|
||||
z->current->crc32 = crc32_done(&z->crc32);
|
||||
if (!z->crc32_set)
|
||||
{
|
||||
z->current->crc32 = crc32_done(&z->crc32);
|
||||
}
|
||||
|
||||
if (z->current->size != 0)
|
||||
{
|
||||
@@ -400,3 +406,31 @@ void zip_close(zip* z)
|
||||
|
||||
sys_realloc(z->files, 0);
|
||||
}
|
||||
|
||||
void zip_write_file_at(zip* z, uint64_t offset, const void* data, uint32_t size)
|
||||
{
|
||||
if (z->current->compress)
|
||||
{
|
||||
fatal("ERROR: cannot write at specific offset for compressed files\n");
|
||||
}
|
||||
|
||||
sys_write(z->file, z->current->offset + offset, data, size);
|
||||
z->current->size += size;
|
||||
z->current->compressed += size;
|
||||
}
|
||||
|
||||
void zip_set_offset(zip* z, uint64_t offset)
|
||||
{
|
||||
z->total = z->current->offset + offset;
|
||||
}
|
||||
|
||||
void zip_set_crc32(zip* z, uint32_t crc)
|
||||
{
|
||||
z->current->crc32 = crc;
|
||||
z->crc32_set = 1;
|
||||
}
|
||||
|
||||
uint32_t zip_get_crc32(zip* z)
|
||||
{
|
||||
return crc32_done(&z->crc32);
|
||||
}
|
||||
|
||||
+8
-1
@@ -20,6 +20,7 @@ typedef struct {
|
||||
uint16_t date;
|
||||
tdefl_compressor tdefl;
|
||||
crc32_ctx crc32;
|
||||
int crc32_set;
|
||||
uint32_t allocated; // bytes
|
||||
zip_file* files;
|
||||
zip_file* current;
|
||||
@@ -27,7 +28,13 @@ typedef struct {
|
||||
|
||||
void zip_create(zip* z, const char* name);
|
||||
void zip_add_folder(zip* z, const char* name);
|
||||
void zip_begin_file(zip* z, const char* name, int compress);
|
||||
uint64_t zip_begin_file(zip* z, const char* name, int compress);
|
||||
void zip_write_file(zip* z, const void* data, uint32_t size);
|
||||
void zip_end_file(zip* z);
|
||||
void zip_close(zip* z);
|
||||
|
||||
// hacky solution to be able to write cso header after the data is written
|
||||
void zip_write_file_at(zip* z, uint64_t offset, const void* data, uint32_t size);
|
||||
void zip_set_offset(zip* z, uint64_t offset);
|
||||
void zip_set_crc32(zip* z, uint32_t crc);
|
||||
uint32_t zip_get_crc32(zip* z);
|
||||
|
||||
Reference in New Issue
Block a user