PS3UPDAT.PUP installer (#2386)

* Add PUP loader

* Add .tar loader and update .pup loader

* Add extract method + offset to TAR loader

Also adds error checking + operator bool overload

* Add firmware decryption keys to key vault

* Initial seperation of SELFDecrypter

This seperates SELFDecrypter into itself and SCEDecrypter.
SCEDecrypter contains the logic to decrypt any file with an SCE Header.
SELFDecrypter inherits from SCEDecrypter and contains the code
specifically to do with ELF. DecryptData could be deduplicated more.

* Add "Install Firmware" option to tools menu

* SCEDecrypter: put each segment in own file

Also, const-correctness, adjusted buffer size and better error handling

* More SELFDecrypter refactoring

* Compile fix

* Add messageboxes to firmware install

* Add progress bar to firmware install
This commit is contained in:
Cornee Traas 2017-02-16 03:15:00 +01:00 committed by raven02
parent b1aa87b515
commit 458dbbd15d
11 changed files with 657 additions and 124 deletions

View file

@ -140,6 +140,15 @@ static u8 VSH_PUB[0x28] = {
0x6E, 0x73, 0x6A, 0xBF, 0x81, 0xF7, 0x0E, 0xE9, 0x16, 0x1B, 0x0D, 0xDE, 0xB0, 0x26, 0x76, 0x1A, 0xFF, 0x7B, 0xC8, 0x5B
};
static u8 SCEPKG_RIV[0x10] = {
0x4A, 0xCE, 0xF0, 0x12, 0x24, 0xFB, 0xEE, 0xDF, 0x82, 0x45, 0xF8, 0xFF, 0x10, 0x21, 0x1E, 0x6E
};
static u8 SCEPKG_ERK[0x20] = {
0xA9, 0x78, 0x18, 0xBD, 0x19, 0x3A, 0x67, 0xA1, 0x6F, 0xE8, 0x3A, 0x85, 0x5E, 0x1B, 0xE9, 0xFB, 0x56, 0x40, 0x93, 0x8D,
0x4D, 0xBC, 0xB2, 0xCB, 0x52, 0xC5, 0xA2, 0xF8, 0xB0, 0x2B, 0x10, 0x31
};
class KeyVault
{
std::vector<SELF_KEY> sk_LV0_arr;

View file

@ -647,18 +647,17 @@ void SelfHeader::Load(const fs::file& f)
pad = Read64(f);
}
SELFDecrypter::SELFDecrypter(const fs::file& s)
: self_f(s)
, key_v()
SCEDecrypter::SCEDecrypter(const fs::file& s)
: sce_f(s)
, data_buf_length(0)
{
}
bool SELFDecrypter::LoadHeaders(bool isElf32)
bool SCEDecrypter::LoadHeaders()
{
// Read SCE header.
self_f.seek(0);
sce_hdr.Load(self_f);
sce_f.seek(0);
sce_hdr.Load(sce_f);
// Check SCE magic.
if (!sce_hdr.CheckMagic())
@ -667,20 +666,235 @@ bool SELFDecrypter::LoadHeaders(bool isElf32)
return false;
}
return true;
}
bool SCEDecrypter::LoadMetadata(const u8 erk[32], const u8 riv[16])
{
aes_context aes;
u32 metadata_info_size = SIZE_32(meta_info);
auto metadata_info = std::make_unique<u8[]>(metadata_info_size);
u32 metadata_headers_size = sce_hdr.se_hsize - (SIZE_32(sce_hdr) + sce_hdr.se_meta + SIZE_32(meta_info));
auto metadata_headers = std::make_unique<u8[]>(metadata_headers_size);
// Locate and read the encrypted metadata info.
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
sce_f.read(metadata_info.get(), metadata_info_size);
// Locate and read the encrypted metadata header and section header.
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr) + metadata_info_size);
sce_f.read(metadata_headers.get(), metadata_headers_size);
// Copy the necessary parameters.
u8 metadata_key[0x20];
u8 metadata_iv[0x10];
memcpy(metadata_key, erk, 0x20);
memcpy(metadata_iv, riv, 0x10);
// Check DEBUG flag.
if ((sce_hdr.se_flags & 0x8000) != 0x8000)
{
// Decrypt the metadata info.
aes_setkey_dec(&aes, metadata_key, 256); // AES-256
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info.get(), metadata_info.get());
}
// Load the metadata info.
meta_info.Load(metadata_info.get());
// If the padding is not NULL for the key or iv fields, the metadata info
// is not properly decrypted.
if ((meta_info.key_pad[0] != 0x00) ||
(meta_info.iv_pad[0] != 0x00))
{
LOG_ERROR(LOADER, "SELF: Failed to decrypt metadata info!");
return false;
}
// Perform AES-CTR encryption on the metadata headers.
size_t ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
aes_setkey_enc(&aes, meta_info.key, 128);
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
// Load the metadata header.
meta_hdr.Load(metadata_headers.get());
// Load the metadata section headers.
meta_shdr.clear();
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
meta_shdr.emplace_back();
meta_shdr.back().Load(metadata_headers.get() + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
}
// Copy the decrypted data keys.
data_keys_length = meta_hdr.key_count * 0x10;
data_keys = std::make_unique<u8[]>(data_keys_length);
memcpy(data_keys.get(), metadata_headers.get() + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
return true;
}
bool SCEDecrypter::DecryptData()
{
aes_context aes;
// Calculate the total data size.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
data_buf_length += meta_shdr[i].data_size;
}
// Allocate a buffer to store decrypted data.
data_buf = std::make_unique<u8[]>(data_buf_length);
// Set initial offset.
u32 data_buf_offset = 0;
// Parse the metadata section headers to find the offsets of encrypted data.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
size_t ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
u8 data_key[0x10];
u8 data_iv[0x10];
// Check if this is an encrypted section.
if (meta_shdr[i].encrypted == 3)
{
// Make sure the key and iv are not out of boundaries.
if ((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
{
// Get the key and iv from the previously stored key buffer.
memcpy(data_key, data_keys.get() + meta_shdr[i].key_idx * 0x10, 0x10);
memcpy(data_iv, data_keys.get() + meta_shdr[i].iv_idx * 0x10, 0x10);
// Allocate a buffer to hold the data.
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
// Seek to the section data offset and read the encrypted data.
sce_f.seek(meta_shdr[i].data_offset);
sce_f.read(buf.get(), meta_shdr[i].data_size);
// Zero out our ctr nonce.
memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
// Perform AES-CTR encryption on the data blocks.
aes_setkey_enc(&aes, data_key, 128);
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf.get(), buf.get());
// Copy the decrypted data.
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
}
}
else
{
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
sce_f.seek(meta_shdr[i].data_offset);
sce_f.read(buf.get(), meta_shdr[i].data_size);
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
}
// Advance the buffer's offset.
data_buf_offset += meta_shdr[i].data_size;
}
return true;
}
// Each section gets put into its own file.
std::vector<fs::file> SCEDecrypter::MakeFile()
{
std::vector<fs::file> vec;
// Set initial offset.
u32 data_buf_offset = 0;
// Write data.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
fs::file out_f = fs::make_stream<std::vector<u8>>();
bool isValid = true;
// Decompress if necessary.
if (meta_shdr[i].compressed == 2)
{
const size_t BUFSIZE = 32 * 1024;
u8 tempbuf[BUFSIZE];
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = meta_shdr[i].data_size;
strm.avail_out = BUFSIZE;
strm.next_in = data_buf.get()+data_buf_offset;
strm.next_out = tempbuf;
int ret = inflateInit(&strm);
while (strm.avail_in)
{
ret = inflate(&strm, Z_NO_FLUSH);
if (ret == Z_STREAM_END)
break;
if (ret != Z_OK)
isValid = false;
if (!strm.avail_out) {
out_f.write(tempbuf, BUFSIZE);
strm.next_out = tempbuf;
strm.avail_out = BUFSIZE;
}
else
break;
}
int inflate_res = Z_OK;
inflate_res = inflate(&strm, Z_FINISH);
if (inflate_res != Z_STREAM_END)
isValid = false;
out_f.write(tempbuf, BUFSIZE - strm.avail_out);
inflateEnd(&strm);
}
else
{
// Write the data.
out_f.write(data_buf.get()+data_buf_offset, meta_shdr[i].data_size);
}
// Advance the data buffer offset by data size.
data_buf_offset += meta_shdr[i].data_size;
if (out_f.pos() != out_f.size())
fmt::throw_exception("MakeELF written bytes (%llu) does not equal buffer size (%llu).", out_f.pos(), out_f.size());
if (isValid) vec.push_back(std::move(out_f));
}
return vec;
}
bool SELFDecrypter::LoadHeaders()
{
if(!SCEDecrypter::LoadHeaders()) return false;
// Read SELF header.
self_hdr.Load(self_f);
self_hdr.Load(sce_f);
// Read the APP INFO.
self_f.seek(self_hdr.se_appinfooff);
app_info.Load(self_f);
sce_f.seek(self_hdr.se_appinfooff);
app_info.Load(sce_f);
// Read ELF header.
self_f.seek(self_hdr.se_elfoff);
sce_f.seek(self_hdr.se_elfoff);
if (isElf32)
elf32_hdr.Load(self_f);
elf32_hdr.Load(sce_f);
else
elf64_hdr.Load(self_f);
elf64_hdr.Load(sce_f);
// Read ELF program headers.
if (isElf32)
@ -691,11 +905,11 @@ bool SELFDecrypter::LoadHeaders(bool isElf32)
LOG_ERROR(LOADER, "SELF: ELF program header offset is null!");
return false;
}
self_f.seek(self_hdr.se_phdroff);
sce_f.seek(self_hdr.se_phdroff);
for(u32 i = 0; i < elf32_hdr.e_phnum; ++i)
{
phdr32_arr.emplace_back();
phdr32_arr.back().Load(self_f);
phdr32_arr.back().Load(sce_f);
}
}
else
@ -708,40 +922,40 @@ bool SELFDecrypter::LoadHeaders(bool isElf32)
return false;
}
self_f.seek(self_hdr.se_phdroff);
sce_f.seek(self_hdr.se_phdroff);
for (u32 i = 0; i < elf64_hdr.e_phnum; ++i)
{
phdr64_arr.emplace_back();
phdr64_arr.back().Load(self_f);
phdr64_arr.back().Load(sce_f);
}
}
// Read section info.
secinfo_arr.clear();
self_f.seek(self_hdr.se_secinfoff);
sce_f.seek(self_hdr.se_secinfoff);
for(u32 i = 0; i < ((isElf32) ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i)
{
secinfo_arr.emplace_back();
secinfo_arr.back().Load(self_f);
secinfo_arr.back().Load(sce_f);
}
// Read SCE version info.
self_f.seek(self_hdr.se_sceveroff);
scev_info.Load(self_f);
sce_f.seek(self_hdr.se_sceveroff);
scev_info.Load(sce_f);
// Read control info.
ctrlinfo_arr.clear();
self_f.seek(self_hdr.se_controloff);
sce_f.seek(self_hdr.se_controloff);
u32 i = 0;
while(i < self_hdr.se_controlsize)
{
ctrlinfo_arr.emplace_back();
ControlInfo &cinfo = ctrlinfo_arr.back();
cinfo.Load(self_f);
cinfo.Load(sce_f);
i += cinfo.size;
}
@ -756,12 +970,12 @@ bool SELFDecrypter::LoadHeaders(bool isElf32)
return true;
}
self_f.seek(self_hdr.se_shdroff);
sce_f.seek(self_hdr.se_shdroff);
for(u32 i = 0; i < elf32_hdr.e_shnum; ++i)
{
shdr32_arr.emplace_back();
shdr32_arr.back().Load(self_f);
shdr32_arr.back().Load(sce_f);
}
}
else
@ -773,19 +987,19 @@ bool SELFDecrypter::LoadHeaders(bool isElf32)
return true;
}
self_f.seek(self_hdr.se_shdroff);
sce_f.seek(self_hdr.se_shdroff);
for(u32 i = 0; i < elf64_hdr.e_shnum; ++i)
{
shdr64_arr.emplace_back();
shdr64_arr.back().Load(self_f);
shdr64_arr.back().Load(sce_f);
}
}
return true;
}
void SELFDecrypter::ShowHeaders(bool isElf32)
void SELFDecrypter::ShowHeaders()
{
LOG_NOTICE(LOADER, "SCE header");
LOG_NOTICE(LOADER, "----------------------------------------------------");
@ -902,76 +1116,8 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
bool SELFDecrypter::LoadMetadata()
{
aes_context aes;
u32 metadata_info_size = SIZE_32(meta_info);
auto metadata_info = std::make_unique<u8[]>(metadata_info_size);
u32 metadata_headers_size = sce_hdr.se_hsize - (SIZE_32(sce_hdr) + sce_hdr.se_meta + SIZE_32(meta_info));
auto metadata_headers = std::make_unique<u8[]>(metadata_headers_size);
// Locate and read the encrypted metadata info.
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
self_f.read(metadata_info.get(), metadata_info_size);
// Locate and read the encrypted metadata header and section header.
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr) + metadata_info_size);
self_f.read(metadata_headers.get(), metadata_headers_size);
// Find the right keyset from the key vault.
SELF_KEY keyset = key_v.FindSelfKey(app_info.self_type, sce_hdr.se_flags, app_info.version);
// Copy the necessary parameters.
u8 metadata_key[0x20];
u8 metadata_iv[0x10];
memcpy(metadata_key, keyset.erk, 0x20);
memcpy(metadata_iv, keyset.riv, 0x10);
// Check DEBUG flag.
if ((sce_hdr.se_flags & 0x8000) != 0x8000)
{
// Decrypt the NPDRM layer.
if (!DecryptNPDRM(metadata_info.get(), metadata_info_size))
return false;
// Decrypt the metadata info.
aes_setkey_dec(&aes, metadata_key, 256); // AES-256
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info.get(), metadata_info.get());
}
// Load the metadata info.
meta_info.Load(metadata_info.get());
// If the padding is not NULL for the key or iv fields, the metadata info
// is not properly decrypted.
if ((meta_info.key_pad[0] != 0x00) ||
(meta_info.iv_pad[0] != 0x00))
{
LOG_ERROR(LOADER, "SELF: Failed to decrypt metadata info!");
return false;
}
// Perform AES-CTR encryption on the metadata headers.
size_t ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
aes_setkey_enc(&aes, meta_info.key, 128);
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
// Load the metadata header.
meta_hdr.Load(metadata_headers.get());
// Load the metadata section headers.
meta_shdr.clear();
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
meta_shdr.emplace_back();
meta_shdr.back().Load(metadata_headers.get() + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
}
// Copy the decrypted data keys.
data_keys_length = meta_hdr.key_count * 0x10;
data_keys = std::make_unique<u8[]>(data_keys_length);
memcpy(data_keys.get(), metadata_headers.get() + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
return true;
return SCEDecrypter::LoadMetadata(keyset.erk,keyset.riv);
}
bool SELFDecrypter::DecryptData()
@ -1016,8 +1162,8 @@ bool SELFDecrypter::DecryptData()
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
// Seek to the section data offset and read the encrypted data.
self_f.seek(meta_shdr[i].data_offset);
self_f.read(buf.get(), meta_shdr[i].data_size);
sce_f.seek(meta_shdr[i].data_offset);
sce_f.read(buf.get(), meta_shdr[i].data_size);
// Zero out our ctr nonce.
memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
@ -1038,7 +1184,7 @@ bool SELFDecrypter::DecryptData()
return true;
}
fs::file SELFDecrypter::MakeElf(bool isElf32)
fs::file SELFDecrypter::MakeFile()
{
// Create a new ELF file.
fs::file e = fs::make_stream<std::vector<u8>>();
@ -1273,10 +1419,10 @@ extern fs::file decrypt_self(fs::file elf_or_self)
bool isElf32 = IsSelfElf32(elf_or_self);
// Start the decrypter on this SELF file.
SELFDecrypter self_dec(elf_or_self);
SELFDecrypter self_dec(elf_or_self, isElf32);
// Load the SELF file headers.
if (!self_dec.LoadHeaders(isElf32))
if (!self_dec.LoadHeaders())
{
LOG_ERROR(LOADER, "SELF: Failed to load SELF file headers!");
return fs::file{};
@ -1297,7 +1443,7 @@ extern fs::file decrypt_self(fs::file elf_or_self)
}
// Make a new ELF file from this SELF.
return self_dec.MakeElf(isElf32);
return self_dec.MakeFile();
}
return elf_or_self;

View file

@ -338,16 +338,42 @@ struct SelfHeader
void Show(){}
};
class SELFDecrypter
class SCEDecrypter
{
protected:
// Main SELF file stream.
const fs::file& self_f;
const fs::file& sce_f;
// SCE, SELF and APP headers.
// SCE headers.
SceHeader sce_hdr;
// Metadata structs.
MetadataInfo meta_info;
MetadataHeader meta_hdr;
std::vector<MetadataSectionHeader> meta_shdr;
// Internal data buffers.
std::unique_ptr<u8[]> data_keys;
u32 data_keys_length;
std::unique_ptr<u8[]> data_buf;
u32 data_buf_length;
public:
SCEDecrypter(const fs::file& s);
std::vector<fs::file> MakeFile();
bool LoadHeaders();
bool LoadMetadata(const u8 erk[32], const u8 riv[16]);
bool DecryptData();
};
class SELFDecrypter : private SCEDecrypter
{
// SELF, APP headers.
SelfHeader self_hdr;
AppInfo app_info;
bool isElf32;
// ELF64 header and program header/section header arrays.
Elf64_Ehdr elf64_hdr;
std::vector<Elf64_Shdr> shdr64_arr;
@ -363,25 +389,14 @@ class SELFDecrypter
SCEVersionInfo scev_info;
std::vector<ControlInfo> ctrlinfo_arr;
// Metadata structs.
MetadataInfo meta_info;
MetadataHeader meta_hdr;
std::vector<MetadataSectionHeader> meta_shdr;
// Internal data buffers.
std::unique_ptr<u8[]> data_keys;
u32 data_keys_length;
std::unique_ptr<u8[]> data_buf;
u32 data_buf_length;
// Main key vault instance.
KeyVault key_v;
public:
SELFDecrypter(const fs::file& s);
fs::file MakeElf(bool isElf32);
bool LoadHeaders(bool isElf32);
void ShowHeaders(bool isElf32);
SELFDecrypter(const fs::file& s, bool isElf32) : SCEDecrypter(s), key_v(), isElf32(isElf32) {};
fs::file MakeFile();
bool LoadHeaders();
void ShowHeaders();
bool LoadMetadata();
bool DecryptData();
bool DecryptNPDRM(u8 *metadata, u32 metadata_size);

View file

@ -5,6 +5,7 @@
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Gui/PADManager.h"
#include "Gui/AboutDialog.h"
#include "Gui/GameViewer.h"
@ -16,13 +17,16 @@
#include "Gui/SettingsDialog.h"
#include "Gui/MemoryStringSearcher.h"
#include "Gui/CgDisasm.h"
#include "Crypto/unpkg.h"
#include "Crypto/unself.h"
#include "Loader/PUP.h"
#include "Loader/TAR.h"
#include "Utilities/Thread.h"
#include "Utilities/StrUtil.h"
#include "../Crypto/unself.h"
#include <thread>
#ifndef _WIN32
@ -51,6 +55,7 @@ enum IDs
id_tools_rsx_debugger,
id_tools_string_search,
id_tools_decrypt_sprx_libraries,
id_tools_install_firmware,
id_tools_cg_disasm,
id_help_about,
id_update_dbg
@ -111,6 +116,7 @@ MainFrame::MainFrame()
menu_tools->Append(id_tools_string_search, "&String Search")->Enable(false);
menu_tools->AppendSeparator();
menu_tools->Append(id_tools_decrypt_sprx_libraries, "&Decrypt SPRX libraries");
menu_tools->Append(id_tools_install_firmware, "&Install Firmware");
wxMenu* menu_help = new wxMenu();
menubar->Append(menu_help, "&Help");
@ -146,6 +152,8 @@ MainFrame::MainFrame()
Bind(wxEVT_MENU, &MainFrame::ConfigVHDD, this, id_config_vhdd_manager);
Bind(wxEVT_MENU, &MainFrame::ConfigSaveData, this, id_config_savedata_manager);
Bind(wxEVT_MENU, &MainFrame::DecryptSPRXLibraries, this, id_tools_decrypt_sprx_libraries);
Bind(wxEVT_MENU, &MainFrame::InstallFirmware, this, id_tools_install_firmware);
Bind(wxEVT_MENU, &MainFrame::OpenELFCompiler, this, id_tools_compiler);
Bind(wxEVT_MENU, &MainFrame::OpenKernelExplorer, this, id_tools_kernel_explorer);
@ -408,6 +416,102 @@ void MainFrame::DecryptSPRXLibraries(wxCommandEvent& WXUNUSED(event))
LOG_NOTICE(GENERAL, "Finished decrypting all SPRX libraries.");
}
void MainFrame::InstallFirmware(wxCommandEvent& WXUNUSED(event))
{
wxFileDialog ctrl(this, L"Select PS3UPDAT.PUP file", wxEmptyString, wxEmptyString,
"PS3 update file (PS3UPDAT.PUP)|PS3UPDAT.PUP",
wxFD_OPEN);
if (ctrl.ShowModal() == wxID_CANCEL)
{
return;
}
fs::file pup_f(ctrl.GetPath().ToStdString());
pup_object pup(pup_f);
if (!pup) {
LOG_ERROR(GENERAL, "Error while installing firmware: PUP file is invalid.");
wxMessageBox("Error while installing firmware: PUP file is invalid.", "Failure!", wxOK | wxICON_ERROR, this);
return;
}
fs::file update_files_f = pup.get_file(0x300);
tar_object update_files(update_files_f);
auto updatefilenames = update_files.get_filenames();
updatefilenames.erase(std::remove_if(
updatefilenames.begin(), updatefilenames.end(), [](std::string s) {return s.find("dev_flash_") == std::string::npos; }),
updatefilenames.end());
wxProgressDialog pdlg("Firmware Installer", "Please wait, unpacking...", updatefilenames.size(), this, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
// Synchronization variable
atomic_t<int> progress(0);
{
// Run asynchronously
scope_thread worker("Firmware Installer", [&]
{
for (auto updatefilename : updatefilenames)
{
if (progress == -1) break;
fs::file updatefile = update_files.get_file(updatefilename);
SCEDecrypter self_dec(updatefile);
self_dec.LoadHeaders();
self_dec.LoadMetadata(SCEPKG_ERK, SCEPKG_RIV);
self_dec.DecryptData();
auto dev_flash_tar_f = self_dec.MakeFile();
if (dev_flash_tar_f.size() < 3) {
LOG_ERROR(GENERAL, "Error while installing firmware: PUP contents are invalid.");
wxMessageBox("Error while installing firmware: PUP contents are invalid.", "Failure!", wxOK | wxICON_ERROR, this);
progress = -1;
}
tar_object dev_flash_tar(dev_flash_tar_f[2]);
if (!dev_flash_tar.extract(fs::get_executable_dir()))
{
LOG_ERROR(GENERAL, "Error while installing firmware: TAR contents are invalid.");
wxMessageBox("Error while installing firmware: TAR contents are invalid.", "Failure!", wxOK | wxICON_ERROR, this);
progress = -1;
}
if(progress >= 0)
progress += 1;
}
});
// Wait for the completion
while (std::this_thread::sleep_for(5ms), std::abs(progress) < pdlg.GetRange())
{
// Update progress window
if (!pdlg.Update(static_cast<int>(progress)))
{
// Installation cancelled (signal with negative value)
progress = -1;
break;
}
}
update_files_f.close();
pup_f.close();
if (progress > 0)
{
pdlg.Update(pdlg.GetRange());
std::this_thread::sleep_for(100ms);
}
}
pdlg.Close();
if (progress > 0)
{
LOG_SUCCESS(GENERAL, "Successfully installed PS3 firmware.");
wxMessageBox("Successfully installed PS3 firmware and LLE Modules!", "Success!", wxOK, this);
}
}
void MainFrame::Pause(wxCommandEvent& WXUNUSED(event))
{
if(Emu.IsReady())

View file

@ -24,7 +24,6 @@ public:
private:
void BootGame(wxCommandEvent& event);
void BootGameAndRun(wxCommandEvent& event);
void InstallPkg(wxCommandEvent& event);
void BootElf(wxCommandEvent& event);
void Pause(wxCommandEvent& event);
@ -44,6 +43,7 @@ private:
void OpenStringSearch(wxCommandEvent& evt);
void OpenCgDisasm(wxCommandEvent& evt);
void DecryptSPRXLibraries(wxCommandEvent& event);
void InstallFirmware(wxCommandEvent& event);
void AboutDialogHandler(wxCommandEvent& event);
void UpdateUI(wxEvent& event);
void OnKeyDown(wxKeyEvent& event);

36
rpcs3/Loader/PUP.cpp Normal file
View file

@ -0,0 +1,36 @@
#include "stdafx.h"
#include "PUP.h"
pup_object::pup_object(const fs::file& file): m_file(file)
{
PUPHeader m_header;
m_file.read(m_header);
if (m_header.magic != "SCEUF\0\0\0"_u64)
{
isValid = false;
return;
}
m_file_tbl.resize(m_header.file_count);
m_file.read(m_file_tbl);
m_hash_tbl.resize(m_header.file_count);
m_file.read(m_hash_tbl);
}
fs::file pup_object::get_file(u64 entry_id)
{
if (!isValid) return fs::file();
for (PUPFileEntry file_entry : m_file_tbl)
{
if (file_entry.entry_id == entry_id)
{
std::vector<u8> file_buf(file_entry.data_length);
m_file.seek(file_entry.data_offset);
m_file.read(file_buf, file_entry.data_length);
return fs::make_stream(std::move(file_buf));
}
}
return fs::file();
};

43
rpcs3/Loader/PUP.h Normal file
View file

@ -0,0 +1,43 @@
#pragma once
#include "../../Utilities/types.h"
#include "../../Utilities/File.h"
#include <vector>
typedef struct {
le_t<u64> magic;
be_t<u64> package_version;
be_t<u64> image_version;
be_t<u64> file_count;
be_t<u64> header_length;
be_t<u64> data_length;
} PUPHeader;
typedef struct {
be_t<u64> entry_id;
be_t<u64> data_offset;
be_t<u64> data_length;
u8 padding[8];
} PUPFileEntry;
typedef struct {
be_t<u64> entry_id;
be_t<u8> hash[20];
be_t<u8> padding[4];
} PUPHashEntry;
class pup_object {
const fs::file& m_file;
bool isValid = true;
std::vector<PUPFileEntry> m_file_tbl;
std::vector<PUPHashEntry> m_hash_tbl;
public:
pup_object(const fs::file& file);
operator bool() const { return isValid; };
fs::file get_file(u64 entry_id);
};

124
rpcs3/Loader/TAR.cpp Normal file
View file

@ -0,0 +1,124 @@
#include "stdafx.h"
#include "TAR.h"
#include <cmath>
#include <cstdlib>
tar_object::tar_object(const fs::file& file, size_t offset) : m_file(file), initial_offset(offset)
{
m_file.seek(initial_offset);
largest_offset = initial_offset;
}
TARHeader tar_object::read_header(u64 offset)
{
m_file.seek(offset);
TARHeader header;
m_file.read(header);
return header;
}
int octalToDecimal(int octalNumber)
{
int decimalNumber = 0, i = 0, rem;
while (octalNumber != 0)
{
rem = octalNumber % 10;
octalNumber /= 10;
decimalNumber += rem * pow(8, i);
++i;
}
return decimalNumber;
}
std::vector<std::string> tar_object::get_filenames()
{
if (!isValid) return std::vector<std::string>();
std::vector<std::string> vec;
get_file("");
for (auto it = m_map.cbegin(); it != m_map.cend(); ++it)
{
vec.push_back(it->first);
}
return vec;
}
fs::file tar_object::get_file(std::string path)
{
if (!isValid || !m_file) return fs::file();
auto it = m_map.find(path);
if (it != m_map.end())
{
TARHeader header = read_header(it->second);
int size = octalToDecimal(atoi(header.size));
std::vector<u8> buf(size);
m_file.read(buf, size);
int offset = ((m_file.pos() - initial_offset + 512 - 1) & ~(512 - 1)) + initial_offset; // Always keep the offset aligned to 512 bytes + the initial offset.
m_file.seek(offset);
return fs::make_stream(std::move(buf));
}
else //continue scanning from last file entered
{
while (m_file.pos() < m_file.size()) {
TARHeader header = read_header(largest_offset);
if (!isValid) return fs::file();
if (std::string(header.magic).find("ustar") != std::string::npos)
m_map[header.name] = largest_offset;
int size = octalToDecimal(atoi(header.size));
if (path.compare(header.name) == 0) { //path is equal, read file and advance offset to start of next block
std::vector<u8> buf(size);
m_file.read(buf, size);
int offset = ((m_file.pos() - initial_offset + 512 - 1) & ~(512 - 1)) + initial_offset;
m_file.seek(offset);
largest_offset = offset;
return fs::make_stream(std::move(buf));
}
else { // just advance offset to next block
m_file.seek(size, fs::seek_mode::seek_cur);
int offset = ((m_file.pos() - initial_offset + 512 - 1) & ~(512 - 1)) + initial_offset;
m_file.seek(offset);
largest_offset = offset;
}
}
return fs::file();
}
}
bool tar_object::extract(std::string path)
{
if (!isValid || !m_file) return false;
get_file(""); //Make sure we have scanned all files
for (auto iter : m_map)
{
TARHeader header = read_header(iter.second);
if (std::string(header.name).empty()) continue;
switch (header.filetype) {
case '0':
{
fs::file file(header.name, fs::rewrite);
file.write(get_file(header.name).to_vector<u8>());
break;
}
case '5':
{
fs::create_dir(path + header.name);
break;
}
default:
LOG_ERROR(GENERAL,"Tar loader: unknown file type: "+header.filetype);
return false;
}
}
return true;
}

40
rpcs3/Loader/TAR.h Normal file
View file

@ -0,0 +1,40 @@
#pragma once
#include <map>
typedef struct {
char name[100];
char dontcare[24];
char size[12];
char mtime[12];
char chksum[8];
char filetype;
char linkname[100];
char magic[6];
char dontcare2[82];
char prefix[155];
char padding[12];
} TARHeader;
class tar_object
{
const fs::file& m_file;
int initial_offset;
int largest_offset; //we store the largest offset so we can continue to scan from there.
std::map<std::string, u64> m_map; //maps path to offset of header of that file, so we only need to scan the entire file once.
bool isValid = true;
TARHeader read_header(u64 offset);
public:
tar_object(const fs::file& file, size_t offset = 0);
operator bool() const { return isValid; };
std::vector<std::string> get_filenames();
fs::file get_file(std::string path);
bool extract(std::string path); // extract all files in archive to path
};

View file

@ -374,6 +374,8 @@
<ClCompile Include="Emu\System.cpp" />
<ClCompile Include="Loader\ELF.cpp" />
<ClCompile Include="Loader\PSF.cpp" />
<ClCompile Include="Loader\PUP.cpp" />
<ClCompile Include="Loader\TAR.cpp" />
<ClCompile Include="Loader\TROPUSR.cpp" />
<ClCompile Include="Loader\TRP.cpp" />
<ClCompile Include="rpcs3_api.cpp" />
@ -650,6 +652,8 @@
<ClInclude Include="Emu\System.h" />
<ClInclude Include="Loader\ELF.h" />
<ClInclude Include="Loader\PSF.h" />
<ClInclude Include="Loader\PUP.h" />
<ClInclude Include="Loader\TAR.h" />
<ClInclude Include="Loader\TROPUSR.h" />
<ClInclude Include="Loader\TRP.h" />
<ClInclude Include="ps3emu_api_enums.h" />

View file

@ -890,6 +890,12 @@
<ClCompile Include="..\Utilities\sema.cpp">
<Filter>Utilities</Filter>
</ClCompile>
<ClCompile Include="Loader\PUP.cpp">
<Filter>Loader</Filter>
</ClCompile>
<ClCompile Include="Loader\TAR.cpp">
<Filter>Loader</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Crypto\aes.h">
@ -1711,5 +1717,11 @@
<ClInclude Include="Emu\Cell\Modules\cellOskDialog.h">
<Filter>Emu\Cell\Modules</Filter>
</ClInclude>
<ClInclude Include="Loader\PUP.h">
<Filter>Loader</Filter>
</ClInclude>
<ClInclude Include="Loader\TAR.h">
<Filter>Loader</Filter>
</ClInclude>
</ItemGroup>
</Project>