diff --git a/rpcs3/Crypto/key_vault.h b/rpcs3/Crypto/key_vault.h index 3091f4c4e7..057c79e843 100644 --- a/rpcs3/Crypto/key_vault.h +++ b/rpcs3/Crypto/key_vault.h @@ -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 sk_LV0_arr; diff --git a/rpcs3/Crypto/unself.cpp b/rpcs3/Crypto/unself.cpp index 9df111260b..91dd579c3f 100644 --- a/rpcs3/Crypto/unself.cpp +++ b/rpcs3/Crypto/unself.cpp @@ -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(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(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(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(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(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(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 SCEDecrypter::MakeFile() +{ + std::vector 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>(); + + 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(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(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(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(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>(); @@ -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; diff --git a/rpcs3/Crypto/unself.h b/rpcs3/Crypto/unself.h index e79fdfa6c9..ee9933de64 100644 --- a/rpcs3/Crypto/unself.h +++ b/rpcs3/Crypto/unself.h @@ -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 meta_shdr; + + // Internal data buffers. + std::unique_ptr data_keys; + u32 data_keys_length; + std::unique_ptr data_buf; + u32 data_buf_length; + +public: + SCEDecrypter(const fs::file& s); + std::vector 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 shdr64_arr; @@ -363,25 +389,14 @@ class SELFDecrypter SCEVersionInfo scev_info; std::vector ctrlinfo_arr; - // Metadata structs. - MetadataInfo meta_info; - MetadataHeader meta_hdr; - std::vector meta_shdr; - - // Internal data buffers. - std::unique_ptr data_keys; - u32 data_keys_length; - std::unique_ptr 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); diff --git a/rpcs3/Gui/MainFrame.cpp b/rpcs3/Gui/MainFrame.cpp index fa63458fcc..8dd34569aa 100644 --- a/rpcs3/Gui/MainFrame.cpp +++ b/rpcs3/Gui/MainFrame.cpp @@ -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 #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 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(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()) diff --git a/rpcs3/Gui/MainFrame.h b/rpcs3/Gui/MainFrame.h index 349319b2f5..284609837c 100644 --- a/rpcs3/Gui/MainFrame.h +++ b/rpcs3/Gui/MainFrame.h @@ -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); diff --git a/rpcs3/Loader/PUP.cpp b/rpcs3/Loader/PUP.cpp new file mode 100644 index 0000000000..161af8118d --- /dev/null +++ b/rpcs3/Loader/PUP.cpp @@ -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 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(); +}; diff --git a/rpcs3/Loader/PUP.h b/rpcs3/Loader/PUP.h new file mode 100644 index 0000000000..2e38ed591d --- /dev/null +++ b/rpcs3/Loader/PUP.h @@ -0,0 +1,43 @@ +#pragma once + +#include "../../Utilities/types.h" +#include "../../Utilities/File.h" + +#include + +typedef struct { + le_t magic; + be_t package_version; + be_t image_version; + be_t file_count; + be_t header_length; + be_t data_length; +} PUPHeader; + +typedef struct { + be_t entry_id; + be_t data_offset; + be_t data_length; + u8 padding[8]; +} PUPFileEntry; + +typedef struct { + be_t entry_id; + be_t hash[20]; + be_t padding[4]; +} PUPHashEntry; + +class pup_object { + const fs::file& m_file; + bool isValid = true; + + std::vector m_file_tbl; + std::vector m_hash_tbl; + +public: + pup_object(const fs::file& file); + + operator bool() const { return isValid; }; + + fs::file get_file(u64 entry_id); +}; \ No newline at end of file diff --git a/rpcs3/Loader/TAR.cpp b/rpcs3/Loader/TAR.cpp new file mode 100644 index 0000000000..58de2841cb --- /dev/null +++ b/rpcs3/Loader/TAR.cpp @@ -0,0 +1,124 @@ +#include "stdafx.h" + +#include "TAR.h" + +#include +#include + +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 tar_object::get_filenames() +{ + if (!isValid) return std::vector(); + + std::vector 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 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 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()); + 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; +} \ No newline at end of file diff --git a/rpcs3/Loader/TAR.h b/rpcs3/Loader/TAR.h new file mode 100644 index 0000000000..c6e48f76fa --- /dev/null +++ b/rpcs3/Loader/TAR.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +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 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 get_filenames(); + + fs::file get_file(std::string path); + + bool extract(std::string path); // extract all files in archive to path +}; \ No newline at end of file diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 0f6621dbac..7990046375 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -374,6 +374,8 @@ + + @@ -650,6 +652,8 @@ + + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index b8d9fbee7e..8e52a7ffab 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -890,6 +890,12 @@ Utilities + + Loader + + + Loader + @@ -1711,5 +1717,11 @@ Emu\Cell\Modules + + Loader + + + Loader + \ No newline at end of file