From 6d56f1ae86451e75cbbe0dfcc76122c0f24846e6 Mon Sep 17 00:00:00 2001 From: O1L Date: Wed, 13 Nov 2013 22:35:25 +0400 Subject: [PATCH] Implemented PKG Installer, improved Boot game. --- rpcs3.sln | 8 + rpcs3/Gui/AboutDialog.cpp | 3 +- rpcs3/Gui/MainFrame.cpp | 51 ++++- rpcs3/Gui/MainFrame.h | 1 + unpkg/oddkeys.h | 20 ++ unpkg/ps3_common.h | 106 ++++++++++ unpkg/unpkg.c | 406 ++++++++++++++++++++++++++++++++++++++ unpkg/unpkg.h | 176 +++++++++++++++++ 8 files changed, 768 insertions(+), 3 deletions(-) create mode 100644 unpkg/oddkeys.h create mode 100644 unpkg/ps3_common.h create mode 100644 unpkg/unpkg.c create mode 100644 unpkg/unpkg.h diff --git a/rpcs3.sln b/rpcs3.sln index 819265aa30..3d5647a6ea 100644 --- a/rpcs3.sln +++ b/rpcs3.sln @@ -95,6 +95,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scetool", "scetool", "{AB33 scetool\zlib.h = scetool\zlib.h EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "unpkg", "unpkg", "{9F2D2094-BA46-4456-8C45-FD9EC108F1EE}" + ProjectSection(SolutionItems) = preProject + unpkg\oddkeys.h = unpkg\oddkeys.h + unpkg\ps3_common.h = unpkg\ps3_common.h + unpkg\unpkg.c = unpkg\unpkg.c + unpkg\unpkg.h = unpkg\unpkg.h + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 diff --git a/rpcs3/Gui/AboutDialog.cpp b/rpcs3/Gui/AboutDialog.cpp index 38ce2abcc7..76a78a3f23 100644 --- a/rpcs3/Gui/AboutDialog.cpp +++ b/rpcs3/Gui/AboutDialog.cpp @@ -47,7 +47,8 @@ AboutDialog::AboutDialog(wxWindow *parent) wxBoxSizer* s_panel_credits(new wxBoxSizer(wxHORIZONTAL)); wxStaticText* t_section1 = new wxStaticText(this, wxID_ANY, "\nDevelopers:\n\nDH\nAlexAltea", wxDefaultPosition, wxSize(156,160)); wxStaticText* t_section2 = new wxStaticText(this, wxID_ANY, "\nThanks:\n\nBlackDaemon", wxDefaultPosition, wxSize(156,160)); - wxStaticText* t_section3 = new wxStaticText(this, wxID_ANY, "\nExternal code:\n\n - SELF Decrypter based on scetool (C) 2011-2013 by naehrwert", wxDefaultPosition, wxSize(156,160)); + wxStaticText* t_section3 = new wxStaticText(this, wxID_ANY, "\nExternal code:\n\n - SELF Decrypter based on scetool (C) 2011-2013 by naehrwert\n\ - PKG Installer based on ps3pkgtool (C) 2011-2013 by avtolstoy and PKG Finalize (C) by geohot", wxDefaultPosition, wxSize(156,160)); + s_panel_credits->AddSpacer(12); s_panel_credits->Add(t_section1); s_panel_credits->AddSpacer(8); diff --git a/rpcs3/Gui/MainFrame.cpp b/rpcs3/Gui/MainFrame.cpp index d46089ac4a..551501fc3c 100644 --- a/rpcs3/Gui/MainFrame.cpp +++ b/rpcs3/Gui/MainFrame.cpp @@ -12,6 +12,7 @@ #include #include "scetool/scetool.cpp" +#include "unpkg/unpkg.c" BEGIN_EVENT_TABLE(MainFrame, FrameBase) EVT_CLOSE(MainFrame::OnQuit) @@ -22,6 +23,7 @@ enum IDs id_boot_elf = 0x555, id_boot_self, id_boot_game, + id_boot_pkg, id_sys_pause, id_sys_stop, id_sys_send_open_menu, @@ -65,6 +67,7 @@ MainFrame::MainFrame() menubar.Append(&menu_help, "Help"); menu_boot.Append(id_boot_game, "Boot game"); + menu_boot.Append(id_boot_pkg, "Install PKG"); menu_boot.AppendSeparator(); menu_boot.Append(id_boot_elf, "Boot ELF"); menu_boot.Append(id_boot_self, "Boot SELF"); @@ -88,6 +91,7 @@ MainFrame::MainFrame() AddPane(m_game_viewer, "Game List", wxAUI_DOCK_BOTTOM); Connect( id_boot_game, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::BootGame) ); + Connect( id_boot_pkg, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::BootPkg) ); Connect( id_boot_elf, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::BootElf) ); Connect( id_boot_self, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::BootSelf) ); @@ -167,9 +171,19 @@ void MainFrame::BootGame(wxCommandEvent& WXUNUSED(event)) { if(wxFile::Access(ctrl.GetPath() + elf[i], wxFile::read)) { - Emu.SetPath(ctrl.GetPath() + elf[i]); - ConLog.Write("Elf: booting..."); + ConLog.Write("SELF: booting..."); + + Emu.Stop(); + + wxString fileIn = ctrl.GetPath()+elf[i]; + wxString fileOut = (ctrl.GetPath()+elf[i])+".elf"; + scetool_decrypt((scetool::s8 *)fileIn.mb_str(), (scetool::s8 *)fileOut.mb_str()); + + Emu.SetPath((ctrl.GetPath()+elf[i])+".elf"); Emu.Load(); + if (!wxRemoveFile((ctrl.GetPath()+elf[i])+".elf")) + ConLog.Warning("Could not delete the decrypted ELF file"); + ConLog.Write("Game: boot done."); return; } @@ -179,6 +193,39 @@ void MainFrame::BootGame(wxCommandEvent& WXUNUSED(event)) return; } + +void MainFrame::BootPkg(wxCommandEvent& WXUNUSED(event)) +{ + bool stopped = false; + + if(Emu.IsRunning()) + { + Emu.Pause(); + stopped = true; + } + + wxFileDialog ctrl (this, L"Select PKG", wxEmptyString, wxEmptyString, "*.*", + wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + if(ctrl.ShowModal() == wxID_CANCEL) + { + if(stopped) Emu.Resume(); + return; + } + + ConLog.Write("PKG: extracting..."); + + Emu.Stop(); + + wxString fileName = ctrl.GetPath(); + pkg_unpack((const char *)fileName.mb_str()); + + if (!wxRemoveFile(ctrl.GetPath()+".dec")) + ConLog.Warning("Could not delete the decoded DEC file"); + + ConLog.Write("PKG: extract done."); +} + void MainFrame::BootElf(wxCommandEvent& WXUNUSED(event)) { bool stopped = false; diff --git a/rpcs3/Gui/MainFrame.h b/rpcs3/Gui/MainFrame.h index 94fdf0e9f2..a8d0bc89a6 100644 --- a/rpcs3/Gui/MainFrame.h +++ b/rpcs3/Gui/MainFrame.h @@ -20,6 +20,7 @@ private: void OnQuit(wxCloseEvent& event); void BootGame(wxCommandEvent& event); + void BootPkg(wxCommandEvent& event); void BootElf(wxCommandEvent& event); void BootSelf(wxCommandEvent& event); void Pause(wxCommandEvent& event); diff --git a/unpkg/oddkeys.h b/unpkg/oddkeys.h new file mode 100644 index 0000000000..9c53edab34 --- /dev/null +++ b/unpkg/oddkeys.h @@ -0,0 +1,20 @@ +#pragma once +#include "stdafx.h" + +u8 retail_pkg_aes_key[] = {0x2E,0x7B,0x71,0xD7,0xC9,0xC9,0xA1,0x4E,0xA3,0x22,0x1F,0x18,0x88,0x28,0xB8,0xF8}; + +u8 npdrm_keypair_e[] = { +0xA1,0xC0,0x13,0xAB,0xCE,0x98,0xA7,0xE3,0xDC,0x69,0x92,0x3B,0x07,0xC0,0x28,0x5F, +0x75,0x54,0xC5,0x12,0xB0,0xB0,0xA9,0x6F,0x24,0x52,0x40,0xF2,0xFD,0x43,0x3A,0xF2, +0x3F,0x4E,0xFE,0xC6,0xC1,0x83,0xEA,0x37,0x8D,0x1B,0xEC,0xB0,0x9D,0x88,0xDB,0x32, +0x8F,0x2C,0x86,0x37,0xB7,0xAC,0x72,0x05,0x9B,0x15,0x56,0xB0,0xD9,0x5B,0x5B,0xE0}; + +u8 npdrm_keypair_d[] = { +0x87,0xC7,0x4F,0xFE,0x66,0x93,0x0B,0xAA,0xA1,0x6F,0x86,0x40,0x91,0xC5,0x66,0xFB, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x08,0x28,0xB5,0x8F,0xAC,0xF9,0xDE,0xC8,0xD7,0x0D,0xFE,0xF0,0xF3,0x76,0x63,0xAE, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + +u8 npdrm_omac_key1[] = {0x72,0xF9,0x90,0x78,0x8F,0x9C,0xFF,0x74,0x57,0x25,0xF0,0x8E,0x4C,0x12,0x83,0x87}; +u8 npdrm_omac_key2[] = {0x6B,0xA5,0x29,0x76,0xEF,0xDA,0x16,0xEF,0x3C,0x33,0x9F,0xB2,0x97,0x1E,0x25,0x6B}; +u8 npdrm_omac_key3[] = {0x9B,0x51,0x5F,0xEA,0xCF,0x75,0x06,0x49,0x81,0xAA,0x60,0x4D,0x91,0xA5,0x4E,0x97}; diff --git a/unpkg/ps3_common.h b/unpkg/ps3_common.h new file mode 100644 index 0000000000..73d8e2185d --- /dev/null +++ b/unpkg/ps3_common.h @@ -0,0 +1,106 @@ +#pragma once +#include "stdafx.h" + +typedef struct { + u32 magic; + u32 debugFlag; + u32 infoOffset; + u32 unknown1; + u32 headSize; + u32 itemCount; + u64 packageSize; + u64 dataOffset; + u64 dataSize; +} pkg_header2; + +u64 get_u64(void* vd) { + u8 *d = (u8*)vd; + return ((u64)d[0]<<56) | ((u64)d[1]<<48) | ((u64)d[2]<<40) | ((u64)d[3]<<32) | (d[4]<<24) | (d[5]<<16) | (d[6]<<8) | d[7]; +} + +void set_u64(void* vd, u64 v) { + u8 *d = (u8*)vd; + d[0] = v>>56; + d[1] = v>>48; + d[2] = v>>40; + d[3] = v>>32; + d[4] = v>>24; + d[5] = v>>16; + d[6] = v>>8; + d[7] = v>>0; +} + +void set_u32(void* vd, u32 v) { + u8 *d = (u8*)vd; + d[0] = v>>24; + d[1] = v>>16; + d[2] = v>>8; + d[3] = v>>0; +} + +void set_u16(void* vd, u16 v) { + u8 *d = (u8*)vd; + d[0] = v>>8; + d[1] = v>>0; +} + +u32 get_u32(void* vd) { + u8 *d = (u8*)vd; + return (d[0]<<24) | (d[1]<<16) | (d[2]<<8) | d[3]; +} + +float get_float(u8* d) { + float ret; + u32 inter = (d[0]<<24) | (d[1]<<16) | (d[2]<<8) | d[3]; + memcpy(&ret, &inter, 4); + return ret; +} + +u32 get_u16(void* vd) { + u8 *d = (u8*)vd; + return (d[0]<<8) | d[1]; +} + +void hexdump(u8* d, int l) { + int i; + for(i=0;i + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "unpkg.h" +#include "ps3_common.h" +#include "oddkeys.h" + +static void hash_tostring(char *str, u8 *hash, u32 len) +{ + u8 *p; + memset(str, 0, 2*len+1); + for (p = hash; p-hash < len; p++) + { + str += 2; + } +} + +static void *pkg_open(const char *fname) +{ + FILE *f; + + f = fopen(fname, "rb"); + if (f == NULL) + { + ConLog.Error ("UnPkg: Could not open package file!"); + return NULL; + } + + return f; +} + +static int pkg_sanity_check(FILE *f, FILE *g, pkg_header **h_ptr, const char *fname) +{ + pkg_header *header = (pkg_header*)malloc(sizeof(pkg_header)); + u64 tmp; + + if (!fread(header, sizeof(pkg_header), 1, f)) + { + ConLog.Error("UnPkg: Package file is too short!"); + return 1; + } + + // some sanity checks + + if (ntohl(header->magic) != PKG_MAGIC) + { + ConLog.Error("UnPkg: Not a package file!"); + return 1; + } + + switch (ntohl(header->rel_type) >> 16 & (0xffff)) + { + case PKG_RELEASE_TYPE_DEBUG: + { + ConLog.Warning ("UnPkg: Debug PKG detected."); + u8* data; + u8 sha_key[0x40]; + int i; + f= fopen(fname, "rb"); + fseek(f, 0, SEEK_END); + int nlen = ftell(f); + fseek(f, 0, SEEK_SET); + data = (u8*)malloc(nlen); + fread(data, 1, nlen, f); + fclose(f); + + pkg_header2 *header = (pkg_header2 *)data; + int data_offset = get_u64(&(header->dataOffset)); + int data_size = get_u64(&(header->dataSize)); + + // decrypt debug + u8 sha_crap[0x40]; + memset(sha_crap, 0, 0x40); + memcpy(sha_crap, &data[0x60], 8); + memcpy(sha_crap+0x8, &data[0x60], 8); + memcpy(sha_crap+0x10, &data[0x68], 8); + memcpy(sha_crap+0x18, &data[0x68], 8); + + int dptr; + for(dptr = data_offset; dptr < (data_offset+data_size); dptr+=0x10) { + u8 hash[0x14]; + sha1(sha_crap, 0x40, hash); + for(i=0;i<0x10;i++) data[dptr+i] ^= hash[i]; + set_u64(sha_crap+0x38, get_u64(sha_crap+0x38)+1); + } + + // recrypt retail + u8 pkg_key[0x10]; + memcpy(pkg_key, &data[0x70], 0x10); + + //AES_KEY aes_key; + aes_context aes_key; + aes_setkey_enc(&aes_key, retail_pkg_aes_key, 128); + + size_t num=0; u8 ecount_buf[0x10]; memset(ecount_buf, 0, 0x10); + aes_crypt_ctr(&aes_key, data_size, &num, pkg_key, ecount_buf, &data[data_offset], &data[data_offset]); + + // write back + g = fopen(fname, "wb"); + data[4] = 0x80; // set finalize flag + memset(&data[(data_offset+data_size)], 0, 0x60); + + // add hash + sha1(data, nlen-0x20, &data[nlen-0x20]); + fwrite(data, 1, nlen, g); + //fclose(g); // not close the file for continuing + + fseek(g, 0, SEEK_END); + tmp = ftell(g); + } + break; + + + case PKG_RELEASE_TYPE_RELEASE: + { + ConLog.Warning ("UnPkg: Retail PKG detected."); + fseek(f, 0, SEEK_END); + tmp = ftell(f); + } + break; + + default: + ConLog.Error("UnPkg: Unknown release type."); + return 1; + + + } + switch (ntohl(header->rel_type) & (0xffff)) + { + case PKG_PLATFORM_TYPE_PS3: + case PKG_PLATFORM_TYPE_PSP: + break; + + default: + ConLog.Error("UnPkg: Unknown platform type."); + return 1; + } + + if (ntohl(header->header_size) != PKG_HEADER_SIZE) + { + ConLog.Error("UnPkg: Wrong header size: "); + return 1; + } + + //fseek(g, 0, SEEK_END); + //tmp = ftell(g); + if (ntohll(header->pkg_size) != tmp) + { + ConLog.Error("UnPkg: File size mismatch."); + return 1; + } + + tmp -= ntohll(header->data_offset) + 0x60; + if (ntohll(header->data_size) != tmp) + { + ConLog.Error("UnPkg: Data size mismatch."); + return 1; + } + + if (h_ptr != NULL) + { + (*h_ptr) = (pkg_header*) malloc(sizeof(pkg_header)); + memcpy(h_ptr, &header, sizeof(pkg_header*)); + } + + return 0; +} + +static void print_pkg_header(pkg_header *header) +{ + char qa[33], kl[33]; + + if (header == NULL) + return; + + hash_tostring(qa, header->qa_digest, sizeof(header->qa_digest)); + hash_tostring(kl, header->klicensee, sizeof(header->klicensee)); + + ConLog.Write("Magic: %x\n", ntohl(header->magic)); + ConLog.Write("Release Type: %x\n", ntohl(header->rel_type) >> 16 & (0xffff)); + ConLog.Write("Platform Type: %x\n", ntohl(header->rel_type) & (0xffff)); + ConLog.Write("Header size: %x\n", ntohl(header->header_size)); + ConLog.Write("Unk1: %x\n", ntohl(header->unk1)); + ConLog.Write("Metadata size: %x\n", ntohl(header->meta_size)); + ConLog.Write("File count: %u\n", ntohl(header->file_count)); + ConLog.Write("Pkg size: %llu\n", ntohll(header->pkg_size)); + ConLog.Write("Data offset: %llx\n", ntohll(header->data_offset)); + ConLog.Write("Data size: %llu\n", ntohll(header->data_size)); + ConLog.Write("TitleID: %s\n", header->title_id); + ConLog.Write("QA Digest: %s\n", qa); + ConLog.Write( "KLicensee: %s\n", kl); +} + +static void *pkg_info(const char *fname, pkg_header **h_ptr) +{ + FILE *f; + FILE *g; + pkg_header *header; + + f = (FILE*) pkg_open(fname); + if (f == NULL) + return NULL; + + if (pkg_sanity_check(f, g, &header, fname)) + return NULL; + + print_pkg_header(header); + + if (h_ptr != NULL) + { + (*h_ptr) = header; + } + else + { + free(header); + } + + + return f; +} + + +static void pkg_crypt(const u8 *key, const u8 *kl, FILE *f, + u64 len, FILE *out) +{ + aes_context c; + u32 parts, bits; + u32 i, j; + u8 iv[HASH_LEN]; + u8 buf[BUF_SIZE]; + u8 ctr[BUF_SIZE]; + u8 out_buf[BUF_SIZE]; + u32 l; + u64 hi, lo; + + parts = len / BUF_SIZE; + if (len % BUF_SIZE != 0) + parts++; + + memcpy(iv, kl, sizeof(iv)); + aes_setkey_enc(&c, key, 128); + + for (i = 0; iname_offset, SEEK_SET); + + memset(buf, 0, sizeof(buf)); + fread(buf, fentry->name_size, 1, dec); + + switch (fentry->type & (0xffff)) + { + case PKG_FILE_ENTRY_NPDRM: + case PKG_FILE_ENTRY_NPDRMEDAT: + case PKG_FILE_ENTRY_SDAT: + case PKG_FILE_ENTRY_REGULAR: + out = fopen((char *)buf, "wb"); + fseek(dec, fentry->file_offset, SEEK_SET); + for (size = 0; size < fentry->file_size; ) + { + size += fread(buf, sizeof(u8), BUF_SIZE, dec); + if (size > fentry->file_size) + tmp = size - fentry->file_size; + else + tmp = 0; + + fwrite(buf, sizeof(u8), BUF_SIZE - tmp, out); + } + + fclose(out); + break; + + case PKG_FILE_ENTRY_FOLDER: + mkdir ((char *)buf); + break; + } +} + +static void pkg_unpack_data(u32 file_count, FILE *dec) +{ + u32 i; + pkg_file_entry *file_table = NULL; + + fseek(dec, 0, SEEK_SET); + + file_table = (pkg_file_entry *)malloc(sizeof(pkg_file_entry)*file_count); + i = fread(file_table, sizeof(pkg_file_entry), file_count, dec); + + if (ntohl(file_table->name_offset) / sizeof(pkg_file_entry) != file_count) + { + ConLog.Error("UnPkg: ERROR. Impossiburu!"); + return; + } + + for (i = 0; iname_offset = ntohl((file_table+i)->name_offset); + (file_table+i)->name_size = ntohl((file_table+i)->name_size); + (file_table+i)->file_offset = ntohll((file_table+i)->file_offset); + (file_table+i)->file_size = ntohll((file_table+i)->file_size); + (file_table+i)->type = ntohl((file_table+i)->type); + + pkg_unpack_file(file_table+i, dec); + + } + + free(file_table); +} + +static void pkg_unpack(const char *fname) +{ + FILE *f, *dec; + char *dec_fname; + pkg_header *header; + int ret; + struct stat sb; + + f = (FILE*) pkg_info(fname, &header); + + if (f == NULL) + return; + + fseek(f, ntohll(header->data_offset), SEEK_SET); + + dec_fname = (char*)malloc(strlen(fname)+4); + memset(dec_fname, 0, strlen(fname)+4); + sprintf(dec_fname, "%s.dec", fname); + + dec = fopen(dec_fname, "wb+"); + if (dec == NULL) + { + ConLog.Error("UnPkg: Could not create temp file for decrypted data."); + free(header); + return; + } + unlink(dec_fname); + + pkg_crypt(PKG_AES_KEY, header->klicensee, f, ntohll(header->data_size), + dec); + fseek(dec, 0, SEEK_SET); + + fclose(f); + + if (stat(header->title_id, &sb) != 0) + { + ret = mkdir(header->title_id); + if (ret < 0) + { + ConLog.Error("UnPkg: Could not mkdir."); + free(header); + return; + } + } + + chdir(header->title_id); + + pkg_unpack_data(ntohl(header->file_count), dec); + fclose(dec); +} \ No newline at end of file diff --git a/unpkg/unpkg.h b/unpkg/unpkg.h new file mode 100644 index 0000000000..0eb214f1b4 --- /dev/null +++ b/unpkg/unpkg.h @@ -0,0 +1,176 @@ +/* + * Copyright 2011 Andrey Tolstoy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#ifndef UNPKG_H_ +#define UNPKG_H_ + +#include "stdafx.h" +#include "scetool/aes.h" +#include "scetool/sha1.h" + +#define ntohll(x) (((u64) ntohl (x) << 32) | (u64) ntohl (x >> 32) ) +#define htonll(x) (((u64) htonl (x) << 32) | (u64) htonl (x >> 32) ) +#define conv_ntohl(x) { x = ntohl(x); } +#define conv_ntohll(x) { x = ntohll(x); } +#define conv_htonl(x) { x = htonl(x); } +#define conv_htonll(x) { x = htonll(x); } + +#define unpack32(x) ((u32) ((u32)*(x) << 24 | \ + (u32)*(x+1) << 16 | \ + (u32)*(x+2) << 8 | \ + (u32)*(x+3) << 0)) + +#define unpack64(x) ((u64)unpack32(x) << 32 | (u64)unpack32(x+4)) + +#define pack32(x, p) \ + { \ + *(x) = (u8)(p >> 24); \ + *((x)+1) = (u8)(p >> 16); \ + *((x)+2) = (u8)(p >> 8); \ + *((x)+3) = (u8)(p >> 0); \ + } + +#define pack64(x, p) { pack32((x + 4), p); pack32((x), p >> 32); } + +#define HASH_LEN 16 +#define BUF_SIZE 4096 + +typedef struct { + u32 magic; // magic 0x7f504b47 + u32 rel_type; // release type + u32 header_size; // 0xc0 + u32 unk1; //some pkg version maybe + u32 meta_size; //size of metadata (block after header & hashes) + u32 file_count; // number of files + u64 pkg_size; // pkg size in bytes + u64 data_offset; // encrypted data offset + u64 data_size; // encrypted data size in bytes + char title_id[48]; // title id + u8 qa_digest[16]; // this should be the hash of "files + attribs" + u8 klicensee[16]; // nonce +} pkg_header; + +typedef struct { + u8 hash1[16]; + u8 hash2[16]; + u8 hash3[16]; + u8 hash4[16]; +} pkg_unk_checksum; + +/* + is it in meta or sfo? + # CATEGORY : HG + # BOOTABLE : YES + # VERSION : 01.00 + # APP_VER : 01.00 + # PS3_SYSTEM_VER : 03.0000 + */ + +/* meta hell structure */ +typedef struct { + u32 unk1; + u32 unk2; + u32 drm_type; + u32 unk3; + + u32 unk4; + u32 unk5; + u32 unk6; + u32 unk7; + + u32 unk8; + u32 unk9; + u32 unk10; + u32 unk11; + + u32 data_size; + u32 unk12; + u32 unk13; + u32 packager; + + u8 unk14[64]; +} pkg_meta; + +typedef struct { + u32 name_offset; // file name offset + u32 name_size; // file name size + u64 file_offset; // file offset + u64 file_size; // file size + u32 type; // file type + /* + 0x80000003 - regular file + 0x80000001 - npdrm + 0x80000004 - folder + 0x80000009 - sdat ? + 0x80000002 - npdrm.edat ? + */ + u32 pad; // padding (zeros) +} pkg_file_entry; + +typedef struct { + pkg_file_entry fe; + char *name; + char *path; +} file_table_tr; + +#define PKG_MAGIC 0x7f504b47 // \x7fPKG +#define PKG_HEADER_SIZE sizeof(pkg_header) + sizeof(pkg_unk_checksum) +#define PKG_RELEASE_TYPE_RELEASE 0x8000 +#define PKG_RELEASE_TYPE_DEBUG 0x0000 +#define PKG_PLATFORM_TYPE_PS3 0x0001 +#define PKG_PLATFORM_TYPE_PSP 0x0002 + +#define PKG_FILE_ENTRY_OVERWRITE 0x80000000 +#define PKG_FILE_ENTRY_NPDRM 0x0001 +#define PKG_FILE_ENTRY_NPDRMEDAT 0x0002 // npdrm.edat +#define PKG_FILE_ENTRY_REGULAR 0x0003 +#define PKG_FILE_ENTRY_FOLDER 0x0004 +#define PKG_FILE_ENTRY_SDAT 0x0009 // .sdat ? + +static const u8 PKG_AES_KEY[16] = { + 0x2e, 0x7b, 0x71, 0xd7, + 0xc9, 0xc9, 0xa1, 0x4e, + 0xa3, 0x22, 0x1f, 0x18, + 0x88, 0x28, 0xb8, 0xf8 +}; + +static void hash_tostring(char *str, u8 *hash, u32 len); + +static void *pkg_open(const char *fname); + +static int pkg_sanity_check(FILE *f, FILE *g, pkg_header **h_ptr, const char *fname); + +static void print_pkg_header(pkg_header *header); + +static void *pkg_info(const char *fname, pkg_header **h_ptr); + +static void pkg_crypt(const u8 *key, const u8 *kl, FILE *f, + u64 len, FILE *out); + +static void pkg_unpack(const char *fname); + +static void pkg_unpack_data(u32 file_count, FILE *dec); + +static void pkg_unpack_file(pkg_file_entry *fentry, FILE *dec);; + +static int pkg_pack_data(file_table_tr *ftr, pkg_file_entry *table, + int file_count, sha1_context *ctx, FILE *out); + + +static void *pkg_pack_create_filetable(file_table_tr *tr, int file_count, + char **n_table, u32 *n_table_len); + +#endif