diff --git a/cmake/Findcryptopp.cmake b/cmake/Findcryptopp.cmake new file mode 100644 index 000000000..d57c0bc54 --- /dev/null +++ b/cmake/Findcryptopp.cmake @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +find_package(PkgConfig QUIET) +pkg_search_module(CRYPTOPP QUIET IMPORTED_TARGET libcryptopp) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(cryptopp + REQUIRED_VARS CRYPTOPP_LINK_LIBRARIES + VERSION_VAR CRYPTOPP_VERSION +) + +if (cryptopp_FOUND AND NOT TARGET cryptopp::cryptopp) + add_library(cryptopp::cryptopp ALIAS PkgConfig::CRYPTOPP) +endif() diff --git a/deleted_commit1.txt b/deleted_commit1.txt index c911d7423..72439d083 100644 --- a/deleted_commit1.txt +++ b/deleted_commit1.txt @@ -1,4 +1,4 @@ -Fri +Date: Fri Mar 28 16:15:31 2025 +0200 src/core/crypto/crypto.cpp src/core/crypto/crypto.h src/core/crypto/keys.h diff --git a/deleted_commit2.txt b/deleted_commit2.txt index 3860ba134..7072487b5 100644 --- a/deleted_commit2.txt +++ b/deleted_commit2.txt @@ -1,4 +1,4 @@ -Fri +Date: Fri Mar 28 10:32:17 2025 -0700 documents/Quickstart/2.png src/core/file_format/pkg.cpp src/core/loader.cpp diff --git a/deleted_commit3.txt b/deleted_commit3.txt index f4e7bd27f..e7a0de5be 100644 --- a/deleted_commit3.txt +++ b/deleted_commit3.txt @@ -1 +1 @@ -Fri +Date: Fri Mar 28 19:49:04 2025 +0200 diff --git a/deleted_commit4.txt b/deleted_commit4.txt index dc1696862..2799b92ae 100644 --- a/deleted_commit4.txt +++ b/deleted_commit4.txt @@ -1 +1 @@ -Sat +Date: Sat Mar 29 11:03:40 2025 +0200 diff --git a/deleted_commit5.txt b/deleted_commit5.txt index 58a490ac6..8260cfafd 100644 --- a/deleted_commit5.txt +++ b/deleted_commit5.txt @@ -1,2 +1,2 @@ -Mon +Date: Mon Mar 31 19:29:24 2025 +0000 cmake/Findcryptopp.cmake diff --git a/documents/Quickstart/2.png b/documents/Quickstart/2.png new file mode 100644 index 000000000..7e5bdfb15 Binary files /dev/null and b/documents/Quickstart/2.png differ diff --git a/src/core/crypto/crypto.cpp b/src/core/crypto/crypto.cpp new file mode 100644 index 000000000..4020edfd8 --- /dev/null +++ b/src/core/crypto/crypto.cpp @@ -0,0 +1,215 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "crypto.h" + +CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() { + CryptoPP::InvertibleRSAFunction params; + params.SetPrime1(CryptoPP::Integer(PkgDerivedKey3Keyset::Prime1, 0x80)); + params.SetPrime2(CryptoPP::Integer(PkgDerivedKey3Keyset::Prime2, 0x80)); + + params.SetPublicExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::PublicExponent, 4)); + params.SetPrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::PrivateExponent, 0x100)); + + params.SetModPrime1PrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::Exponent1, 0x80)); + params.SetModPrime2PrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::Exponent2, 0x80)); + + params.SetModulus(CryptoPP::Integer(PkgDerivedKey3Keyset::Modulus, 0x100)); + params.SetMultiplicativeInverseOfPrime2ModPrime1( + CryptoPP::Integer(PkgDerivedKey3Keyset::Coefficient, 0x80)); + + CryptoPP::RSA::PrivateKey privateKey(params); + + return privateKey; +} + +CryptoPP::RSA::PrivateKey Crypto::FakeKeyset_keyset_init() { + CryptoPP::InvertibleRSAFunction params; + params.SetPrime1(CryptoPP::Integer(FakeKeyset::Prime1, 0x80)); + params.SetPrime2(CryptoPP::Integer(FakeKeyset::Prime2, 0x80)); + + params.SetPublicExponent(CryptoPP::Integer(FakeKeyset::PublicExponent, 4)); + params.SetPrivateExponent(CryptoPP::Integer(FakeKeyset::PrivateExponent, 0x100)); + + params.SetModPrime1PrivateExponent(CryptoPP::Integer(FakeKeyset::Exponent1, 0x80)); + params.SetModPrime2PrivateExponent(CryptoPP::Integer(FakeKeyset::Exponent2, 0x80)); + + params.SetModulus(CryptoPP::Integer(FakeKeyset::Modulus, 0x100)); + params.SetMultiplicativeInverseOfPrime2ModPrime1( + CryptoPP::Integer(FakeKeyset::Coefficient, 0x80)); + + CryptoPP::RSA::PrivateKey privateKey(params); + + return privateKey; +} + +CryptoPP::RSA::PrivateKey Crypto::DebugRifKeyset_init() { + CryptoPP::InvertibleRSAFunction params; + params.SetPrime1(CryptoPP::Integer(DebugRifKeyset::Prime1, sizeof(DebugRifKeyset::Prime1))); + params.SetPrime2(CryptoPP::Integer(DebugRifKeyset::Prime2, sizeof(DebugRifKeyset::Prime2))); + + params.SetPublicExponent( + CryptoPP::Integer(DebugRifKeyset::PublicExponent, sizeof(DebugRifKeyset::PublicExponent))); + params.SetPrivateExponent(CryptoPP::Integer(DebugRifKeyset::PrivateExponent, + sizeof(DebugRifKeyset::PrivateExponent))); + + params.SetModPrime1PrivateExponent( + CryptoPP::Integer(DebugRifKeyset::Exponent1, sizeof(DebugRifKeyset::Exponent1))); + params.SetModPrime2PrivateExponent( + CryptoPP::Integer(DebugRifKeyset::Exponent2, sizeof(DebugRifKeyset::Exponent2))); + + params.SetModulus(CryptoPP::Integer(DebugRifKeyset::Modulus, sizeof(DebugRifKeyset::Modulus))); + params.SetMultiplicativeInverseOfPrime2ModPrime1( + CryptoPP::Integer(DebugRifKeyset::Coefficient, sizeof(DebugRifKeyset::Coefficient))); + + CryptoPP::RSA::PrivateKey privateKey(params); + + return privateKey; +} + +void Crypto::RSA2048Decrypt(std::span dec_key, + std::span ciphertext, + bool is_dk3) { // RSAES_PKCS1v15_ + // Create an RSA decryptor + CryptoPP::RSA::PrivateKey privateKey; + if (is_dk3) { + privateKey = key_pkg_derived_key3_keyset_init(); + } else { + privateKey = FakeKeyset_keyset_init(); + } + + CryptoPP::RSAES_PKCS1v15_Decryptor rsaDecryptor(privateKey); + + // Allocate memory for the decrypted data + std::array decrypted; + + // Perform the decryption + CryptoPP::AutoSeededRandomPool rng; + CryptoPP::DecodingResult result = + rsaDecryptor.Decrypt(rng, ciphertext.data(), decrypted.size(), decrypted.data()); + std::copy(decrypted.begin(), decrypted.begin() + dec_key.size(), dec_key.begin()); +} + +void Crypto::ivKeyHASH256(std::span cipher_input, + std::span ivkey_result) { + CryptoPP::SHA256 sha256; + std::array hashResult; + auto array_sink = new CryptoPP::ArraySink(hashResult.data(), CryptoPP::SHA256::DIGESTSIZE); + auto filter = new CryptoPP::HashFilter(sha256, array_sink); + CryptoPP::ArraySource r(cipher_input.data(), cipher_input.size(), true, filter); + std::copy(hashResult.begin(), hashResult.begin() + ivkey_result.size(), ivkey_result.begin()); +} + +void Crypto::aesCbcCfb128Decrypt(std::span ivkey, + std::span ciphertext, + std::span decrypted) { + std::array key; + std::array iv; + + std::copy(ivkey.begin() + 16, ivkey.begin() + 16 + key.size(), key.begin()); + std::copy(ivkey.begin(), ivkey.begin() + iv.size(), iv.begin()); + + CryptoPP::AES::Decryption aesDecryption(key.data(), CryptoPP::AES::DEFAULT_KEYLENGTH); + CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data()); + + for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) { + cbcDecryption.ProcessData(decrypted.data() + i, ciphertext.data() + i, + CryptoPP::AES::BLOCKSIZE); + } +} + +void Crypto::aesCbcCfb128DecryptEntry(std::span ivkey, + std::span ciphertext, + std::span decrypted) { + std::array key; + std::array iv; + + std::copy(ivkey.begin() + 16, ivkey.begin() + 16 + key.size(), key.begin()); + std::copy(ivkey.begin(), ivkey.begin() + iv.size(), iv.begin()); + + CryptoPP::AES::Decryption aesDecryption(key.data(), CryptoPP::AES::DEFAULT_KEYLENGTH); + CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data()); + + for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) { + cbcDecryption.ProcessData(decrypted.data() + i, ciphertext.data() + i, + CryptoPP::AES::BLOCKSIZE); + } +} + +void Crypto::decryptEFSM(std::span trophyKey, + std::span NPcommID, + std::span efsmIv, std::span ciphertext, + std::span decrypted) { + + // step 1: Encrypt NPcommID + CryptoPP::CBC_Mode::Encryption encrypt; + + std::vector trophyIv(16, 0); + std::vector trpKey(16); + + encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data()); + encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16); + + // step 2: decrypt efsm. + CryptoPP::CBC_Mode::Decryption decrypt; + decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data()); + + for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) { + decrypt.ProcessData(decrypted.data() + i, ciphertext.data() + i, CryptoPP::AES::BLOCKSIZE); + } +} + +void Crypto::PfsGenCryptoKey(std::span ekpfs, + std::span seed, + std::span dataKey, + std::span tweakKey) { + CryptoPP::HMAC hmac(ekpfs.data(), ekpfs.size()); + + CryptoPP::SecByteBlock d(20); // Use Crypto++ SecByteBlock for better memory management + + // Copy the bytes of 'index' to the 'd' array + uint32_t index = 1; + std::memcpy(d, &index, sizeof(uint32_t)); + + // Copy the bytes of 'seed' to the 'd' array starting from index 4 + std::memcpy(d + sizeof(uint32_t), seed.data(), seed.size()); + + // Allocate memory for 'u64' using new + std::vector data_tweak_key(hmac.DigestSize()); + + // Calculate the HMAC + hmac.CalculateDigest(data_tweak_key.data(), d, d.size()); + std::copy(data_tweak_key.begin(), data_tweak_key.begin() + dataKey.size(), tweakKey.begin()); + std::copy(data_tweak_key.begin() + tweakKey.size(), + data_tweak_key.begin() + tweakKey.size() + dataKey.size(), dataKey.begin()); +} + +void Crypto::decryptPFS(std::span dataKey, + std::span tweakKey, std::span src_image, + std::span dst_image, u64 sector) { + // Start at 0x10000 to keep the header when decrypting the whole pfs_image. + for (int i = 0; i < src_image.size(); i += 0x1000) { + const u64 current_sector = sector + (i / 0x1000); + CryptoPP::ECB_Mode::Encryption encrypt(tweakKey.data(), tweakKey.size()); + CryptoPP::ECB_Mode::Decryption decrypt(dataKey.data(), dataKey.size()); + + std::array tweak{}; + std::array encryptedTweak; + std::array xorBuffer; + std::memcpy(tweak.data(), ¤t_sector, sizeof(u64)); + + // Encrypt the tweak for each sector. + encrypt.ProcessData(encryptedTweak.data(), tweak.data(), 16); + + for (int plaintextOffset = 0; plaintextOffset < 0x1000; plaintextOffset += 16) { + xtsXorBlock(xorBuffer.data(), src_image.data() + i + plaintextOffset, + encryptedTweak.data()); // x, c, t + decrypt.ProcessData(xorBuffer.data(), xorBuffer.data(), 16); // x, x + xtsXorBlock(dst_image.data() + i + plaintextOffset, xorBuffer.data(), + encryptedTweak.data()); //(p) c, x , t + xtsMult(encryptedTweak); + } + } +} diff --git a/src/core/crypto/crypto.h b/src/core/crypto/crypto.h new file mode 100644 index 000000000..b5d8104b5 --- /dev/null +++ b/src/core/crypto/crypto.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/types.h" +#include "keys.h" + +class Crypto { +public: + CryptoPP::RSA::PrivateKey key_pkg_derived_key3_keyset_init(); + CryptoPP::RSA::PrivateKey FakeKeyset_keyset_init(); + CryptoPP::RSA::PrivateKey DebugRifKeyset_init(); + + void RSA2048Decrypt(std::span dk3, + std::span ciphertext, + bool is_dk3); // RSAES_PKCS1v15_ + void ivKeyHASH256(std::span cipher_input, + std::span ivkey_result); + void aesCbcCfb128Decrypt(std::span ivkey, + std::span ciphertext, + std::span decrypted); + void aesCbcCfb128DecryptEntry(std::span ivkey, + std::span ciphertext, + std::span decrypted); + void decryptEFSM(std::span trophyKey, + std::span NPcommID, std::span efsmIv, + std::span ciphertext, std::span decrypted); + void PfsGenCryptoKey(std::span ekpfs, + std::span seed, + std::span dataKey, + std::span tweakKey); + void decryptPFS(std::span dataKey, + std::span tweakKey, std::span src_image, + std::span dst_image, u64 sector); + + void xtsXorBlock(CryptoPP::byte* x, const CryptoPP::byte* a, const CryptoPP::byte* b) { + for (int i = 0; i < 16; i++) { + x[i] = a[i] ^ b[i]; + } + } + + void xtsMult(std::span encryptedTweak) { + int feedback = 0; + for (int k = 0; k < encryptedTweak.size(); k++) { + const auto tmp = (encryptedTweak[k] >> 7) & 1; + encryptedTweak[k] = ((encryptedTweak[k] << 1) + feedback) & 0xFF; + feedback = tmp; + } + if (feedback != 0) { + encryptedTweak[0] ^= 0x87; + } + } +}; diff --git a/src/core/crypto/keys.h b/src/core/crypto/keys.h new file mode 100644 index 000000000..441082481 --- /dev/null +++ b/src/core/crypto/keys.h @@ -0,0 +1,305 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include + +class FakeKeyset { +public: + // Constructor + static constexpr CryptoPP::byte Exponent1[] = { + 0x6D, 0x48, 0xE0, 0x54, 0x40, 0x25, 0xC8, 0x41, 0x29, 0x52, 0x42, 0x27, 0xEB, 0xD2, 0xC7, + 0xAB, 0x6B, 0x9C, 0x27, 0x0A, 0xB4, 0x1F, 0x94, 0x4E, 0xFA, 0x42, 0x1D, 0xB7, 0xBC, 0xB9, + 0xAE, 0xBC, 0x04, 0x6F, 0x75, 0x8F, 0x10, 0x5F, 0x89, 0xAC, 0xAB, 0x9C, 0xD2, 0xFA, 0xE6, + 0xA4, 0x13, 0x83, 0x68, 0xD4, 0x56, 0x38, 0xFE, 0xE5, 0x2B, 0x78, 0x44, 0x9C, 0x34, 0xE6, + 0x5A, 0xA0, 0xBE, 0x05, 0x70, 0xAD, 0x15, 0xC3, 0x2D, 0x31, 0xAC, 0x97, 0x5D, 0x88, 0xFC, + 0xC1, 0x62, 0x3D, 0xE2, 0xED, 0x11, 0xDB, 0xB6, 0x9E, 0xFC, 0x5A, 0x5A, 0x03, 0xF6, 0xCF, + 0x08, 0xD4, 0x5D, 0x90, 0xC9, 0x2A, 0xB9, 0x9B, 0xCF, 0xC8, 0x1A, 0x65, 0xF3, 0x5B, 0xE8, + 0x7F, 0xCF, 0xA5, 0xA6, 0x4C, 0x5C, 0x2A, 0x12, 0x0F, 0x92, 0xA5, 0xE3, 0xF0, 0x17, 0x1E, + 0x9A, 0x97, 0x45, 0x86, 0xFD, 0xDB, 0x54, 0x25}; + // exponent2 = d mod (q - 1) + static constexpr CryptoPP::byte Exponent2[] = { + 0x2A, 0x51, 0xCE, 0x02, 0x44, 0x28, 0x50, 0xE8, 0x30, 0x20, 0x7C, 0x9C, 0x55, 0xBF, 0x60, + 0x39, 0xBC, 0xD1, 0xF0, 0xE7, 0x68, 0xF8, 0x08, 0x5B, 0x61, 0x1F, 0xA7, 0xBF, 0xD0, 0xE8, + 0x8B, 0xB5, 0xB1, 0xD5, 0xD9, 0x16, 0xAC, 0x75, 0x0C, 0x6D, 0xF2, 0xE0, 0xB5, 0x97, 0x75, + 0xD2, 0x68, 0x16, 0x1F, 0x00, 0x7D, 0x8B, 0x17, 0xE8, 0x78, 0x48, 0x41, 0x71, 0x2B, 0x18, + 0x96, 0x80, 0x11, 0xDB, 0x68, 0x39, 0x9C, 0xD6, 0xE0, 0x72, 0x42, 0x86, 0xF0, 0x1B, 0x16, + 0x0D, 0x3E, 0x12, 0x94, 0x3D, 0x25, 0xA8, 0xA9, 0x30, 0x9E, 0x54, 0x5A, 0xD6, 0x36, 0x6C, + 0xD6, 0x8C, 0x20, 0x62, 0x8F, 0xA1, 0x6B, 0x1F, 0x7C, 0x6D, 0xB2, 0xB1, 0xC1, 0x2E, 0xAD, + 0x36, 0x02, 0x9C, 0x3A, 0xCA, 0x2F, 0x09, 0xD2, 0x45, 0x9E, 0xEB, 0xF2, 0xBC, 0x6C, 0xAA, + 0x3B, 0x3E, 0x90, 0xBC, 0x38, 0x67, 0x35, 0x4D}; + // e + static constexpr CryptoPP::byte PublicExponent[] = {0, 1, 0, 1}; + // (InverseQ)(q) = 1 mod p + static constexpr CryptoPP::byte Coefficient[] = { + 0x0B, 0x67, 0x1C, 0x0D, 0x6C, 0x57, 0xD3, 0xE7, 0x05, 0x65, 0x94, 0x31, 0x56, 0x55, 0xFD, + 0x28, 0x08, 0xFA, 0x05, 0x8A, 0xCC, 0x55, 0x39, 0x61, 0x97, 0x63, 0xA0, 0x16, 0x27, 0x3D, + 0xED, 0xC1, 0x16, 0x40, 0x2A, 0x12, 0xEA, 0x6F, 0xD9, 0xD8, 0x58, 0x56, 0xA8, 0x56, 0x8B, + 0x0D, 0x38, 0x5E, 0x1E, 0x80, 0x3B, 0x5F, 0x40, 0x80, 0x6F, 0x62, 0x4F, 0x28, 0xA2, 0x69, + 0xF3, 0xD3, 0xF7, 0xFD, 0xB2, 0xC3, 0x52, 0x43, 0x20, 0x92, 0x9D, 0x97, 0x8D, 0xA0, 0x15, + 0x07, 0x15, 0x6E, 0xA4, 0x0D, 0x56, 0xD3, 0x37, 0x1A, 0xC4, 0x9E, 0xDF, 0x02, 0x49, 0xB8, + 0x0A, 0x84, 0x62, 0xF5, 0xFA, 0xB9, 0x3F, 0xA4, 0x09, 0x76, 0xCC, 0xAA, 0xB9, 0x9B, 0xA6, + 0x4F, 0xC1, 0x6A, 0x64, 0xCE, 0xD8, 0x77, 0xAB, 0x4B, 0xF9, 0xA0, 0xAE, 0xDA, 0xF1, 0x67, + 0x87, 0x7C, 0x98, 0x5C, 0x7E, 0xB8, 0x73, 0xF5}; + // n = p * q + static constexpr CryptoPP::byte Modulus[] = { + 0xC6, 0xCF, 0x71, 0xE7, 0xE5, 0x9A, 0xF0, 0xD1, 0x2A, 0x2C, 0x45, 0x8B, 0xF9, 0x2A, 0x0E, + 0xC1, 0x43, 0x05, 0x8B, 0xC3, 0x71, 0x17, 0x80, 0x1D, 0xCD, 0x49, 0x7D, 0xDE, 0x35, 0x9D, + 0x25, 0x9B, 0xA0, 0xD7, 0xA0, 0xF2, 0x7D, 0x6C, 0x08, 0x7E, 0xAA, 0x55, 0x02, 0x68, 0x2B, + 0x23, 0xC6, 0x44, 0xB8, 0x44, 0x18, 0xEB, 0x56, 0xCF, 0x16, 0xA2, 0x48, 0x03, 0xC9, 0xE7, + 0x4F, 0x87, 0xEB, 0x3D, 0x30, 0xC3, 0x15, 0x88, 0xBF, 0x20, 0xE7, 0x9D, 0xFF, 0x77, 0x0C, + 0xDE, 0x1D, 0x24, 0x1E, 0x63, 0xA9, 0x4F, 0x8A, 0xBF, 0x5B, 0xBE, 0x60, 0x19, 0x68, 0x33, + 0x3B, 0xFC, 0xED, 0x9F, 0x47, 0x4E, 0x5F, 0xF8, 0xEA, 0xCB, 0x3D, 0x00, 0xBD, 0x67, 0x01, + 0xF9, 0x2C, 0x6D, 0xC6, 0xAC, 0x13, 0x64, 0xE7, 0x67, 0x14, 0xF3, 0xDC, 0x52, 0x69, 0x6A, + 0xB9, 0x83, 0x2C, 0x42, 0x30, 0x13, 0x1B, 0xB2, 0xD8, 0xA5, 0x02, 0x0D, 0x79, 0xED, 0x96, + 0xB1, 0x0D, 0xF8, 0xCC, 0x0C, 0xDF, 0x81, 0x95, 0x4F, 0x03, 0x58, 0x09, 0x57, 0x0E, 0x80, + 0x69, 0x2E, 0xFE, 0xFF, 0x52, 0x77, 0xEA, 0x75, 0x28, 0xA8, 0xFB, 0xC9, 0xBE, 0xBF, 0x9F, + 0xBB, 0xB7, 0x79, 0x8E, 0x18, 0x05, 0xE1, 0x80, 0xBD, 0x50, 0x34, 0x94, 0x81, 0xD3, 0x53, + 0xC2, 0x69, 0xA2, 0xD2, 0x4C, 0xCF, 0x6C, 0xF4, 0x57, 0x2C, 0x10, 0x4A, 0x3F, 0xFB, 0x22, + 0xFD, 0x8B, 0x97, 0xE2, 0xC9, 0x5B, 0xA6, 0x2B, 0xCD, 0xD6, 0x1B, 0x6B, 0xDB, 0x68, 0x7F, + 0x4B, 0xC2, 0xA0, 0x50, 0x34, 0xC0, 0x05, 0xE5, 0x8D, 0xEF, 0x24, 0x67, 0xFF, 0x93, 0x40, + 0xCF, 0x2D, 0x62, 0xA2, 0xA0, 0x50, 0xB1, 0xF1, 0x3A, 0xA8, 0x3D, 0xFD, 0x80, 0xD1, 0xF9, + 0xB8, 0x05, 0x22, 0xAF, 0xC8, 0x35, 0x45, 0x90, 0x58, 0x8E, 0xE3, 0x3A, 0x7C, 0xBD, 0x3E, + 0x27}; + // p + static constexpr CryptoPP::byte Prime1[] = { + 0xFE, 0xF6, 0xBF, 0x1D, 0x69, 0xAB, 0x16, 0x25, 0x08, 0x47, 0x55, 0x6B, 0x86, 0xE4, 0x35, + 0x88, 0x72, 0x2A, 0xB1, 0x3D, 0xF8, 0xB6, 0x44, 0xCA, 0xB3, 0xAB, 0x19, 0xD1, 0x04, 0x24, + 0x28, 0x0A, 0x74, 0x55, 0xB8, 0x15, 0x45, 0x09, 0xCC, 0x13, 0x1C, 0xF2, 0xBA, 0x37, 0xA9, + 0x03, 0x90, 0x8F, 0x02, 0x10, 0xFF, 0x25, 0x79, 0x86, 0xCC, 0x18, 0x50, 0x9A, 0x10, 0x5F, + 0x5B, 0x4C, 0x1C, 0x4E, 0xB0, 0xA7, 0xE3, 0x59, 0xB1, 0x2D, 0xA0, 0xC6, 0xB0, 0x20, 0x2C, + 0x21, 0x33, 0x12, 0xB3, 0xAF, 0x72, 0x34, 0x83, 0xCD, 0x52, 0x2F, 0xAF, 0x0F, 0x20, 0x5A, + 0x1B, 0xC0, 0xE2, 0xA3, 0x76, 0x34, 0x0F, 0xD7, 0xFC, 0xC1, 0x41, 0xC9, 0xF9, 0x79, 0x40, + 0x17, 0x42, 0x21, 0x3E, 0x9D, 0xFD, 0xC7, 0xC1, 0x50, 0xDE, 0x44, 0x5A, 0xC9, 0x31, 0x89, + 0x6A, 0x78, 0x05, 0xBE, 0x65, 0xB4, 0xE8, 0x2D}; + // q + static constexpr CryptoPP::byte Prime2[] = { + 0xC7, 0x9E, 0x47, 0x58, 0x00, 0x7D, 0x62, 0x82, 0xB0, 0xD2, 0x22, 0x81, 0xD4, 0xA8, 0x97, + 0x1B, 0x79, 0x0C, 0x3A, 0xB0, 0xD7, 0xC9, 0x30, 0xE3, 0xC3, 0x53, 0x8E, 0x57, 0xEF, 0xF0, + 0x9B, 0x9F, 0xB3, 0x90, 0x52, 0xC6, 0x94, 0x22, 0x36, 0xAA, 0xE6, 0x4A, 0x5F, 0x72, 0x1D, + 0x70, 0xE8, 0x76, 0x58, 0xC8, 0xB2, 0x91, 0xCE, 0x9C, 0xC3, 0xE9, 0x09, 0x7F, 0x2E, 0x47, + 0x97, 0xCC, 0x90, 0x39, 0x15, 0x35, 0x31, 0xDE, 0x1F, 0x0C, 0x8C, 0x0D, 0xC1, 0xC2, 0x92, + 0xBE, 0x97, 0xBF, 0x2F, 0x91, 0xA1, 0x8C, 0x7D, 0x50, 0xA8, 0x21, 0x2F, 0xD7, 0xA2, 0x9A, + 0x7E, 0xB5, 0xA7, 0x2A, 0x90, 0x02, 0xD9, 0xF3, 0x3D, 0xD1, 0xEB, 0xB8, 0xE0, 0x5A, 0x79, + 0x9E, 0x7D, 0x8D, 0xCA, 0x18, 0x6D, 0xBD, 0x9E, 0xA1, 0x80, 0x28, 0x6B, 0x2A, 0xFE, 0x51, + 0x24, 0x9B, 0x6F, 0x4D, 0x84, 0x77, 0x80, 0x23}; + static constexpr CryptoPP::byte PrivateExponent[] = { + 0x7F, 0x76, 0xCD, 0x0E, 0xE2, 0xD4, 0xDE, 0x05, 0x1C, 0xC6, 0xD9, 0xA8, 0x0E, 0x8D, 0xFA, + 0x7B, 0xCA, 0x1E, 0xAA, 0x27, 0x1A, 0x40, 0xF8, 0xF1, 0x22, 0x87, 0x35, 0xDD, 0xDB, 0xFD, + 0xEE, 0xF8, 0xC2, 0xBC, 0xBD, 0x01, 0xFB, 0x8B, 0xE2, 0x3E, 0x63, 0xB2, 0xB1, 0x22, 0x5C, + 0x56, 0x49, 0x6E, 0x11, 0xBE, 0x07, 0x44, 0x0B, 0x9A, 0x26, 0x66, 0xD1, 0x49, 0x2C, 0x8F, + 0xD3, 0x1B, 0xCF, 0xA4, 0xA1, 0xB8, 0xD1, 0xFB, 0xA4, 0x9E, 0xD2, 0x21, 0x28, 0x83, 0x09, + 0x8A, 0xF6, 0xA0, 0x0B, 0xA3, 0xD6, 0x0F, 0x9B, 0x63, 0x68, 0xCC, 0xBC, 0x0C, 0x4E, 0x14, + 0x5B, 0x27, 0xA4, 0xA9, 0xF4, 0x2B, 0xB9, 0xB8, 0x7B, 0xC0, 0xE6, 0x51, 0xAD, 0x1D, 0x77, + 0xD4, 0x6B, 0xB9, 0xCE, 0x20, 0xD1, 0x26, 0x66, 0x7E, 0x5E, 0x9E, 0xA2, 0xE9, 0x6B, 0x90, + 0xF3, 0x73, 0xB8, 0x52, 0x8F, 0x44, 0x11, 0x03, 0x0C, 0x13, 0x97, 0x39, 0x3D, 0x13, 0x22, + 0x58, 0xD5, 0x43, 0x82, 0x49, 0xDA, 0x6E, 0x7C, 0xA1, 0xC5, 0x8C, 0xA5, 0xB0, 0x09, 0xE0, + 0xCE, 0x3D, 0xDF, 0xF4, 0x9D, 0x3C, 0x97, 0x15, 0xE2, 0x6A, 0xC7, 0x2B, 0x3C, 0x50, 0x93, + 0x23, 0xDB, 0xBA, 0x4A, 0x22, 0x66, 0x44, 0xAC, 0x78, 0xBB, 0x0E, 0x1A, 0x27, 0x43, 0xB5, + 0x71, 0x67, 0xAF, 0xF4, 0xAB, 0x48, 0x46, 0x93, 0x73, 0xD0, 0x42, 0xAB, 0x93, 0x63, 0xE5, + 0x6C, 0x9A, 0xDE, 0x50, 0x24, 0xC0, 0x23, 0x7D, 0x99, 0x79, 0x3F, 0x22, 0x07, 0xE0, 0xC1, + 0x48, 0x56, 0x1B, 0xDF, 0x83, 0x09, 0x12, 0xB4, 0x2D, 0x45, 0x6B, 0xC9, 0xC0, 0x68, 0x85, + 0x99, 0x90, 0x79, 0x96, 0x1A, 0xD7, 0xF5, 0x4D, 0x1F, 0x37, 0x83, 0x40, 0x4A, 0xEC, 0x39, + 0x37, 0xA6, 0x80, 0x92, 0x7D, 0xC5, 0x80, 0xC7, 0xD6, 0x6F, 0xFE, 0x8A, 0x79, 0x89, 0xC6, + 0xB1}; +}; + +class DebugRifKeyset { +public: + // std::uint8_t* PrivateExponent; + static constexpr CryptoPP::byte Exponent1[] = { + 0xCD, 0x9A, 0x61, 0xB0, 0xB8, 0xD5, 0xB4, 0xE4, 0xE4, 0xF6, 0xAB, 0xF7, 0x27, 0xB7, 0x56, + 0x59, 0x6B, 0xB9, 0x11, 0xE7, 0xF4, 0x83, 0xAF, 0xB9, 0x73, 0x99, 0x7F, 0x49, 0xA2, 0x9C, + 0xF0, 0xB5, 0x6D, 0x37, 0x82, 0x14, 0x15, 0xF1, 0x04, 0x8A, 0xD4, 0x8E, 0xEB, 0x2E, 0x1F, + 0xE2, 0x81, 0xA9, 0x62, 0x6E, 0xB1, 0x68, 0x75, 0x62, 0xF3, 0x0F, 0xFE, 0xD4, 0x91, 0x87, + 0x98, 0x78, 0xBF, 0x26, 0xB5, 0x07, 0x58, 0xD0, 0xEE, 0x3F, 0x21, 0xE8, 0xC8, 0x0F, 0x5F, + 0xFA, 0x1C, 0x64, 0x74, 0x49, 0x52, 0xEB, 0xE7, 0xEE, 0xDE, 0xBA, 0x23, 0x26, 0x4A, 0xF6, + 0x9C, 0x1A, 0x09, 0x3F, 0xB9, 0x0B, 0x36, 0x26, 0x1A, 0xBE, 0xA9, 0x76, 0xE6, 0xF2, 0x69, + 0xDE, 0xFF, 0xAF, 0xCC, 0x0C, 0x9A, 0x66, 0x03, 0x86, 0x0A, 0x1F, 0x49, 0xA4, 0x10, 0xB6, + 0xBC, 0xC3, 0x7C, 0x88, 0xE8, 0xCE, 0x4B, 0xD9}; + // exponent2 = d mod (q - 1) + static constexpr CryptoPP::byte Exponent2[] = { + 0xB3, 0x73, 0xA3, 0x59, 0xE6, 0x97, 0xC0, 0xAB, 0x3B, 0x68, 0xFC, 0x39, 0xAC, 0xDB, 0x44, + 0xB1, 0xB4, 0x9E, 0x35, 0x4D, 0xBE, 0xC5, 0x36, 0x69, 0x6C, 0x3D, 0xC5, 0xFC, 0xFE, 0x4B, + 0x2F, 0xDC, 0x86, 0x80, 0x46, 0x96, 0x40, 0x1A, 0x0D, 0x6E, 0xFA, 0x8C, 0xE0, 0x47, 0x91, + 0xAC, 0xAD, 0x95, 0x2B, 0x8E, 0x1F, 0xF2, 0x0A, 0x45, 0xF8, 0x29, 0x95, 0x70, 0xC6, 0x88, + 0x5F, 0x71, 0x03, 0x99, 0x79, 0xBC, 0x84, 0x71, 0xBD, 0xE8, 0x84, 0x8C, 0x0E, 0xD4, 0x7B, + 0x30, 0x74, 0x57, 0x1A, 0x95, 0xE7, 0x90, 0x19, 0x8D, 0xAD, 0x8B, 0x4C, 0x4E, 0xC3, 0xE7, + 0x6B, 0x23, 0x86, 0x01, 0xEE, 0x9B, 0xE0, 0x2F, 0x15, 0xA2, 0x2C, 0x4C, 0x39, 0xD3, 0xDF, + 0x9C, 0x39, 0x01, 0xF1, 0x8C, 0x44, 0x4A, 0x15, 0x44, 0xDC, 0x51, 0xF7, 0x22, 0xD7, 0x7F, + 0x41, 0x7F, 0x68, 0xFA, 0xEE, 0x56, 0xE8, 0x05}; + // e + static constexpr CryptoPP::byte PublicExponent[] = {0x00, 0x01, 0x00, 0x01}; + // (InverseQ)(q) = 1 mod p + static constexpr CryptoPP::byte Coefficient[] = { + 0xC0, 0x32, 0x43, 0xD3, 0x8C, 0x3D, 0xB4, 0xD2, 0x48, 0x8C, 0x42, 0x41, 0x24, 0x94, 0x6C, + 0x80, 0xC9, 0xC1, 0x79, 0x36, 0x7F, 0xAC, 0xC3, 0xFF, 0x6A, 0x25, 0xEB, 0x2C, 0xFB, 0xD4, + 0x2B, 0xA0, 0xEB, 0xFE, 0x25, 0xE9, 0xC6, 0x77, 0xCE, 0xFE, 0x2D, 0x23, 0xFE, 0xD0, 0xF4, + 0x0F, 0xD9, 0x7E, 0xD5, 0xA5, 0x7D, 0x1F, 0xC0, 0xE8, 0xE8, 0xEC, 0x80, 0x5B, 0xC7, 0xFD, + 0xE2, 0xBD, 0x94, 0xA6, 0x2B, 0xDD, 0x6A, 0x60, 0x45, 0x54, 0xAB, 0xCA, 0x42, 0x9C, 0x6A, + 0x6C, 0xBF, 0x3C, 0x84, 0xF9, 0xA5, 0x0E, 0x63, 0x0C, 0x51, 0x58, 0x62, 0x6D, 0x5A, 0xB7, + 0x3C, 0x3F, 0x49, 0x1A, 0xD0, 0x93, 0xB8, 0x4F, 0x1A, 0x6C, 0x5F, 0xC5, 0xE5, 0xA9, 0x75, + 0xD4, 0x86, 0x9E, 0xDF, 0x87, 0x0F, 0x27, 0xB0, 0x26, 0x78, 0x4E, 0xFB, 0xC1, 0x8A, 0x4A, + 0x24, 0x3F, 0x7F, 0x8F, 0x9A, 0x12, 0x51, 0xCB}; + // n = p * q + static constexpr CryptoPP::byte Modulus[] = { + 0xC2, 0xD2, 0x44, 0xBC, 0xDD, 0x84, 0x3F, 0xD9, 0xC5, 0x22, 0xAF, 0xF7, 0xFC, 0x88, 0x8A, + 0x33, 0x80, 0xED, 0x8E, 0xE2, 0xCC, 0x81, 0xF7, 0xEC, 0xF8, 0x1C, 0x79, 0xBF, 0x02, 0xBB, + 0x12, 0x8E, 0x61, 0x68, 0x29, 0x1B, 0x15, 0xB6, 0x5E, 0xC6, 0xF8, 0xBF, 0x5A, 0xE0, 0x3B, + 0x6A, 0x6C, 0xD9, 0xD6, 0xF5, 0x75, 0xAB, 0xA0, 0x6F, 0x34, 0x81, 0x34, 0x9A, 0x5B, 0xAD, + 0xED, 0x31, 0xE3, 0xC6, 0xEA, 0x1A, 0xD1, 0x13, 0x22, 0xBB, 0xB3, 0xDA, 0xB3, 0xB2, 0x53, + 0xBD, 0x45, 0x79, 0x87, 0xAD, 0x0A, 0x01, 0x72, 0x18, 0x10, 0x29, 0x49, 0xF4, 0x41, 0x7F, + 0xD6, 0x47, 0x0C, 0x72, 0x92, 0x9E, 0xE9, 0xBB, 0x95, 0xA9, 0x5D, 0x79, 0xEB, 0xE4, 0x30, + 0x76, 0x90, 0x45, 0x4B, 0x9D, 0x9C, 0xCF, 0x92, 0x03, 0x60, 0x8C, 0x4B, 0x6C, 0xB3, 0x7A, + 0x3A, 0x05, 0x39, 0xA0, 0x66, 0xA9, 0x35, 0xCF, 0xB9, 0xFA, 0xAD, 0x9C, 0xAB, 0xEB, 0xE4, + 0x6A, 0x8C, 0xE9, 0x3B, 0xCC, 0x72, 0x12, 0x62, 0x63, 0xBD, 0x80, 0xC4, 0xEE, 0x37, 0x2B, + 0x32, 0x03, 0xA3, 0x09, 0xF7, 0xA0, 0x61, 0x57, 0xAD, 0x0D, 0xCF, 0x15, 0x98, 0x9E, 0x4E, + 0x49, 0xF8, 0xB5, 0xA3, 0x5C, 0x27, 0xEE, 0x45, 0x04, 0xEA, 0xE4, 0x4B, 0xBC, 0x8F, 0x87, + 0xED, 0x19, 0x1E, 0x46, 0x75, 0x63, 0xC4, 0x5B, 0xD5, 0xBC, 0x09, 0x2F, 0x02, 0x73, 0x19, + 0x3C, 0x58, 0x55, 0x49, 0x66, 0x4C, 0x11, 0xEC, 0x0F, 0x09, 0xFA, 0xA5, 0x56, 0x0A, 0x5A, + 0x63, 0x56, 0xAD, 0xA0, 0x0D, 0x86, 0x08, 0xC1, 0xE6, 0xB6, 0x13, 0x22, 0x49, 0x2F, 0x7C, + 0xDB, 0x4C, 0x56, 0x97, 0x0E, 0xC2, 0xD9, 0x2E, 0x87, 0xBC, 0x0E, 0x67, 0xC0, 0x1B, 0x58, + 0xBC, 0x64, 0x2B, 0xC2, 0x6E, 0xE2, 0x93, 0x2E, 0xB5, 0x6B, 0x70, 0xA4, 0x42, 0x9F, 0x64, + 0xC1}; + // p + static constexpr CryptoPP::byte Prime1[] = { + 0xE5, 0x62, 0xE1, 0x7F, 0x9F, 0x86, 0x08, 0xE2, 0x61, 0xD3, 0xD0, 0x42, 0xE2, 0xC4, 0xB6, + 0xA8, 0x51, 0x09, 0x19, 0x14, 0xA4, 0x3A, 0x11, 0x4C, 0x33, 0xA5, 0x9C, 0x01, 0x5E, 0x34, + 0xB6, 0x3F, 0x02, 0x1A, 0xCA, 0x47, 0xF1, 0x4F, 0x3B, 0x35, 0x2A, 0x07, 0x20, 0xEC, 0xD8, + 0xC1, 0x15, 0xD9, 0xCA, 0x03, 0x4F, 0xB8, 0xE8, 0x09, 0x73, 0x3F, 0x85, 0xB7, 0x41, 0xD5, + 0x51, 0x3E, 0x7B, 0xE3, 0x53, 0x2B, 0x48, 0x8B, 0x8E, 0xCB, 0xBA, 0xF7, 0xE0, 0x60, 0xF5, + 0x35, 0x0E, 0x6F, 0xB0, 0xD9, 0x2A, 0x99, 0xD0, 0xFF, 0x60, 0x14, 0xED, 0x40, 0xEA, 0xF8, + 0xD7, 0x0B, 0xC3, 0x8D, 0x8C, 0xE8, 0x81, 0xB3, 0x75, 0x93, 0x15, 0xB3, 0x7D, 0xF6, 0x39, + 0x60, 0x1A, 0x00, 0xE7, 0xC3, 0x27, 0xAD, 0xA4, 0x33, 0xD5, 0x3E, 0xA4, 0x35, 0x48, 0x6F, + 0x22, 0xEF, 0x5D, 0xDD, 0x7D, 0x7B, 0x61, 0x05}; + // q + static constexpr CryptoPP::byte Prime2[] = { + 0xD9, 0x6C, 0xC2, 0x0C, 0xF7, 0xAE, 0xD1, 0xF3, 0x3B, 0x3B, 0x49, 0x1E, 0x9F, 0x12, 0x9C, + 0xA1, 0x78, 0x1F, 0x35, 0x1D, 0x98, 0x26, 0x13, 0x71, 0xF9, 0x09, 0xFD, 0xF0, 0xAD, 0x38, + 0x55, 0xB7, 0xEE, 0x61, 0x04, 0x72, 0x51, 0x87, 0x2E, 0x05, 0x84, 0xB1, 0x1D, 0x0C, 0x0D, + 0xDB, 0xD4, 0x25, 0x3E, 0x26, 0xED, 0xEA, 0xB8, 0xF7, 0x49, 0xFE, 0xA2, 0x94, 0xE6, 0xF2, + 0x08, 0x92, 0xA7, 0x85, 0xF5, 0x30, 0xB9, 0x84, 0x22, 0xBF, 0xCA, 0xF0, 0x5F, 0xCB, 0x31, + 0x20, 0x34, 0x49, 0x16, 0x76, 0x34, 0xCC, 0x7A, 0xCB, 0x96, 0xFE, 0x78, 0x7A, 0x41, 0xFE, + 0x9A, 0xA2, 0x23, 0xF7, 0x68, 0x80, 0xD6, 0xCE, 0x4A, 0x78, 0xA5, 0xB7, 0x05, 0x77, 0x81, + 0x1F, 0xDE, 0x5E, 0xA8, 0x6E, 0x3E, 0x87, 0xEC, 0x44, 0xD2, 0x69, 0xC6, 0x54, 0x91, 0x6B, + 0x5E, 0x13, 0x8A, 0x03, 0x87, 0x05, 0x31, 0x8D}; + static constexpr CryptoPP::byte PrivateExponent[] = { + 0x01, 0x61, 0xAD, 0xD8, 0x9C, 0x06, 0x89, 0xD0, 0x60, 0xC8, 0x41, 0xF0, 0xB3, 0x83, 0x01, + 0x5D, 0xE3, 0xA2, 0x6B, 0xA2, 0xBA, 0x9A, 0x0A, 0x58, 0xCD, 0x1A, 0xA0, 0x97, 0x64, 0xEC, + 0xD0, 0x31, 0x1F, 0xCA, 0x36, 0x0E, 0x69, 0xDD, 0x40, 0xF7, 0x4E, 0xC0, 0xC6, 0xA3, 0x73, + 0xF0, 0x69, 0x84, 0xB2, 0xF4, 0x4B, 0x29, 0x14, 0x2A, 0x6D, 0xB8, 0x23, 0xD8, 0x1B, 0x61, + 0xD4, 0x9E, 0x87, 0xB3, 0xBB, 0xA9, 0xC4, 0x85, 0x4A, 0xF8, 0x03, 0x4A, 0xBF, 0xFE, 0xF9, + 0xFE, 0x8B, 0xDD, 0x54, 0x83, 0xBA, 0xE0, 0x2F, 0x3F, 0xB1, 0xEF, 0xA5, 0x05, 0x5D, 0x28, + 0x8B, 0xAB, 0xB5, 0xD0, 0x23, 0x2F, 0x8A, 0xCF, 0x48, 0x7C, 0xAA, 0xBB, 0xC8, 0x5B, 0x36, + 0x27, 0xC5, 0x16, 0xA4, 0xB6, 0x61, 0xAC, 0x0C, 0x28, 0x47, 0x79, 0x3F, 0x38, 0xAE, 0x5E, + 0x25, 0xC6, 0xAF, 0x35, 0xAE, 0xBC, 0xB0, 0xF3, 0xBC, 0xBD, 0xFD, 0xA4, 0x87, 0x0D, 0x14, + 0x3D, 0x90, 0xE4, 0xDE, 0x5D, 0x1D, 0x46, 0x81, 0xF1, 0x28, 0x6D, 0x2F, 0x2C, 0x5E, 0x97, + 0x2D, 0x89, 0x2A, 0x51, 0x72, 0x3C, 0x20, 0x02, 0x59, 0xB1, 0x98, 0x93, 0x05, 0x1E, 0x3F, + 0xA1, 0x8A, 0x69, 0x30, 0x0E, 0x70, 0x84, 0x8B, 0xAE, 0x97, 0xA1, 0x08, 0x95, 0x63, 0x4C, + 0xC7, 0xE8, 0x5D, 0x59, 0xCA, 0x78, 0x2A, 0x23, 0x87, 0xAC, 0x6F, 0x04, 0x33, 0xB1, 0x61, + 0xB9, 0xF0, 0x95, 0xDA, 0x33, 0xCC, 0xE0, 0x4C, 0x82, 0x68, 0x82, 0x14, 0x51, 0xBE, 0x49, + 0x1C, 0x58, 0xA2, 0x8B, 0x05, 0x4E, 0x98, 0x37, 0xEB, 0x94, 0x0B, 0x01, 0x22, 0xDC, 0xB3, + 0x19, 0xCA, 0x77, 0xA6, 0x6E, 0x97, 0xFF, 0x8A, 0x53, 0x5A, 0xC5, 0x24, 0xE4, 0xAF, 0x6E, + 0xA8, 0x2B, 0x53, 0xA4, 0xBE, 0x96, 0xA5, 0x7B, 0xCE, 0x22, 0x56, 0xA3, 0xF1, 0xCF, 0x14, + 0xA5}; +}; + +class PkgDerivedKey3Keyset { +public: + // std::uint8_t* PrivateExponent; + static constexpr CryptoPP::byte Exponent1[] = { + 0x52, 0xCC, 0x2D, 0xA0, 0x9C, 0x9E, 0x75, 0xE7, 0x28, 0xEE, 0x3D, 0xDE, 0xE3, 0x45, 0xD1, + 0x4F, 0x94, 0x1C, 0xCC, 0xC8, 0x87, 0x29, 0x45, 0x3B, 0x8D, 0x6E, 0xAB, 0x6E, 0x2A, 0xA7, + 0xC7, 0x15, 0x43, 0xA3, 0x04, 0x8F, 0x90, 0x5F, 0xEB, 0xF3, 0x38, 0x4A, 0x77, 0xFA, 0x36, + 0xB7, 0x15, 0x76, 0xB6, 0x01, 0x1A, 0x8E, 0x25, 0x87, 0x82, 0xF1, 0x55, 0xD8, 0xC6, 0x43, + 0x2A, 0xC0, 0xE5, 0x98, 0xC9, 0x32, 0xD1, 0x94, 0x6F, 0xD9, 0x01, 0xBA, 0x06, 0x81, 0xE0, + 0x6D, 0x88, 0xF2, 0x24, 0x2A, 0x25, 0x01, 0x64, 0x5C, 0xBF, 0xF2, 0xD9, 0x99, 0x67, 0x3E, + 0xF6, 0x72, 0xEE, 0xE4, 0xE2, 0x33, 0x5C, 0xF8, 0x00, 0x40, 0xE3, 0x2A, 0x9A, 0xF4, 0x3D, + 0x22, 0x86, 0x44, 0x3C, 0xFB, 0x0A, 0xA5, 0x7C, 0x3F, 0xCC, 0xF5, 0xF1, 0x16, 0xC4, 0xAC, + 0x88, 0xB4, 0xDE, 0x62, 0x94, 0x92, 0x6A, 0x13}; + // exponent2 = d mod (q - 1) + static constexpr CryptoPP::byte Exponent2[] = { + 0x7C, 0x9D, 0xAD, 0x39, 0xE0, 0xD5, 0x60, 0x14, 0x94, 0x48, 0x19, 0x7F, 0x88, 0x95, 0xD5, + 0x8B, 0x80, 0xAD, 0x85, 0x8A, 0x4B, 0x77, 0x37, 0x85, 0xD0, 0x77, 0xBB, 0xBF, 0x89, 0x71, + 0x4A, 0x72, 0xCB, 0x72, 0x68, 0x38, 0xEC, 0x02, 0xC6, 0x7D, 0xC6, 0x44, 0x06, 0x33, 0x51, + 0x1C, 0xC0, 0xFF, 0x95, 0x8F, 0x0D, 0x75, 0xDC, 0x25, 0xBB, 0x0B, 0x73, 0x91, 0xA9, 0x6D, + 0x42, 0xD8, 0x03, 0xB7, 0x68, 0xD4, 0x1E, 0x75, 0x62, 0xA3, 0x70, 0x35, 0x79, 0x78, 0x00, + 0xC8, 0xF5, 0xEF, 0x15, 0xB9, 0xFC, 0x4E, 0x47, 0x5A, 0xC8, 0x70, 0x70, 0x5B, 0x52, 0x98, + 0xC0, 0xC2, 0x58, 0x4A, 0x70, 0x96, 0xCC, 0xB8, 0x10, 0xE1, 0x2F, 0x78, 0x8B, 0x2B, 0xA1, + 0x7F, 0xF9, 0xAC, 0xDE, 0xF0, 0xBB, 0x2B, 0xE2, 0x66, 0xE3, 0x22, 0x92, 0x31, 0x21, 0x57, + 0x92, 0xC4, 0xB8, 0xF2, 0x3E, 0x76, 0x20, 0x37}; + // e + static constexpr CryptoPP::byte PublicExponent[] = {0, 1, 0, 1}; + // (InverseQ)(q) = 1 mod p + static constexpr CryptoPP::byte Coefficient[] = { + 0x45, 0x97, 0x55, 0xD4, 0x22, 0x08, 0x5E, 0xF3, 0x5C, 0xB4, 0x05, 0x7A, 0xFD, 0xAA, 0x42, + 0x42, 0xAD, 0x9A, 0x8C, 0xA0, 0x6C, 0xBB, 0x1D, 0x68, 0x54, 0x54, 0x6E, 0x3E, 0x32, 0xE3, + 0x53, 0x73, 0x76, 0xF1, 0x3E, 0x01, 0xEA, 0xD3, 0xCF, 0xEB, 0xEB, 0x23, 0x3E, 0xC0, 0xBE, + 0xCE, 0xEC, 0x2C, 0x89, 0x5F, 0xA8, 0x27, 0x3A, 0x4C, 0xB7, 0xE6, 0x74, 0xBC, 0x45, 0x4C, + 0x26, 0xC8, 0x25, 0xFF, 0x34, 0x63, 0x25, 0x37, 0xE1, 0x48, 0x10, 0xC1, 0x93, 0xA6, 0xAF, + 0xEB, 0xBA, 0xE3, 0xA2, 0xF1, 0x3D, 0xEF, 0x63, 0xD8, 0xF4, 0xFD, 0xD3, 0xEE, 0xE2, 0x5D, + 0xE9, 0x33, 0xCC, 0xAD, 0xBA, 0x75, 0x5C, 0x85, 0xAF, 0xCE, 0xA9, 0x3D, 0xD1, 0xA2, 0x17, + 0xF3, 0xF6, 0x98, 0xB3, 0x50, 0x8E, 0x5E, 0xF6, 0xEB, 0x02, 0x8E, 0xA1, 0x62, 0xA7, 0xD6, + 0x2C, 0xEC, 0x91, 0xFF, 0x15, 0x40, 0xD2, 0xE3}; + // n = p * q + static constexpr CryptoPP::byte Modulus[] = { + 0xd2, 0x12, 0xfc, 0x33, 0x5f, 0x6d, 0xdb, 0x83, 0x16, 0x09, 0x62, 0x8b, 0x03, 0x56, 0x27, + 0x37, 0x82, 0xd4, 0x77, 0x85, 0x35, 0x29, 0x39, 0x2d, 0x52, 0x6b, 0x8c, 0x4c, 0x8c, 0xfb, + 0x06, 0xc1, 0x84, 0x5b, 0xe7, 0xd4, 0xf7, 0xbc, 0xd2, 0x4e, 0x62, 0x45, 0xcd, 0x2a, 0xbb, + 0xd7, 0x77, 0x76, 0x45, 0x36, 0x55, 0x27, 0x3f, 0xb3, 0xf5, 0xf9, 0x8e, 0xda, 0x4b, 0xef, + 0xaa, 0x59, 0xae, 0xb3, 0x9b, 0xea, 0x54, 0x98, 0xd2, 0x06, 0x32, 0x6a, 0x58, 0x31, 0x2a, + 0xe0, 0xd4, 0x4f, 0x90, 0xb5, 0x0a, 0x7d, 0xec, 0xf4, 0x3a, 0x9c, 0x52, 0x67, 0x2d, 0x99, + 0x31, 0x8e, 0x0c, 0x43, 0xe6, 0x82, 0xfe, 0x07, 0x46, 0xe1, 0x2e, 0x50, 0xd4, 0x1f, 0x2d, + 0x2f, 0x7e, 0xd9, 0x08, 0xba, 0x06, 0xb3, 0xbf, 0x2e, 0x20, 0x3f, 0x4e, 0x3f, 0xfe, 0x44, + 0xff, 0xaa, 0x50, 0x43, 0x57, 0x91, 0x69, 0x94, 0x49, 0x15, 0x82, 0x82, 0xe4, 0x0f, 0x4c, + 0x8d, 0x9d, 0x2c, 0xc9, 0x5b, 0x1d, 0x64, 0xbf, 0x88, 0x8b, 0xd4, 0xc5, 0x94, 0xe7, 0x65, + 0x47, 0x84, 0x1e, 0xe5, 0x79, 0x10, 0xfb, 0x98, 0x93, 0x47, 0xb9, 0x7d, 0x85, 0x12, 0xa6, + 0x40, 0x98, 0x2c, 0xf7, 0x92, 0xbc, 0x95, 0x19, 0x32, 0xed, 0xe8, 0x90, 0x56, 0x0d, 0x65, + 0xc1, 0xaa, 0x78, 0xc6, 0x2e, 0x54, 0xfd, 0x5f, 0x54, 0xa1, 0xf6, 0x7e, 0xe5, 0xe0, 0x5f, + 0x61, 0xc1, 0x20, 0xb4, 0xb9, 0xb4, 0x33, 0x08, 0x70, 0xe4, 0xdf, 0x89, 0x56, 0xed, 0x01, + 0x29, 0x46, 0x77, 0x5f, 0x8c, 0xb8, 0xa9, 0xf5, 0x1e, 0x2e, 0xb3, 0xb9, 0xbf, 0xe0, 0x09, + 0xb7, 0x8d, 0x28, 0xd4, 0xa6, 0xc3, 0xb8, 0x1e, 0x1f, 0x07, 0xeb, 0xb4, 0x12, 0x0b, 0x95, + 0xb8, 0x85, 0x30, 0xfd, 0xdc, 0x39, 0x13, 0xd0, 0x7c, 0xdc, 0x8f, 0xed, 0xf9, 0xc9, 0xa3, + 0xc1}; + // p + static constexpr CryptoPP::byte Prime1[] = { + 0xF9, 0x67, 0xAD, 0x99, 0x12, 0x31, 0x0C, 0x56, 0xA2, 0x2E, 0x16, 0x1C, 0x46, 0xB3, 0x4D, + 0x5B, 0x43, 0xBE, 0x42, 0xA2, 0xF6, 0x86, 0x96, 0x80, 0x42, 0xC3, 0xC7, 0x3F, 0xC3, 0x42, + 0xF5, 0x87, 0x49, 0x33, 0x9F, 0x07, 0x5D, 0x6E, 0x2C, 0x04, 0xFD, 0xE3, 0xE1, 0xB2, 0xAE, + 0x0A, 0x0C, 0xF0, 0xC7, 0xA6, 0x1C, 0xA1, 0x63, 0x50, 0xC8, 0x09, 0x9C, 0x51, 0x24, 0x52, + 0x6C, 0x5E, 0x5E, 0xBD, 0x1E, 0x27, 0x06, 0xBB, 0xBC, 0x9E, 0x94, 0xE1, 0x35, 0xD4, 0x6D, + 0xB3, 0xCB, 0x3C, 0x68, 0xDD, 0x68, 0xB3, 0xFE, 0x6C, 0xCB, 0x8D, 0x82, 0x20, 0x76, 0x23, + 0x63, 0xB7, 0xE9, 0x68, 0x10, 0x01, 0x4E, 0xDC, 0xBA, 0x27, 0x5D, 0x01, 0xC1, 0x2D, 0x80, + 0x5E, 0x2B, 0xAF, 0x82, 0x6B, 0xD8, 0x84, 0xB6, 0x10, 0x52, 0x86, 0xA7, 0x89, 0x8E, 0xAE, + 0x9A, 0xE2, 0x89, 0xC6, 0xF7, 0xD5, 0x87, 0xFB}; + // q + static constexpr CryptoPP::byte Prime2[] = { + 0xD7, 0xA1, 0x0F, 0x9A, 0x8B, 0xF2, 0xC9, 0x11, 0x95, 0x32, 0x9A, 0x8C, 0xF0, 0xD9, 0x40, + 0x47, 0xF5, 0x68, 0xA0, 0x0D, 0xBD, 0xC1, 0xFC, 0x43, 0x2F, 0x65, 0xF9, 0xC3, 0x61, 0x0F, + 0x25, 0x77, 0x54, 0xAD, 0xD7, 0x58, 0xAC, 0x84, 0x40, 0x60, 0x8D, 0x3F, 0xF3, 0x65, 0x89, + 0x75, 0xB5, 0xC6, 0x2C, 0x51, 0x1A, 0x2F, 0x1F, 0x22, 0xE4, 0x43, 0x11, 0x54, 0xBE, 0xC9, + 0xB4, 0xC7, 0xB5, 0x1B, 0x05, 0x0B, 0xBC, 0x56, 0x9A, 0xCD, 0x4A, 0xD9, 0x73, 0x68, 0x5E, + 0x5C, 0xFB, 0x92, 0xB7, 0x8B, 0x0D, 0xFF, 0xF5, 0x07, 0xCA, 0xB4, 0xC8, 0x9B, 0x96, 0x3C, + 0x07, 0x9E, 0x3E, 0x6B, 0x2A, 0x11, 0xF2, 0x8A, 0xB1, 0x8A, 0xD7, 0x2E, 0x1B, 0xA5, 0x53, + 0x24, 0x06, 0xED, 0x50, 0xB8, 0x90, 0x67, 0xB1, 0xE2, 0x41, 0xC6, 0x92, 0x01, 0xEE, 0x10, + 0xF0, 0x61, 0xBB, 0xFB, 0xB2, 0x7D, 0x4A, 0x73}; + static constexpr CryptoPP::byte PrivateExponent[] = { + 0x32, 0xD9, 0x03, 0x90, 0x8F, 0xBD, 0xB0, 0x8F, 0x57, 0x2B, 0x28, 0x5E, 0x0B, 0x8D, 0xB3, + 0xEA, 0x5C, 0xD1, 0x7E, 0xA8, 0x90, 0x88, 0x8C, 0xDD, 0x6A, 0x80, 0xBB, 0xB1, 0xDF, 0xC1, + 0xF7, 0x0D, 0xAA, 0x32, 0xF0, 0xB7, 0x7C, 0xCB, 0x88, 0x80, 0x0E, 0x8B, 0x64, 0xB0, 0xBE, + 0x4C, 0xD6, 0x0E, 0x9B, 0x8C, 0x1E, 0x2A, 0x64, 0xE1, 0xF3, 0x5C, 0xD7, 0x76, 0x01, 0x41, + 0x5E, 0x93, 0x5C, 0x94, 0xFE, 0xDD, 0x46, 0x62, 0xC3, 0x1B, 0x5A, 0xE2, 0xA0, 0xBC, 0x2D, + 0xEB, 0xC3, 0x98, 0x0A, 0xA7, 0xB7, 0x85, 0x69, 0x70, 0x68, 0x2B, 0x64, 0x4A, 0xB3, 0x1F, + 0xCC, 0x7D, 0xDC, 0x7C, 0x26, 0xF4, 0x77, 0xF6, 0x5C, 0xF2, 0xAE, 0x5A, 0x44, 0x2D, 0xD3, + 0xAB, 0x16, 0x62, 0x04, 0x19, 0xBA, 0xFB, 0x90, 0xFF, 0xE2, 0x30, 0x50, 0x89, 0x6E, 0xCB, + 0x56, 0xB2, 0xEB, 0xC0, 0x91, 0x16, 0x92, 0x5E, 0x30, 0x8E, 0xAE, 0xC7, 0x94, 0x5D, 0xFD, + 0x35, 0xE1, 0x20, 0xF8, 0xAD, 0x3E, 0xBC, 0x08, 0xBF, 0xC0, 0x36, 0x74, 0x9F, 0xD5, 0xBB, + 0x52, 0x08, 0xFD, 0x06, 0x66, 0xF3, 0x7A, 0xB3, 0x04, 0xF4, 0x75, 0x29, 0x5D, 0xE9, 0x5F, + 0xAA, 0x10, 0x30, 0xB2, 0x0F, 0x5A, 0x1A, 0xC1, 0x2A, 0xB3, 0xFE, 0xCB, 0x21, 0xAD, 0x80, + 0xEC, 0x8F, 0x20, 0x09, 0x1C, 0xDB, 0xC5, 0x58, 0x94, 0xC2, 0x9C, 0xC6, 0xCE, 0x82, 0x65, + 0x3E, 0x57, 0x90, 0xBC, 0xA9, 0x8B, 0x06, 0xB4, 0xF0, 0x72, 0xF6, 0x77, 0xDF, 0x98, 0x64, + 0xF1, 0xEC, 0xFE, 0x37, 0x2D, 0xBC, 0xAE, 0x8C, 0x08, 0x81, 0x1F, 0xC3, 0xC9, 0x89, 0x1A, + 0xC7, 0x42, 0x82, 0x4B, 0x2E, 0xDC, 0x8E, 0x8D, 0x73, 0xCE, 0xB1, 0xCC, 0x01, 0xD9, 0x08, + 0x70, 0x87, 0x3C, 0x44, 0x08, 0xEC, 0x49, 0x8F, 0x81, 0x5A, 0xE2, 0x40, 0xFF, 0x77, 0xFC, + 0x0D}; +}; \ No newline at end of file diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp new file mode 100644 index 000000000..ecc5f10a4 --- /dev/null +++ b/src/core/file_format/pkg.cpp @@ -0,0 +1,473 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/io_file.h" +#include "common/logging/formatter.h" +#include "core/file_format/pkg.h" +#include "core/file_format/pkg_type.h" + +static void DecompressPFSC(std::span compressed_data, std::span decompressed_data) { + z_stream decompressStream; + decompressStream.zalloc = Z_NULL; + decompressStream.zfree = Z_NULL; + decompressStream.opaque = Z_NULL; + + if (inflateInit(&decompressStream) != Z_OK) { + // std::cerr << "Error initializing zlib for deflation." << std::endl; + } + + decompressStream.avail_in = compressed_data.size(); + decompressStream.next_in = reinterpret_cast(compressed_data.data()); + decompressStream.avail_out = decompressed_data.size(); + decompressStream.next_out = reinterpret_cast(decompressed_data.data()); + + if (inflate(&decompressStream, Z_FINISH)) { + } + if (inflateEnd(&decompressStream) != Z_OK) { + // std::cerr << "Error ending zlib inflate" << std::endl; + } +} + +u32 GetPFSCOffset(std::span pfs_image) { + static constexpr u32 PfscMagic = 0x43534650; + u32 value; + for (u32 i = 0x20000; i < pfs_image.size(); i += 0x10000) { + std::memcpy(&value, &pfs_image[i], sizeof(u32)); + if (value == PfscMagic) + return i; + } + return -1; +} + +PKG::PKG() = default; + +PKG::~PKG() = default; + +bool PKG::Open(const std::filesystem::path& filepath, std::string& failreason) { + Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); + if (!file.IsOpen()) { + return false; + } + pkgSize = file.GetSize(); + + file.Read(pkgheader); + if (pkgheader.magic != 0x7F434E54) + return false; + + for (const auto& flag : flagNames) { + if (isFlagSet(pkgheader.pkg_content_flags, flag.first)) { + if (!pkgFlags.empty()) + pkgFlags += (", "); + pkgFlags += (flag.second); + } + } + + // Find title id it is part of pkg_content_id starting at offset 0x40 + file.Seek(0x47); // skip first 7 characters of content_id + file.Read(pkgTitleID); + + u32 offset = pkgheader.pkg_table_entry_offset; + u32 n_files = pkgheader.pkg_table_entry_count; + + if (!file.Seek(offset)) { + failreason = "Failed to seek to PKG table entry offset"; + return false; + } + + for (int i = 0; i < n_files; i++) { + PKGEntry entry{}; + file.Read(entry.id); + file.Read(entry.filename_offset); + file.Read(entry.flags1); + file.Read(entry.flags2); + file.Read(entry.offset); + file.Read(entry.size); + file.Seek(8, Common::FS::SeekOrigin::CurrentPosition); + + // Try to figure out the name + const auto name = GetEntryNameByType(entry.id); + if (name == "param.sfo") { + sfo.clear(); + if (!file.Seek(entry.offset)) { + failreason = "Failed to seek to param.sfo offset"; + return false; + } + sfo.resize(entry.size); + file.ReadRaw(sfo.data(), entry.size); + } + } + file.Close(); + + return true; +} + +bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem::path& extract, + std::string& failreason) { + extract_path = extract; + pkgpath = filepath; + Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); + if (!file.IsOpen()) { + return false; + } + pkgSize = file.GetSize(); + file.ReadRaw(&pkgheader, sizeof(PKGHeader)); + + if (pkgheader.magic != 0x7F434E54) + return false; + + if (pkgheader.pkg_size > pkgSize) { + failreason = "PKG file size is different"; + return false; + } + if ((pkgheader.pkg_content_size + pkgheader.pkg_content_offset) > pkgheader.pkg_size) { + failreason = "Content size is bigger than pkg size"; + return false; + } + + u32 offset = pkgheader.pkg_table_entry_offset; + u32 n_files = pkgheader.pkg_table_entry_count; + + std::array concatenated_ivkey_dk3; + std::array seed_digest; + std::array, 7> digest1; + std::array, 7> key1; + std::array imgkeydata; + + if (!file.Seek(offset)) { + failreason = "Failed to seek to PKG table entry offset"; + return false; + } + + for (int i = 0; i < n_files; i++) { + PKGEntry entry{}; + file.Read(entry.id); + file.Read(entry.filename_offset); + file.Read(entry.flags1); + file.Read(entry.flags2); + file.Read(entry.offset); + file.Read(entry.size); + file.Seek(8, Common::FS::SeekOrigin::CurrentPosition); + + auto currentPos = file.Tell(); + + // Try to figure out the name + const auto name = GetEntryNameByType(entry.id); + const auto filepath = extract_path / "sce_sys" / name; + std::filesystem::create_directories(filepath.parent_path()); + + if (name.empty()) { + // Just print with id + Common::FS::IOFile out(extract_path / "sce_sys" / std::to_string(entry.id), + Common::FS::FileAccessMode::Write); + if (!file.Seek(entry.offset)) { + failreason = "Failed to seek to PKG entry offset"; + return false; + } + + std::vector data; + data.resize(entry.size); + file.ReadRaw(data.data(), entry.size); + out.WriteRaw(data.data(), entry.size); + out.Close(); + + file.Seek(currentPos); + continue; + } + + if (entry.id == 0x1) { // DIGESTS, seek; + // file.Seek(entry.offset, fsSeekSet); + } else if (entry.id == 0x10) { // ENTRY_KEYS, seek; + file.Seek(entry.offset); + file.Read(seed_digest); + + for (int i = 0; i < 7; i++) { + file.Read(digest1[i]); + } + + for (int i = 0; i < 7; i++) { + file.Read(key1[i]); + } + + PKG::crypto.RSA2048Decrypt(dk3_, key1[3], true); // decrypt DK3 + } else if (entry.id == 0x20) { // IMAGE_KEY, seek; IV_KEY + file.Seek(entry.offset); + file.Read(imgkeydata); + + // The Concatenated iv + dk3 imagekey for HASH256 + std::memcpy(concatenated_ivkey_dk3.data(), &entry, sizeof(entry)); + std::memcpy(concatenated_ivkey_dk3.data() + sizeof(entry), dk3_.data(), sizeof(dk3_)); + + PKG::crypto.ivKeyHASH256(concatenated_ivkey_dk3, ivKey); // ivkey_ + // imgkey_ to use for last step to get ekpfs + PKG::crypto.aesCbcCfb128Decrypt(ivKey, imgkeydata, imgKey); + // ekpfs key to get data and tweak keys. + PKG::crypto.RSA2048Decrypt(ekpfsKey, imgKey, false); + } else if (entry.id == 0x80) { + // GENERAL_DIGESTS, seek; + // file.Seek(entry.offset, fsSeekSet); + } + + Common::FS::IOFile out(extract_path / "sce_sys" / name, Common::FS::FileAccessMode::Write); + if (!file.Seek(entry.offset)) { + failreason = "Failed to seek to PKG entry offset"; + return false; + } + + std::vector data; + data.resize(entry.size); + file.ReadRaw(data.data(), entry.size); + out.WriteRaw(data.data(), entry.size); + out.Close(); + + // Decrypt Np stuff and overwrite. + if (entry.id == 0x400 || entry.id == 0x401 || entry.id == 0x402 || + entry.id == 0x403) { // somehow 0x401 is not decrypting + decNp.resize(entry.size); + if (!file.Seek(entry.offset)) { + failreason = "Failed to seek to PKG entry offset"; + return false; + } + + std::vector data; + data.resize(entry.size); + file.ReadRaw(data.data(), entry.size); + + std::span cipherNp(data.data(), entry.size); + std::array concatenated_ivkey_dk3_; + std::memcpy(concatenated_ivkey_dk3_.data(), &entry, sizeof(entry)); + std::memcpy(concatenated_ivkey_dk3_.data() + sizeof(entry), dk3_.data(), sizeof(dk3_)); + PKG::crypto.ivKeyHASH256(concatenated_ivkey_dk3_, ivKey); + PKG::crypto.aesCbcCfb128DecryptEntry(ivKey, cipherNp, decNp); + + Common::FS::IOFile out(extract_path / "sce_sys" / name, + Common::FS::FileAccessMode::Write); + out.Write(decNp); + out.Close(); + } + + file.Seek(currentPos); + } + + // Read the seed + std::array seed; + if (!file.Seek(pkgheader.pfs_image_offset + 0x370)) { + failreason = "Failed to seek to PFS image offset"; + return false; + } + file.Read(seed); + + // Get data and tweak keys. + PKG::crypto.PfsGenCryptoKey(ekpfsKey, seed, dataKey, tweakKey); + const u32 length = pkgheader.pfs_cache_size * 0x2; // Seems to be ok. + + int num_blocks = 0; + std::vector pfsc(length); + if (length != 0) { + // Read encrypted pfs_image + std::vector pfs_encrypted(length); + file.Seek(pkgheader.pfs_image_offset); + file.Read(pfs_encrypted); + file.Close(); + // Decrypt the pfs_image. + std::vector pfs_decrypted(length); + PKG::crypto.decryptPFS(dataKey, tweakKey, pfs_encrypted, pfs_decrypted, 0); + + // Retrieve PFSC from decrypted pfs_image. + pfsc_offset = GetPFSCOffset(pfs_decrypted); + std::memcpy(pfsc.data(), pfs_decrypted.data() + pfsc_offset, length - pfsc_offset); + + PFSCHdr pfsChdr; + std::memcpy(&pfsChdr, pfsc.data(), sizeof(pfsChdr)); + + num_blocks = (int)(pfsChdr.data_length / pfsChdr.block_sz2); + sectorMap.resize(num_blocks + 1); // 8 bytes, need extra 1 to get the last offset. + + for (int i = 0; i < num_blocks + 1; i++) { + std::memcpy(§orMap[i], pfsc.data() + pfsChdr.block_offsets + i * 8, 8); + } + } + + u32 ent_size = 0; + u32 ndinode = 0; + int ndinode_counter = 0; + bool dinode_reached = false; + bool uroot_reached = false; + std::vector compressedData; + std::vector decompressedData(0x10000); + + // Get iNdoes and Dirents. + for (int i = 0; i < num_blocks; i++) { + const u64 sectorOffset = sectorMap[i]; + const u64 sectorSize = sectorMap[i + 1] - sectorOffset; + + compressedData.resize(sectorSize); + std::memcpy(compressedData.data(), pfsc.data() + sectorOffset, sectorSize); + + if (sectorSize == 0x10000) // Uncompressed data + std::memcpy(decompressedData.data(), compressedData.data(), 0x10000); + else if (sectorSize < 0x10000) // Compressed data + DecompressPFSC(compressedData, decompressedData); + + if (i == 0) { + std::memcpy(&ndinode, decompressedData.data() + 0x30, 4); // number of folders and files + } + + int occupied_blocks = + (ndinode * 0xA8) / 0x10000; // how many blocks(0x10000) are taken by iNodes. + if (((ndinode * 0xA8) % 0x10000) != 0) + occupied_blocks += 1; + + if (i >= 1 && i <= occupied_blocks) { // Get all iNodes, gives type, file size and location. + for (int p = 0; p < 0x10000; p += 0xA8) { + Inode node; + std::memcpy(&node, &decompressedData[p], sizeof(node)); + if (node.Mode == 0) { + break; + } + iNodeBuf.push_back(node); + } + } + + // let's deal with the root/uroot entries here. + // Sometimes it's more than 2 entries (Tomb Raider Remastered) + const std::string_view flat_path_table(&decompressedData[0x10], 15); + if (flat_path_table == "flat_path_table") { + uroot_reached = true; + } + + if (uroot_reached) { + for (int i = 0; i < 0x10000; i += ent_size) { + Dirent dirent; + std::memcpy(&dirent, &decompressedData[i], sizeof(dirent)); + ent_size = dirent.entsize; + if (dirent.ino != 0) { + ndinode_counter++; + } else { + // Set the the folder according to the current inode. + // Can be 2 or more (rarely) + auto parent_path = extract_path.parent_path(); + auto title_id = GetTitleID(); + + if (parent_path.filename() != title_id && + !fmt::UTF(extract_path.u8string()).data.ends_with("-patch")) { + extractPaths[ndinode_counter] = parent_path / title_id; + } else { + // DLCs path has different structure + extractPaths[ndinode_counter] = extract_path; + } + uroot_reached = false; + break; + } + } + } + + const char dot = decompressedData[0x10]; + const std::string_view dotdot(&decompressedData[0x28], 2); + if (dot == '.' && dotdot == "..") { + dinode_reached = true; + } + + // Get folder and file names. + bool end_reached = false; + if (dinode_reached) { + for (int j = 0; j < 0x10000; j += ent_size) { // Skip the first parent and child. + Dirent dirent; + std::memcpy(&dirent, &decompressedData[j], sizeof(dirent)); + + // Stop here and continue the main loop + if (dirent.ino == 0) { + break; + } + + ent_size = dirent.entsize; + auto& table = fsTable.emplace_back(); + table.name = std::string(dirent.name, dirent.namelen); + table.inode = dirent.ino; + table.type = dirent.type; + + if (table.type == PFS_CURRENT_DIR) { + current_dir = extractPaths[table.inode]; + } + extractPaths[table.inode] = current_dir / std::filesystem::path(table.name); + + if (table.type == PFS_FILE || table.type == PFS_DIR) { + if (table.type == PFS_DIR) { // Create dirs. + std::filesystem::create_directory(extractPaths[table.inode]); + } + ndinode_counter++; + if ((ndinode_counter + 1) == ndinode) // 1 for the image itself (root). + end_reached = true; + } + } + if (end_reached) { + break; + } + } + } + return true; +} + +void PKG::ExtractFiles(const int index) { + int inode_number = fsTable[index].inode; + int inode_type = fsTable[index].type; + std::string inode_name = fsTable[index].name; + + if (inode_type == PFS_FILE) { + int sector_loc = iNodeBuf[inode_number].loc; + int nblocks = iNodeBuf[inode_number].Blocks; + int bsize = iNodeBuf[inode_number].Size; + + Common::FS::IOFile inflated; + inflated.Open(extractPaths[inode_number], Common::FS::FileAccessMode::Write); + + Common::FS::IOFile pkgFile; // Open the file for each iteration to avoid conflict. + pkgFile.Open(pkgpath, Common::FS::FileAccessMode::Read); + + int size_decompressed = 0; + std::vector compressedData; + std::vector decompressedData(0x10000); + + u64 pfsc_buf_size = 0x11000; // extra 0x1000 + std::vector pfsc(pfsc_buf_size); + std::vector pfs_decrypted(pfsc_buf_size); + + for (int j = 0; j < nblocks; j++) { + u64 sectorOffset = + sectorMap[sector_loc + j]; // offset into PFSC_image and not pfs_image. + u64 sectorSize = sectorMap[sector_loc + j + 1] - + sectorOffset; // indicates if data is compressed or not. + u64 fileOffset = (pkgheader.pfs_image_offset + pfsc_offset + sectorOffset); + u64 currentSector1 = + (pfsc_offset + sectorOffset) / 0x1000; // block size is 0x1000 for xts decryption. + + int sectorOffsetMask = (sectorOffset + pfsc_offset) & 0xFFFFF000; + int previousData = (sectorOffset + pfsc_offset) - sectorOffsetMask; + + pkgFile.Seek(fileOffset - previousData); + pkgFile.Read(pfsc); + + PKG::crypto.decryptPFS(dataKey, tweakKey, pfsc, pfs_decrypted, currentSector1); + + compressedData.resize(sectorSize); + std::memcpy(compressedData.data(), pfs_decrypted.data() + previousData, sectorSize); + + if (sectorSize == 0x10000) // Uncompressed data + std::memcpy(decompressedData.data(), compressedData.data(), 0x10000); + else if (sectorSize < 0x10000) // Compressed data + DecompressPFSC(compressedData, decompressedData); + + size_decompressed += 0x10000; + + if (j < nblocks - 1) { + inflated.WriteRaw(decompressedData.data(), decompressedData.size()); + } else { + // This is to remove the zeros at the end of the file. + const u32 write_size = decompressedData.size() - (size_decompressed - bsize); + inflated.WriteRaw(decompressedData.data(), write_size); + } + } + pkgFile.Close(); + inflated.Close(); + } +} diff --git a/src/core/file_format/pkg.h b/src/core/file_format/pkg.h new file mode 100644 index 000000000..a488a2df8 --- /dev/null +++ b/src/core/file_format/pkg.h @@ -0,0 +1,174 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include "common/endian.h" +#include "core/crypto/crypto.h" +#include "pfs.h" +#include "trp.h" + +struct PKGHeader { + u32_be magic; // Magic + u32_be pkg_type; + u32_be pkg_0x8; // unknown field + u32_be pkg_file_count; + u32_be pkg_table_entry_count; + u16_be pkg_sc_entry_count; + u16_be pkg_table_entry_count_2; // same as pkg_entry_count + u32_be pkg_table_entry_offset; // file table offset + u32_be pkg_sc_entry_data_size; + u64_be pkg_body_offset; // offset of PKG entries + u64_be pkg_body_size; // length of all PKG entries + u64_be pkg_content_offset; + u64_be pkg_content_size; + u8 pkg_content_id[0x24]; // packages' content ID as a 36-byte string + u8 pkg_padding[0xC]; // padding + u32_be pkg_drm_type; // DRM type + u32_be pkg_content_type; // Content type + u32_be pkg_content_flags; // Content flags + u32_be pkg_promote_size; + u32_be pkg_version_date; + u32_be pkg_version_hash; + u32_be pkg_0x088; + u32_be pkg_0x08C; + u32_be pkg_0x090; + u32_be pkg_0x094; + u32_be pkg_iro_tag; + u32_be pkg_drm_type_version; + + u8 pkg_zeroes_1[0x60]; + + /* Digest table */ + u8 digest_entries1[0x20]; // sha256 digest for main entry 1 + u8 digest_entries2[0x20]; // sha256 digest for main entry 2 + u8 digest_table_digest[0x20]; // sha256 digest for digest table + u8 digest_body_digest[0x20]; // sha256 digest for main table + + u8 pkg_zeroes_2[0x280]; + + u32_be pkg_0x400; + + u32_be pfs_image_count; // count of PFS images + u64_be pfs_image_flags; // PFS flags + u64_be pfs_image_offset; // offset to start of external PFS image + u64_be pfs_image_size; // size of external PFS image + u64_be mount_image_offset; + u64_be mount_image_size; + u64_be pkg_size; + u32_be pfs_signed_size; + u32_be pfs_cache_size; + u8 pfs_image_digest[0x20]; + u8 pfs_signed_digest[0x20]; + u64_be pfs_split_size_nth_0; + u64_be pfs_split_size_nth_1; + + u8 pkg_zeroes_3[0xB50]; + + u8 pkg_digest[0x20]; +}; + +enum class PKGContentFlag { + FIRST_PATCH = 0x100000, + PATCHGO = 0x200000, + REMASTER = 0x400000, + PS_CLOUD = 0x800000, + GD_AC = 0x2000000, + NON_GAME = 0x4000000, + UNKNOWN_0x8000000 = 0x8000000, + SUBSEQUENT_PATCH = 0x40000000, + DELTA_PATCH = 0x41000000, + CUMULATIVE_PATCH = 0x60000000 +}; + +struct PKGEntry { + u32_be id; // File ID, useful for files without a filename entry + u32_be filename_offset; // Offset into the filenames table (ID 0x200) where this file's name is + // located + u32_be flags1; // Flags including encrypted flag, etc + u32_be flags2; // Flags including encryption key index, etc + u32_be offset; // Offset into PKG to find the file + u32_be size; // Size of the file + u64_be padding; // blank padding +}; +static_assert(sizeof(PKGEntry) == 32); + +class PKG { +public: + PKG(); + ~PKG(); + + bool Open(const std::filesystem::path& filepath, std::string& failreason); + void ExtractFiles(const int index); + bool Extract(const std::filesystem::path& filepath, const std::filesystem::path& extract, + std::string& failreason); + + std::vector sfo; + + u32 GetNumberOfFiles() { + return fsTable.size(); + } + + u64 GetPkgSize() { + return pkgSize; + } + + std::string GetPkgFlags() { + return pkgFlags; + } + + std::string_view GetTitleID() { + return std::string_view(pkgTitleID, 9); + } + + PKGHeader GetPkgHeader() { + return pkgheader; + } + + static bool isFlagSet(u32_be variable, PKGContentFlag flag) { + return (variable) & static_cast(flag); + } + + static constexpr std::array, 10> flagNames = { + {{PKGContentFlag::FIRST_PATCH, "FIRST_PATCH"}, + {PKGContentFlag::PATCHGO, "PATCHGO"}, + {PKGContentFlag::REMASTER, "REMASTER"}, + {PKGContentFlag::PS_CLOUD, "PS_CLOUD"}, + {PKGContentFlag::GD_AC, "GD_AC"}, + {PKGContentFlag::NON_GAME, "NON_GAME"}, + {PKGContentFlag::UNKNOWN_0x8000000, "UNKNOWN_0x8000000"}, + {PKGContentFlag::SUBSEQUENT_PATCH, "SUBSEQUENT_PATCH"}, + {PKGContentFlag::DELTA_PATCH, "DELTA_PATCH"}, + {PKGContentFlag::CUMULATIVE_PATCH, "CUMULATIVE_PATCH"}}}; + +private: + Crypto crypto; + TRP trp; + u64 pkgSize = 0; + char pkgTitleID[9]; + PKGHeader pkgheader; + std::string pkgFlags; + + std::unordered_map extractPaths; + std::vector fsTable; + std::vector iNodeBuf; + std::vector sectorMap; + u64 pfsc_offset; + + std::array dk3_; + std::array ivKey; + std::array imgKey; + std::array ekpfsKey; + std::array dataKey; + std::array tweakKey; + std::vector decNp; + + std::filesystem::path pkgpath; + std::filesystem::path current_dir; + std::filesystem::path extract_path; +}; diff --git a/src/core/file_format/pkg_type.cpp b/src/core/file_format/pkg_type.cpp new file mode 100644 index 000000000..464f0b993 --- /dev/null +++ b/src/core/file_format/pkg_type.cpp @@ -0,0 +1,638 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "pkg_type.h" + +struct PkgEntryValue { + u32 type; + std::string_view name; + + operator u32() const noexcept { + return type; + } +}; + +constexpr static std::array PkgEntries = {{ + {0x0001, "digests"}, + {0x0010, "entry_keys"}, + {0x0020, "image_key"}, + {0x0080, "general_digests"}, + {0x0100, "metas"}, + {0x0200, "entry_names"}, + {0x0400, "license.dat"}, + {0x0401, "license.info"}, + {0x0402, "nptitle.dat"}, + {0x0403, "npbind.dat"}, + {0x0404, "selfinfo.dat"}, + {0x0406, "imageinfo.dat"}, + {0x0407, "target-deltainfo.dat"}, + {0x0408, "origin-deltainfo.dat"}, + {0x0409, "psreserved.dat"}, + {0x1000, "param.sfo"}, + {0x1001, "playgo-chunk.dat"}, + {0x1002, "playgo-chunk.sha"}, + {0x1003, "playgo-manifest.xml"}, + {0x1004, "pronunciation.xml"}, + {0x1005, "pronunciation.sig"}, + {0x1006, "pic1.png"}, + {0x1007, "pubtoolinfo.dat"}, + {0x1008, "app/playgo-chunk.dat"}, + {0x1009, "app/playgo-chunk.sha"}, + {0x100A, "app/playgo-manifest.xml"}, + {0x100B, "shareparam.json"}, + {0x100C, "shareoverlayimage.png"}, + {0x100D, "save_data.png"}, + {0x100E, "shareprivacyguardimage.png"}, + {0x1200, "icon0.png"}, + {0x1201, "icon0_00.png"}, + {0x1202, "icon0_01.png"}, + {0x1203, "icon0_02.png"}, + {0x1204, "icon0_03.png"}, + {0x1205, "icon0_04.png"}, + {0x1206, "icon0_05.png"}, + {0x1207, "icon0_06.png"}, + {0x1208, "icon0_07.png"}, + {0x1209, "icon0_08.png"}, + {0x120A, "icon0_09.png"}, + {0x120B, "icon0_10.png"}, + {0x120C, "icon0_11.png"}, + {0x120D, "icon0_12.png"}, + {0x120E, "icon0_13.png"}, + {0x120F, "icon0_14.png"}, + {0x1210, "icon0_15.png"}, + {0x1211, "icon0_16.png"}, + {0x1212, "icon0_17.png"}, + {0x1213, "icon0_18.png"}, + {0x1214, "icon0_19.png"}, + {0x1215, "icon0_20.png"}, + {0x1216, "icon0_21.png"}, + {0x1217, "icon0_22.png"}, + {0x1218, "icon0_23.png"}, + {0x1219, "icon0_24.png"}, + {0x121A, "icon0_25.png"}, + {0x121B, "icon0_26.png"}, + {0x121C, "icon0_27.png"}, + {0x121D, "icon0_28.png"}, + {0x121E, "icon0_29.png"}, + {0x121F, "icon0_30.png"}, + {0x1220, "pic0.png"}, + {0x1240, "snd0.at9"}, + {0x1241, "pic1_00.png"}, + {0x1242, "pic1_01.png"}, + {0x1243, "pic1_02.png"}, + {0x1244, "pic1_03.png"}, + {0x1245, "pic1_04.png"}, + {0x1246, "pic1_05.png"}, + {0x1247, "pic1_06.png"}, + {0x1248, "pic1_07.png"}, + {0x1249, "pic1_08.png"}, + {0x124A, "pic1_09.png"}, + {0x124B, "pic1_10.png"}, + {0x124C, "pic1_11.png"}, + {0x124D, "pic1_12.png"}, + {0x124E, "pic1_13.png"}, + {0x124F, "pic1_14.png"}, + {0x1250, "pic1_15.png"}, + {0x1251, "pic1_16.png"}, + {0x1252, "pic1_17.png"}, + {0x1253, "pic1_18.png"}, + {0x1254, "pic1_19.png"}, + {0x1255, "pic1_20.png"}, + {0x1256, "pic1_21.png"}, + {0x1257, "pic1_22.png"}, + {0x1258, "pic1_23.png"}, + {0x1259, "pic1_24.png"}, + {0x125A, "pic1_25.png"}, + {0x125B, "pic1_26.png"}, + {0x125C, "pic1_27.png"}, + {0x125D, "pic1_28.png"}, + {0x125E, "pic1_29.png"}, + {0x125F, "pic1_30.png"}, + {0x1260, "changeinfo/changeinfo.xml"}, + {0x1261, "changeinfo/changeinfo_00.xml"}, + {0x1262, "changeinfo/changeinfo_01.xml"}, + {0x1263, "changeinfo/changeinfo_02.xml"}, + {0x1264, "changeinfo/changeinfo_03.xml"}, + {0x1265, "changeinfo/changeinfo_04.xml"}, + {0x1266, "changeinfo/changeinfo_05.xml"}, + {0x1267, "changeinfo/changeinfo_06.xml"}, + {0x1268, "changeinfo/changeinfo_07.xml"}, + {0x1269, "changeinfo/changeinfo_08.xml"}, + {0x126A, "changeinfo/changeinfo_09.xml"}, + {0x126B, "changeinfo/changeinfo_10.xml"}, + {0x126C, "changeinfo/changeinfo_11.xml"}, + {0x126D, "changeinfo/changeinfo_12.xml"}, + {0x126E, "changeinfo/changeinfo_13.xml"}, + {0x126F, "changeinfo/changeinfo_14.xml"}, + {0x1270, "changeinfo/changeinfo_15.xml"}, + {0x1271, "changeinfo/changeinfo_16.xml"}, + {0x1272, "changeinfo/changeinfo_17.xml"}, + {0x1273, "changeinfo/changeinfo_18.xml"}, + {0x1274, "changeinfo/changeinfo_19.xml"}, + {0x1275, "changeinfo/changeinfo_20.xml"}, + {0x1276, "changeinfo/changeinfo_21.xml"}, + {0x1277, "changeinfo/changeinfo_22.xml"}, + {0x1278, "changeinfo/changeinfo_23.xml"}, + {0x1279, "changeinfo/changeinfo_24.xml"}, + {0x127A, "changeinfo/changeinfo_25.xml"}, + {0x127B, "changeinfo/changeinfo_26.xml"}, + {0x127C, "changeinfo/changeinfo_27.xml"}, + {0x127D, "changeinfo/changeinfo_28.xml"}, + {0x127E, "changeinfo/changeinfo_29.xml"}, + {0x127F, "changeinfo/changeinfo_30.xml"}, + {0x1280, "icon0.dds"}, + {0x1281, "icon0_00.dds"}, + {0x1282, "icon0_01.dds"}, + {0x1283, "icon0_02.dds"}, + {0x1284, "icon0_03.dds"}, + {0x1285, "icon0_04.dds"}, + {0x1286, "icon0_05.dds"}, + {0x1287, "icon0_06.dds"}, + {0x1288, "icon0_07.dds"}, + {0x1289, "icon0_08.dds"}, + {0x128A, "icon0_09.dds"}, + {0x128B, "icon0_10.dds"}, + {0x128C, "icon0_11.dds"}, + {0x128D, "icon0_12.dds"}, + {0x128E, "icon0_13.dds"}, + {0x128F, "icon0_14.dds"}, + {0x1290, "icon0_15.dds"}, + {0x1291, "icon0_16.dds"}, + {0x1292, "icon0_17.dds"}, + {0x1293, "icon0_18.dds"}, + {0x1294, "icon0_19.dds"}, + {0x1295, "icon0_20.dds"}, + {0x1296, "icon0_21.dds"}, + {0x1297, "icon0_22.dds"}, + {0x1298, "icon0_23.dds"}, + {0x1299, "icon0_24.dds"}, + {0x129A, "icon0_25.dds"}, + {0x129B, "icon0_26.dds"}, + {0x129C, "icon0_27.dds"}, + {0x129D, "icon0_28.dds"}, + {0x129E, "icon0_29.dds"}, + {0x129F, "icon0_30.dds"}, + {0x12A0, "pic0.dds"}, + {0x12C0, "pic1.dds"}, + {0x12C1, "pic1_00.dds"}, + {0x12C2, "pic1_01.dds"}, + {0x12C3, "pic1_02.dds"}, + {0x12C4, "pic1_03.dds"}, + {0x12C5, "pic1_04.dds"}, + {0x12C6, "pic1_05.dds"}, + {0x12C7, "pic1_06.dds"}, + {0x12C8, "pic1_07.dds"}, + {0x12C9, "pic1_08.dds"}, + {0x12CA, "pic1_09.dds"}, + {0x12CB, "pic1_10.dds"}, + {0x12CC, "pic1_11.dds"}, + {0x12CD, "pic1_12.dds"}, + {0x12CE, "pic1_13.dds"}, + {0x12CF, "pic1_14.dds"}, + {0x12D0, "pic1_15.dds"}, + {0x12D1, "pic1_16.dds"}, + {0x12D2, "pic1_17.dds"}, + {0x12D3, "pic1_18.dds"}, + {0x12D4, "pic1_19.dds"}, + {0x12D5, "pic1_20.dds"}, + {0x12D6, "pic1_21.dds"}, + {0x12D7, "pic1_22.dds"}, + {0x12D8, "pic1_23.dds"}, + {0x12D9, "pic1_24.dds"}, + {0x12DA, "pic1_25.dds"}, + {0x12DB, "pic1_26.dds"}, + {0x12DC, "pic1_27.dds"}, + {0x12DD, "pic1_28.dds"}, + {0x12DE, "pic1_29.dds"}, + {0x12DF, "pic1_30.dds"}, + {0x1400, "trophy/trophy00.trp"}, + {0x1401, "trophy/trophy01.trp"}, + {0x1402, "trophy/trophy02.trp"}, + {0x1403, "trophy/trophy03.trp"}, + {0x1404, "trophy/trophy04.trp"}, + {0x1405, "trophy/trophy05.trp"}, + {0x1406, "trophy/trophy06.trp"}, + {0x1407, "trophy/trophy07.trp"}, + {0x1408, "trophy/trophy08.trp"}, + {0x1409, "trophy/trophy09.trp"}, + {0x140A, "trophy/trophy10.trp"}, + {0x140B, "trophy/trophy11.trp"}, + {0x140C, "trophy/trophy12.trp"}, + {0x140D, "trophy/trophy13.trp"}, + {0x140E, "trophy/trophy14.trp"}, + {0x140F, "trophy/trophy15.trp"}, + {0x1410, "trophy/trophy16.trp"}, + {0x1411, "trophy/trophy17.trp"}, + {0x1412, "trophy/trophy18.trp"}, + {0x1413, "trophy/trophy19.trp"}, + {0x1414, "trophy/trophy20.trp"}, + {0x1415, "trophy/trophy21.trp"}, + {0x1416, "trophy/trophy22.trp"}, + {0x1417, "trophy/trophy23.trp"}, + {0x1418, "trophy/trophy24.trp"}, + {0x1419, "trophy/trophy25.trp"}, + {0x141A, "trophy/trophy26.trp"}, + {0x141B, "trophy/trophy27.trp"}, + {0x141C, "trophy/trophy28.trp"}, + {0x141D, "trophy/trophy29.trp"}, + {0x141E, "trophy/trophy30.trp"}, + {0x141F, "trophy/trophy31.trp"}, + {0x1420, "trophy/trophy32.trp"}, + {0x1421, "trophy/trophy33.trp"}, + {0x1422, "trophy/trophy34.trp"}, + {0x1423, "trophy/trophy35.trp"}, + {0x1424, "trophy/trophy36.trp"}, + {0x1425, "trophy/trophy37.trp"}, + {0x1426, "trophy/trophy38.trp"}, + {0x1427, "trophy/trophy39.trp"}, + {0x1428, "trophy/trophy40.trp"}, + {0x1429, "trophy/trophy41.trp"}, + {0x142A, "trophy/trophy42.trp"}, + {0x142B, "trophy/trophy43.trp"}, + {0x142C, "trophy/trophy44.trp"}, + {0x142D, "trophy/trophy45.trp"}, + {0x142E, "trophy/trophy46.trp"}, + {0x142F, "trophy/trophy47.trp"}, + {0x1430, "trophy/trophy48.trp"}, + {0x1431, "trophy/trophy49.trp"}, + {0x1432, "trophy/trophy50.trp"}, + {0x1433, "trophy/trophy51.trp"}, + {0x1434, "trophy/trophy52.trp"}, + {0x1435, "trophy/trophy53.trp"}, + {0x1436, "trophy/trophy54.trp"}, + {0x1437, "trophy/trophy55.trp"}, + {0x1438, "trophy/trophy56.trp"}, + {0x1439, "trophy/trophy57.trp"}, + {0x143A, "trophy/trophy58.trp"}, + {0x143B, "trophy/trophy59.trp"}, + {0x143C, "trophy/trophy60.trp"}, + {0x143D, "trophy/trophy61.trp"}, + {0x143E, "trophy/trophy62.trp"}, + {0x143F, "trophy/trophy63.trp"}, + {0x1440, "trophy/trophy64.trp"}, + {0x1441, "trophy/trophy65.trp"}, + {0x1442, "trophy/trophy66.trp"}, + {0x1443, "trophy/trophy67.trp"}, + {0x1444, "trophy/trophy68.trp"}, + {0x1445, "trophy/trophy69.trp"}, + {0x1446, "trophy/trophy70.trp"}, + {0x1447, "trophy/trophy71.trp"}, + {0x1448, "trophy/trophy72.trp"}, + {0x1449, "trophy/trophy73.trp"}, + {0x144A, "trophy/trophy74.trp"}, + {0x144B, "trophy/trophy75.trp"}, + {0x144C, "trophy/trophy76.trp"}, + {0x144D, "trophy/trophy77.trp"}, + {0x144E, "trophy/trophy78.trp"}, + {0x144F, "trophy/trophy79.trp"}, + {0x1450, "trophy/trophy80.trp"}, + {0x1451, "trophy/trophy81.trp"}, + {0x1452, "trophy/trophy82.trp"}, + {0x1453, "trophy/trophy83.trp"}, + {0x1454, "trophy/trophy84.trp"}, + {0x1455, "trophy/trophy85.trp"}, + {0x1456, "trophy/trophy86.trp"}, + {0x1457, "trophy/trophy87.trp"}, + {0x1458, "trophy/trophy88.trp"}, + {0x1459, "trophy/trophy89.trp"}, + {0x145A, "trophy/trophy90.trp"}, + {0x145B, "trophy/trophy91.trp"}, + {0x145C, "trophy/trophy92.trp"}, + {0x145D, "trophy/trophy93.trp"}, + {0x145E, "trophy/trophy94.trp"}, + {0x145F, "trophy/trophy95.trp"}, + {0x1460, "trophy/trophy96.trp"}, + {0x1461, "trophy/trophy97.trp"}, + {0x1462, "trophy/trophy98.trp"}, + {0x1463, "trophy/trophy99.trp"}, + {0x1600, "keymap_rp/001.png"}, + {0x1601, "keymap_rp/002.png"}, + {0x1602, "keymap_rp/003.png"}, + {0x1603, "keymap_rp/004.png"}, + {0x1604, "keymap_rp/005.png"}, + {0x1605, "keymap_rp/006.png"}, + {0x1606, "keymap_rp/007.png"}, + {0x1607, "keymap_rp/008.png"}, + {0x1608, "keymap_rp/009.png"}, + {0x1609, "keymap_rp/010.png"}, + {0x1610, "keymap_rp/00/001.png"}, + {0x1611, "keymap_rp/00/002.png"}, + {0x1612, "keymap_rp/00/003.png"}, + {0x1613, "keymap_rp/00/004.png"}, + {0x1614, "keymap_rp/00/005.png"}, + {0x1615, "keymap_rp/00/006.png"}, + {0x1616, "keymap_rp/00/007.png"}, + {0x1617, "keymap_rp/00/008.png"}, + {0x1618, "keymap_rp/00/009.png"}, + {0x1619, "keymap_rp/00/010.png"}, + {0x1620, "keymap_rp/01/001.png"}, + {0x1621, "keymap_rp/01/002.png"}, + {0x1622, "keymap_rp/01/003.png"}, + {0x1623, "keymap_rp/01/004.png"}, + {0x1624, "keymap_rp/01/005.png"}, + {0x1625, "keymap_rp/01/006.png"}, + {0x1626, "keymap_rp/01/007.png"}, + {0x1627, "keymap_rp/01/008.png"}, + {0x1628, "keymap_rp/01/009.png"}, + {0x1629, "keymap_rp/01/010.png"}, + {0x1630, "keymap_rp/02/001.png"}, + {0x1631, "keymap_rp/02/002.png"}, + {0x1632, "keymap_rp/02/003.png"}, + {0x1633, "keymap_rp/02/004.png"}, + {0x1634, "keymap_rp/02/005.png"}, + {0x1635, "keymap_rp/02/006.png"}, + {0x1636, "keymap_rp/02/007.png"}, + {0x1637, "keymap_rp/02/008.png"}, + {0x1638, "keymap_rp/02/009.png"}, + {0x1639, "keymap_rp/02/010.png"}, + {0x1640, "keymap_rp/03/001.png"}, + {0x1641, "keymap_rp/03/002.png"}, + {0x1642, "keymap_rp/03/003.png"}, + {0x1643, "keymap_rp/03/004.png"}, + {0x1644, "keymap_rp/03/005.png"}, + {0x1645, "keymap_rp/03/006.png"}, + {0x1646, "keymap_rp/03/007.png"}, + {0x1647, "keymap_rp/03/008.png"}, + {0x1648, "keymap_rp/03/0010.png"}, + {0x1650, "keymap_rp/04/001.png"}, + {0x1651, "keymap_rp/04/002.png"}, + {0x1652, "keymap_rp/04/003.png"}, + {0x1653, "keymap_rp/04/004.png"}, + {0x1654, "keymap_rp/04/005.png"}, + {0x1655, "keymap_rp/04/006.png"}, + {0x1656, "keymap_rp/04/007.png"}, + {0x1657, "keymap_rp/04/008.png"}, + {0x1658, "keymap_rp/04/009.png"}, + {0x1659, "keymap_rp/04/010.png"}, + {0x1660, "keymap_rp/05/001.png"}, + {0x1661, "keymap_rp/05/002.png"}, + {0x1662, "keymap_rp/05/003.png"}, + {0x1663, "keymap_rp/05/004.png"}, + {0x1664, "keymap_rp/05/005.png"}, + {0x1665, "keymap_rp/05/006.png"}, + {0x1666, "keymap_rp/05/007.png"}, + {0x1667, "keymap_rp/05/008.png"}, + {0x1668, "keymap_rp/05/009.png"}, + {0x1669, "keymap_rp/05/010.png"}, + {0x1670, "keymap_rp/06/001.png"}, + {0x1671, "keymap_rp/06/002.png"}, + {0x1672, "keymap_rp/06/003.png"}, + {0x1673, "keymap_rp/06/004.png"}, + {0x1674, "keymap_rp/06/005.png"}, + {0x1675, "keymap_rp/06/006.png"}, + {0x1676, "keymap_rp/06/007.png"}, + {0x1677, "keymap_rp/06/008.png"}, + {0x1678, "keymap_rp/06/009.png"}, + {0x1679, "keymap_rp/06/010.png"}, + {0x1680, "keymap_rp/07/001.png"}, + {0x1681, "keymap_rp/07/002.png"}, + {0x1682, "keymap_rp/07/003.png"}, + {0x1683, "keymap_rp/07/004.png"}, + {0x1684, "keymap_rp/07/005.png"}, + {0x1685, "keymap_rp/07/006.png"}, + {0x1686, "keymap_rp/07/007.png"}, + {0x1687, "keymap_rp/07/008.png"}, + {0x1688, "keymap_rp/07/009.png"}, + {0x1689, "keymap_rp/07/010.png"}, + {0x1690, "keymap_rp/08/001.png"}, + {0x1691, "keymap_rp/08/002.png"}, + {0x1692, "keymap_rp/08/003.png"}, + {0x1693, "keymap_rp/08/004.png"}, + {0x1694, "keymap_rp/08/005.png"}, + {0x1695, "keymap_rp/08/006.png"}, + {0x1696, "keymap_rp/08/007.png"}, + {0x1697, "keymap_rp/08/008.png"}, + {0x1698, "keymap_rp/08/009.png"}, + {0x1699, "keymap_rp/08/010.png"}, + {0x16A0, "keymap_rp/09/001.png"}, + {0x16A1, "keymap_rp/09/002.png"}, + {0x16A2, "keymap_rp/09/003.png"}, + {0x16A3, "keymap_rp/09/004.png"}, + {0x16A4, "keymap_rp/09/005.png"}, + {0x16A5, "keymap_rp/09/006.png"}, + {0x16A6, "keymap_rp/09/007.png"}, + {0x16A7, "keymap_rp/09/008.png"}, + {0x16A8, "keymap_rp/09/009.png"}, + {0x16A9, "keymap_rp/09/010.png"}, + {0x16B0, "keymap_rp/10/001.png"}, + {0x16B1, "keymap_rp/10/002.png"}, + {0x16B2, "keymap_rp/10/003.png"}, + {0x16B3, "keymap_rp/10/004.png"}, + {0x16B4, "keymap_rp/10/005.png"}, + {0x16B5, "keymap_rp/10/006.png"}, + {0x16B6, "keymap_rp/10/007.png"}, + {0x16B7, "keymap_rp/10/008.png"}, + {0x16B8, "keymap_rp/10/009.png"}, + {0x16B9, "keymap_rp/10/010.png"}, + {0x16C0, "keymap_rp/11/001.png"}, + {0x16C1, "keymap_rp/11/002.png"}, + {0x16C2, "keymap_rp/11/003.png"}, + {0x16C3, "keymap_rp/11/004.png"}, + {0x16C4, "keymap_rp/11/005.png"}, + {0x16C5, "keymap_rp/11/006.png"}, + {0x16C6, "keymap_rp/11/007.png"}, + {0x16C7, "keymap_rp/11/008.png"}, + {0x16C8, "keymap_rp/11/009.png"}, + {0x16C9, "keymap_rp/11/010.png"}, + {0x16D0, "keymap_rp/12/001.png"}, + {0x16D1, "keymap_rp/12/002.png"}, + {0x16D2, "keymap_rp/12/003.png"}, + {0x16D3, "keymap_rp/12/004.png"}, + {0x16D4, "keymap_rp/12/005.png"}, + {0x16D5, "keymap_rp/12/006.png"}, + {0x16D6, "keymap_rp/12/007.png"}, + {0x16D7, "keymap_rp/12/008.png"}, + {0x16D8, "keymap_rp/12/009.png"}, + {0x16D9, "keymap_rp/12/010.png"}, + {0x16E0, "keymap_rp/13/001.png"}, + {0x16E1, "keymap_rp/13/002.png"}, + {0x16E2, "keymap_rp/13/003.png"}, + {0x16E3, "keymap_rp/13/004.png"}, + {0x16E4, "keymap_rp/13/005.png"}, + {0x16E5, "keymap_rp/13/006.png"}, + {0x16E6, "keymap_rp/13/007.png"}, + {0x16E7, "keymap_rp/13/008.png"}, + {0x16E8, "keymap_rp/13/009.png"}, + {0x16E9, "keymap_rp/13/010.png"}, + {0x16F0, "keymap_rp/14/001.png"}, + {0x16F1, "keymap_rp/14/002.png"}, + {0x16F2, "keymap_rp/14/003.png"}, + {0x16F3, "keymap_rp/14/004.png"}, + {0x16F4, "keymap_rp/14/005.png"}, + {0x16F5, "keymap_rp/14/006.png"}, + {0x16F6, "keymap_rp/14/007.png"}, + {0x16F7, "keymap_rp/14/008.png"}, + {0x16F8, "keymap_rp/14/009.png"}, + {0x16F9, "keymap_rp/14/010.png"}, + {0x1700, "keymap_rp/15/001.png"}, + {0x1701, "keymap_rp/15/002.png"}, + {0x1702, "keymap_rp/15/003.png"}, + {0x1703, "keymap_rp/15/004.png"}, + {0x1704, "keymap_rp/15/005.png"}, + {0x1705, "keymap_rp/15/006.png"}, + {0x1706, "keymap_rp/15/007.png"}, + {0x1707, "keymap_rp/15/008.png"}, + {0x1708, "keymap_rp/15/009.png"}, + {0x1709, "keymap_rp/15/010.png"}, + {0x1710, "keymap_rp/16/001.png"}, + {0x1711, "keymap_rp/16/002.png"}, + {0x1712, "keymap_rp/16/003.png"}, + {0x1713, "keymap_rp/16/004.png"}, + {0x1714, "keymap_rp/16/005.png"}, + {0x1715, "keymap_rp/16/006.png"}, + {0x1716, "keymap_rp/16/007.png"}, + {0x1717, "keymap_rp/16/008.png"}, + {0x1718, "keymap_rp/16/009.png"}, + {0x1719, "keymap_rp/16/010.png"}, + {0x1720, "keymap_rp/17/001.png"}, + {0x1721, "keymap_rp/17/002.png"}, + {0x1722, "keymap_rp/17/003.png"}, + {0x1723, "keymap_rp/17/004.png"}, + {0x1724, "keymap_rp/17/005.png"}, + {0x1725, "keymap_rp/17/006.png"}, + {0x1726, "keymap_rp/17/007.png"}, + {0x1727, "keymap_rp/17/008.png"}, + {0x1728, "keymap_rp/17/009.png"}, + {0x1729, "keymap_rp/17/010.png"}, + {0x1730, "keymap_rp/18/001.png"}, + {0x1731, "keymap_rp/18/002.png"}, + {0x1732, "keymap_rp/18/003.png"}, + {0x1733, "keymap_rp/18/004.png"}, + {0x1734, "keymap_rp/18/005.png"}, + {0x1735, "keymap_rp/18/006.png"}, + {0x1736, "keymap_rp/18/007.png"}, + {0x1737, "keymap_rp/18/008.png"}, + {0x1738, "keymap_rp/18/009.png"}, + {0x1739, "keymap_rp/18/010.png"}, + {0x1740, "keymap_rp/19/001.png"}, + {0x1741, "keymap_rp/19/002.png"}, + {0x1742, "keymap_rp/19/003.png"}, + {0x1743, "keymap_rp/19/004.png"}, + {0x1744, "keymap_rp/19/005.png"}, + {0x1745, "keymap_rp/19/006.png"}, + {0x1746, "keymap_rp/19/007.png"}, + {0x1747, "keymap_rp/19/008.png"}, + {0x1748, "keymap_rp/19/009.png"}, + {0x1749, "keymap_rp/19/010.png"}, + {0x1750, "keymap_rp/20/001.png"}, + {0x1751, "keymap_rp/20/002.png"}, + {0x1752, "keymap_rp/20/003.png"}, + {0x1753, "keymap_rp/20/004.png"}, + {0x1754, "keymap_rp/20/005.png"}, + {0x1755, "keymap_rp/20/006.png"}, + {0x1756, "keymap_rp/20/007.png"}, + {0x1757, "keymap_rp/20/008.png"}, + {0x1758, "keymap_rp/20/009.png"}, + {0x1759, "keymap_rp/20/010.png"}, + {0x1760, "keymap_rp/21/001.png"}, + {0x1761, "keymap_rp/21/002.png"}, + {0x1762, "keymap_rp/21/003.png"}, + {0x1763, "keymap_rp/21/004.png"}, + {0x1764, "keymap_rp/21/005.png"}, + {0x1765, "keymap_rp/21/006.png"}, + {0x1766, "keymap_rp/21/007.png"}, + {0x1767, "keymap_rp/21/008.png"}, + {0x1768, "keymap_rp/21/009.png"}, + {0x1769, "keymap_rp/21/010.png"}, + {0x1770, "keymap_rp/22/001.png"}, + {0x1771, "keymap_rp/22/002.png"}, + {0x1772, "keymap_rp/22/003.png"}, + {0x1773, "keymap_rp/22/004.png"}, + {0x1774, "keymap_rp/22/005.png"}, + {0x1775, "keymap_rp/22/006.png"}, + {0x1776, "keymap_rp/22/007.png"}, + {0x1777, "keymap_rp/22/008.png"}, + {0x1778, "keymap_rp/22/009.png"}, + {0x1779, "keymap_rp/22/010.png"}, + {0x1780, "keymap_rp/23/001.png"}, + {0x1781, "keymap_rp/23/002.png"}, + {0x1782, "keymap_rp/23/003.png"}, + {0x1783, "keymap_rp/23/004.png"}, + {0x1784, "keymap_rp/23/005.png"}, + {0x1785, "keymap_rp/23/006.png"}, + {0x1786, "keymap_rp/23/007.png"}, + {0x1787, "keymap_rp/23/008.png"}, + {0x1788, "keymap_rp/23/009.png"}, + {0x1789, "keymap_rp/23/010.png"}, + {0x1790, "keymap_rp/24/001.png"}, + {0x1791, "keymap_rp/24/002.png"}, + {0x1792, "keymap_rp/24/003.png"}, + {0x1793, "keymap_rp/24/004.png"}, + {0x1794, "keymap_rp/24/005.png"}, + {0x1795, "keymap_rp/24/006.png"}, + {0x1796, "keymap_rp/24/007.png"}, + {0x1797, "keymap_rp/24/008.png"}, + {0x1798, "keymap_rp/24/009.png"}, + {0x1799, "keymap_rp/24/010.png"}, + {0x17A0, "keymap_rp/25/001.png"}, + {0x17A1, "keymap_rp/25/002.png"}, + {0x17A2, "keymap_rp/25/003.png"}, + {0x17A3, "keymap_rp/25/004.png"}, + {0x17A4, "keymap_rp/25/005.png"}, + {0x17A5, "keymap_rp/25/006.png"}, + {0x17A6, "keymap_rp/25/007.png"}, + {0x17A7, "keymap_rp/25/008.png"}, + {0x17A8, "keymap_rp/25/009.png"}, + {0x17A9, "keymap_rp/25/010.png"}, + {0x17B0, "keymap_rp/26/001.png"}, + {0x17B1, "keymap_rp/26/002.png"}, + {0x17B2, "keymap_rp/26/003.png"}, + {0x17B3, "keymap_rp/26/004.png"}, + {0x17B4, "keymap_rp/26/005.png"}, + {0x17B5, "keymap_rp/26/006.png"}, + {0x17B6, "keymap_rp/26/007.png"}, + {0x17B7, "keymap_rp/26/008.png"}, + {0x17B8, "keymap_rp/26/009.png"}, + {0x17B9, "keymap_rp/26/010.png"}, + {0x17C0, "keymap_rp/27/001.png"}, + {0x17C1, "keymap_rp/27/002.png"}, + {0x17C2, "keymap_rp/27/003.png"}, + {0x17C3, "keymap_rp/27/004.png"}, + {0x17C4, "keymap_rp/27/005.png"}, + {0x17C5, "keymap_rp/27/006.png"}, + {0x17C6, "keymap_rp/27/007.png"}, + {0x17C7, "keymap_rp/27/008.png"}, + {0x17C8, "keymap_rp/27/009.png"}, + {0x17C9, "keymap_rp/27/010.png"}, + {0x17D0, "keymap_rp/28/001.png"}, + {0x17D1, "keymap_rp/28/002.png"}, + {0x17D2, "keymap_rp/28/003.png"}, + {0x17D3, "keymap_rp/28/004.png"}, + {0x17D4, "keymap_rp/28/005.png"}, + {0x17D5, "keymap_rp/28/006.png"}, + {0x17D6, "keymap_rp/28/007.png"}, + {0x17D7, "keymap_rp/28/008.png"}, + {0x17D8, "keymap_rp/28/009.png"}, + {0x17D9, "keymap_rp/28/010.png"}, + {0x17E0, "keymap_rp/29/001.png"}, + {0x17E1, "keymap_rp/29/002.png"}, + {0x17E2, "keymap_rp/29/003.png"}, + {0x17E3, "keymap_rp/29/004.png"}, + {0x17E4, "keymap_rp/29/005.png"}, + {0x17E5, "keymap_rp/29/006.png"}, + {0x17E6, "keymap_rp/29/007.png"}, + {0x17E7, "keymap_rp/29/008.png"}, + {0x17E8, "keymap_rp/29/009.png"}, + {0x17E9, "keymap_rp/29/010.png"}, + {0x17F0, "keymap_rp/30/001.png"}, + {0x17F1, "keymap_rp/30/002.png"}, + {0x17F2, "keymap_rp/30/003.png"}, + {0x17F3, "keymap_rp/30/004.png"}, + {0x17F4, "keymap_rp/30/005.png"}, + {0x17F5, "keymap_rp/30/006.png"}, + {0x17F6, "keymap_rp/30/007.png"}, + {0x17F7, "keymap_rp/30/008.png"}, + {0x17F8, "keymap_rp/30/009.png"}, + {0x17F9, "keymap_rp/30/010.png"}, +}}; + +std::string_view GetEntryNameByType(u32 type) { + const auto key = PkgEntryValue{type}; + const auto it = std::ranges::lower_bound(PkgEntries, key); + if (it != PkgEntries.end() && it->type == type) { + return it->name; + } + return ""; +} diff --git a/src/core/file_format/pkg_type.h b/src/core/file_format/pkg_type.h new file mode 100644 index 000000000..6b010e3a3 --- /dev/null +++ b/src/core/file_format/pkg_type.h @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" + +/// Retrieves the PKG entry name from its type identifier. +std::string_view GetEntryNameByType(u32 type); diff --git a/src/core/loader.cpp b/src/core/loader.cpp new file mode 100644 index 000000000..f80bfbb81 --- /dev/null +++ b/src/core/loader.cpp @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/io_file.h" +#include "common/types.h" +#include "loader.h" + +namespace Loader { + +FileTypes DetectFileType(const std::filesystem::path& filepath) { + // No file loaded + if (filepath.empty()) { + return FileTypes::Unknown; + } + Common::FS::IOFile file; + file.Open(filepath, Common::FS::FileAccessMode::Read); + file.Seek(0); + u32 magic; + file.Read(magic); + file.Close(); + switch (magic) { + case PkgMagic: + return FileTypes::Pkg; + } + return FileTypes::Unknown; +} + +} // namespace Loader diff --git a/src/core/loader.h b/src/core/loader.h new file mode 100644 index 000000000..608970dca --- /dev/null +++ b/src/core/loader.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace Loader { + +constexpr static u32 PkgMagic = 0x544e437f; + +enum class FileTypes { + Unknown, + Pkg, +}; + +FileTypes DetectFileType(const std::filesystem::path& filepath); +} // namespace Loader diff --git a/src/qt_gui/install_dir_select.cpp b/src/qt_gui/install_dir_select.cpp new file mode 100644 index 000000000..e90a10ee6 --- /dev/null +++ b/src/qt_gui/install_dir_select.cpp @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "install_dir_select.h" + +InstallDirSelect::InstallDirSelect() : selected_dir() { + auto install_dirs = Config::getGameInstallDirs(); + selected_dir = install_dirs.empty() ? "" : install_dirs.front(); + + if (!install_dirs.empty() && install_dirs.size() == 1) { + accept(); + } + + auto layout = new QVBoxLayout(this); + + layout->addWidget(SetupInstallDirList()); + layout->addStretch(); + layout->addWidget(SetupDialogActions()); + + setWindowTitle(tr("shadPS4 - Choose directory")); + setWindowIcon(QIcon(":images/shadps4.ico")); +} + +InstallDirSelect::~InstallDirSelect() {} + +QWidget* InstallDirSelect::SetupInstallDirList() { + auto group = new QGroupBox(tr("Select which directory you want to install to.")); + auto vlayout = new QVBoxLayout(); + + auto m_path_list = new QListWidget(); + QList qt_list; + for (const auto& str : Config::getGameInstallDirs()) { + QString installDirPath; + Common::FS::PathToQString(installDirPath, str); + qt_list.append(installDirPath); + } + m_path_list->insertItems(0, qt_list); + m_path_list->setSpacing(1); + + connect(m_path_list, &QListWidget::itemClicked, this, &InstallDirSelect::setSelectedDirectory); + connect(m_path_list, &QListWidget::itemActivated, this, + &InstallDirSelect::setSelectedDirectory); + + vlayout->addWidget(m_path_list); + + auto checkbox = new QCheckBox(tr("Install All Queued to Selected Folder")); + connect(checkbox, &QCheckBox::toggled, this, &InstallDirSelect::setUseForAllQueued); + vlayout->addWidget(checkbox); + + auto checkbox2 = new QCheckBox(tr("Delete PKG File on Install")); + connect(checkbox2, &QCheckBox::toggled, this, &InstallDirSelect::setDeleteFileOnInstall); + vlayout->addWidget(checkbox2); + + group->setLayout(vlayout); + return group; +} + +void InstallDirSelect::setSelectedDirectory(QListWidgetItem* item) { + if (item) { + const auto highlighted_path = Common::FS::PathFromQString(item->text()); + if (!highlighted_path.empty()) { + selected_dir = highlighted_path; + } + } +} + +void InstallDirSelect::setUseForAllQueued(bool enabled) { + use_for_all_queued = enabled; +} + +void InstallDirSelect::setDeleteFileOnInstall(bool enabled) { + delete_file_on_install = enabled; +} + +QWidget* InstallDirSelect::SetupDialogActions() { + auto actions = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + connect(actions, &QDialogButtonBox::accepted, this, &InstallDirSelect::accept); + connect(actions, &QDialogButtonBox::rejected, this, &InstallDirSelect::reject); + + return actions; +} diff --git a/src/qt_gui/install_dir_select.h b/src/qt_gui/install_dir_select.h new file mode 100644 index 000000000..e11cbf381 --- /dev/null +++ b/src/qt_gui/install_dir_select.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/config.h" +#include "common/path_util.h" + +class QLineEdit; + +class InstallDirSelect final : public QDialog { + Q_OBJECT + +public: + InstallDirSelect(); + ~InstallDirSelect(); + + std::filesystem::path getSelectedDirectory() { + return selected_dir; + } + + bool useForAllQueued() { + return use_for_all_queued; + } + + bool deleteFileOnInstall() { + return delete_file_on_install; + } + +private: + QWidget* SetupInstallDirList(); + QWidget* SetupDialogActions(); + void setSelectedDirectory(QListWidgetItem* item); + void setDeleteFileOnInstall(bool enabled); + void setUseForAllQueued(bool enabled); + std::filesystem::path selected_dir; + bool delete_file_on_install = false; + bool use_for_all_queued = false; +}; diff --git a/src/qt_gui/pkg_viewer.cpp b/src/qt_gui/pkg_viewer.cpp new file mode 100644 index 000000000..ecbc6312d --- /dev/null +++ b/src/qt_gui/pkg_viewer.cpp @@ -0,0 +1,217 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "pkg_viewer.h" + +PKGViewer::PKGViewer(std::shared_ptr game_info_get, QWidget* parent, + std::function InstallDragDropPkg) + : QMainWindow(), m_game_info(game_info_get) { + this->resize(1280, 720); + this->setAttribute(Qt::WA_DeleteOnClose); + dir_list_std = Config::getPkgViewer(); + dir_list.clear(); + for (const auto& str : dir_list_std) { + dir_list.append(QString::fromStdString(str)); + } + statusBar = new QStatusBar(treeWidget); + this->setStatusBar(statusBar); + treeWidget = new QTreeWidget(this); + treeWidget->setColumnCount(9); + QStringList headers; + headers << tr("Name") << tr("Serial") << tr("Installed") << tr("Size") << tr("Category") + << tr("Type") << tr("App Ver") << tr("FW") << tr("Region") << tr("Flags") << tr("Path"); + treeWidget->setHeaderLabels(headers); + treeWidget->header()->setDefaultAlignment(Qt::AlignCenter); + treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + treeWidget->setColumnWidth(8, 170); + this->setCentralWidget(treeWidget); + QMenuBar* menuBar = new QMenuBar(this); + menuBar->setContextMenuPolicy(Qt::PreventContextMenu); + QMenu* fileMenu = menuBar->addMenu(tr("File")); + QAction* openFolderAct = new QAction(tr("Open Folder"), this); + fileMenu->addAction(openFolderAct); + this->setMenuBar(menuBar); + CheckPKGFolders(); // Check for new PKG files in existing folders. + ProcessPKGInfo(); + + connect(openFolderAct, &QAction::triggered, this, &PKGViewer::OpenPKGFolder); + + connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, + [=, this](const QPoint& pos) { + if (treeWidget->selectedItems().isEmpty()) { + return; + } + m_gui_context_menus.RequestGameMenuPKGViewer(pos, m_full_pkg_list, treeWidget, + InstallDragDropPkg); + }); + + connect(parent, &QWidget::destroyed, this, [this]() { this->deleteLater(); }); +} + +PKGViewer::~PKGViewer() {} + +void PKGViewer::OpenPKGFolder() { + QString folderPath = + QFileDialog::getExistingDirectory(this, tr("Open Folder"), QDir::homePath()); + if (!dir_list.contains(folderPath)) { + dir_list.append(folderPath); + QDir directory(folderPath); + QFileInfoList fileInfoList = directory.entryInfoList(QDir::Files); + for (const QFileInfo& fileInfo : fileInfoList) { + QString file_ext = fileInfo.suffix(); + if (fileInfo.isFile() && file_ext == "pkg") { + m_pkg_list.append(fileInfo.absoluteFilePath()); + } + } + std::sort(m_pkg_list.begin(), m_pkg_list.end()); + ProcessPKGInfo(); + dir_list_std.clear(); + for (auto dir : dir_list) { + dir_list_std.push_back(dir.toStdString()); + } + Config::setPkgViewer(dir_list_std); + } else { + // qDebug() << "Folder selection canceled."; + } +} + +void PKGViewer::CheckPKGFolders() { // Check for new PKG file additions. + m_pkg_list.clear(); + for (const QString& dir : dir_list) { + QDir directory(dir); + QFileInfoList fileInfoList = directory.entryInfoList(QDir::Files); + for (const QFileInfo& fileInfo : fileInfoList) { + QString file_ext = fileInfo.suffix(); + if (fileInfo.isFile() && file_ext == "pkg") { + m_pkg_list.append(fileInfo.absoluteFilePath()); + } + } + } + std::sort(m_pkg_list.begin(), m_pkg_list.end()); +} + +void PKGViewer::ProcessPKGInfo() { + treeWidget->clear(); + map_strings.clear(); + map_integers.clear(); + m_pkg_app_list.clear(); + m_pkg_patch_list.clear(); + m_full_pkg_list.clear(); + for (int i = 0; i < m_pkg_list.size(); i++) { + std::filesystem::path path = Common::FS::PathFromQString(m_pkg_list[i]); + std::string failreason; + if (!package.Open(path, failreason)) { + QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason)); + return; + } + psf.Open(package.sfo); + QString title_name = QString::fromStdString( + std::string{psf.GetString("TITLE").value_or(std::string{tr("Unknown").toStdString()})}); + QString title_id = QString::fromStdString(std::string{ + psf.GetString("TITLE_ID").value_or(std::string{tr("Unknown").toStdString()})}); + QString app_type = GameListUtils::GetAppType(psf.GetInteger("APP_TYPE").value_or(0)); + QString app_version = QString::fromStdString(std::string{ + psf.GetString("APP_VER").value_or(std::string{tr("Unknown").toStdString()})}); + QString title_category = QString::fromStdString(std::string{ + psf.GetString("CATEGORY").value_or(std::string{tr("Unknown").toStdString()})}); + QString pkg_size = GameListUtils::FormatSize(package.GetPkgHeader().pkg_size); + pkg_content_flag = package.GetPkgHeader().pkg_content_flags; + QString flagss = ""; + for (const auto& flag : package.flagNames) { + if (package.isFlagSet(pkg_content_flag, flag.first)) { + if (!flagss.isEmpty()) + flagss += (", "); + flagss += QString::fromStdString(flag.second.data()); + } + } + + QString fw_ = tr("Unknown"); + if (const auto fw_int_opt = psf.GetInteger("SYSTEM_VER"); fw_int_opt.has_value()) { + const u32 fw_int = *fw_int_opt; + if (fw_int == 0) { + fw_ = "0.00"; + } else { + QString fw = QString::number(fw_int, 16); + fw_ = fw.length() > 7 ? QString::number(fw_int, 16).left(3).insert(2, '.') + : fw.left(3).insert(1, '.'); + } + } + char region = package.GetPkgHeader().pkg_content_id[0]; + QString pkg_info = ""; + if (title_category == "gd" && !flagss.contains("PATCH")) { + title_category = "App"; + pkg_info = title_name + ";;" + title_id + ";;" + pkg_size + ";;" + title_category + + ";;" + app_type + ";;" + app_version + ";;" + fw_ + ";;" + + game_list_util.GetRegion(region) + ";;" + flagss + ";;" + m_pkg_list[i]; + m_pkg_app_list.append(pkg_info); + } else { + title_category = "Patch"; + pkg_info = title_name + ";;" + title_id + ";;" + pkg_size + ";;" + title_category + + ";;" + app_type + ";;" + app_version + ";;" + fw_ + ";;" + + game_list_util.GetRegion(region) + ";;" + flagss + ";;" + m_pkg_list[i]; + m_pkg_patch_list.append(pkg_info); + } + } + std::sort(m_pkg_app_list.begin(), m_pkg_app_list.end()); + for (int i = 0; i < m_pkg_app_list.size(); i++) { + QTreeWidgetItem* treeItem = new QTreeWidgetItem(treeWidget); + QStringList pkg_app_ = m_pkg_app_list[i].split(";;"); + m_full_pkg_list.append(m_pkg_app_list[i]); + treeItem->setExpanded(true); + treeItem->setText(0, pkg_app_[0]); + treeItem->setText(1, pkg_app_[1]); + treeItem->setText(3, pkg_app_[2]); + treeItem->setTextAlignment(3, Qt::AlignCenter); + treeItem->setText(4, pkg_app_[3]); + treeItem->setTextAlignment(4, Qt::AlignCenter); + treeItem->setText(5, pkg_app_[4]); + treeItem->setTextAlignment(5, Qt::AlignCenter); + treeItem->setText(6, pkg_app_[5]); + treeItem->setTextAlignment(6, Qt::AlignCenter); + treeItem->setText(7, pkg_app_[6]); + treeItem->setTextAlignment(7, Qt::AlignCenter); + treeItem->setText(8, pkg_app_[7]); + treeItem->setTextAlignment(8, Qt::AlignCenter); + treeItem->setText(9, pkg_app_[8]); + treeItem->setText(10, pkg_app_[9]); + for (const GameInfo& info : m_game_info->m_games) { // Check if game is installed. + if (info.serial == pkg_app_[1].toStdString()) { + treeItem->setText(2, QChar(0x2713)); + treeItem->setTextAlignment(2, Qt::AlignCenter); + } + } + for (const QString& item : m_pkg_patch_list) { + QStringList pkg_patch_ = item.split(";;"); + if (pkg_patch_[1] == pkg_app_[1]) { // check patches with serial. + m_full_pkg_list.append(item); + QTreeWidgetItem* childItem = new QTreeWidgetItem(treeItem); + childItem->setText(0, pkg_patch_[0]); + childItem->setText(1, pkg_patch_[1]); + childItem->setText(3, pkg_patch_[2]); + childItem->setTextAlignment(3, Qt::AlignCenter); + childItem->setText(4, pkg_patch_[3]); + childItem->setTextAlignment(4, Qt::AlignCenter); + childItem->setText(5, pkg_patch_[4]); + childItem->setTextAlignment(5, Qt::AlignCenter); + childItem->setText(6, pkg_patch_[5]); + childItem->setTextAlignment(6, Qt::AlignCenter); + childItem->setText(7, pkg_patch_[6]); + childItem->setTextAlignment(7, Qt::AlignCenter); + childItem->setText(8, pkg_patch_[7]); + childItem->setTextAlignment(8, Qt::AlignCenter); + childItem->setText(9, pkg_patch_[8]); + childItem->setText(10, pkg_patch_[9]); + } + } + } + + for (int column = 0; column < treeWidget->columnCount() - 2; ++column) { + // Resize the column to fit its contents + treeWidget->resizeColumnToContents(column); + } + // Update status bar. + statusBar->clearMessage(); + int numPkgs = m_pkg_list.size(); + QString statusMessage = QString::number(numPkgs) + " " + tr("Package"); + statusBar->showMessage(statusMessage); +} \ No newline at end of file diff --git a/src/qt_gui/pkg_viewer.h b/src/qt_gui/pkg_viewer.h new file mode 100644 index 000000000..265a03b92 --- /dev/null +++ b/src/qt_gui/pkg_viewer.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/io_file.h" +#include "core/file_format/pkg.h" +#include "core/file_format/pkg_type.h" +#include "core/file_format/psf.h" +#include "game_info.h" +#include "game_list_utils.h" +#include "gui_context_menus.h" + +class PKGViewer : public QMainWindow { + Q_OBJECT +public: + explicit PKGViewer( + std::shared_ptr game_info_get, QWidget* parent, + std::function InstallDragDropPkg = nullptr); + ~PKGViewer(); + void OpenPKGFolder(); + void CheckPKGFolders(); + void ProcessPKGInfo(); + +private: + GuiContextMenus m_gui_context_menus; + PKG package; + PSF psf; + PKGHeader pkgheader; + PKGEntry entry; + PSFHeader header; + char pkgTitleID[9]; + std::vector pkg; + u64 pkgSize = 0; + std::unordered_map map_strings; + std::unordered_map map_integers; + + u32_be pkg_content_flag; + std::shared_ptr m_game_info; + GameListUtils game_list_util; + // Status bar + QStatusBar* statusBar; + + std::vector> appTypes = { + {0, "FULL APP"}, + {1, "UPGRADABLE"}, + {2, "DEMO"}, + {3, "FREEMIUM"}, + }; + + QStringList m_full_pkg_list; + QStringList m_pkg_app_list; + QStringList m_pkg_patch_list; + QStringList m_pkg_list; + QStringList dir_list; + std::vector dir_list_std; + QTreeWidget* treeWidget = nullptr; +}; \ No newline at end of file