Merge branch 'main' into Feature/initial-ps4-ime-keyboard

This commit is contained in:
georgemoralis 2025-03-28 23:20:04 +02:00 committed by GitHub
commit 1f64132ef4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 1244 additions and 2974 deletions

View File

@ -113,28 +113,39 @@ git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH) git_branch_name(GIT_BRANCH)
string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S") string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S")
message("start git things")
# Try to get the upstream remote and branch # Try to get the upstream remote and branch
message("check for remote and branch")
execute_process( execute_process(
COMMAND git rev-parse --abbrev-ref --symbolic-full-name @{u} COMMAND git rev-parse --abbrev-ref --symbolic-full-name @{u}
OUTPUT_VARIABLE GIT_REMOTE_NAME OUTPUT_VARIABLE GIT_REMOTE_NAME
RESULT_VARIABLE GIT_BRANCH_RESULT RESULT_VARIABLE GIT_REMOTE_RESULT
ERROR_QUIET ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_STRIP_TRAILING_WHITESPACE
) )
# If there's no upstream set or the command failed, check remote.pushDefault # If there's no upstream set or the command failed, check remote.pushDefault
if (GIT_BRANCH_RESULT OR GIT_REMOTE_NAME STREQUAL "") if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "")
message("check default push")
execute_process( execute_process(
COMMAND git config --get remote.pushDefault COMMAND git config --get remote.pushDefault
OUTPUT_VARIABLE GIT_REMOTE_NAME OUTPUT_VARIABLE GIT_REMOTE_NAME
RESULT_VARIABLE GIT_PUSH_DEFAULT_RESULT RESULT_VARIABLE GIT_REMOTE_RESULT
ERROR_QUIET ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_STRIP_TRAILING_WHITESPACE
) )
endif()
# If remote.pushDefault is not set or fails, default to origin # If running in GitHub Actions and the above fails
if (GIT_PUSH_DEFAULT_RESULT OR GIT_REMOTE_NAME STREQUAL "") if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "")
set(GIT_REMOTE_NAME "origin") message("check github")
set(GIT_REMOTE_NAME "origin")
if (DEFINED ENV{GITHUB_HEAD_REF}) # PR branch name
set(GIT_BRANCH "pr-$ENV{GITHUB_HEAD_REF}")
elseif (DEFINED ENV{GITHUB_REF}) # Normal branch name
string(REGEX REPLACE "^refs/[^/]*/" "" GIT_BRANCH "$ENV{GITHUB_REF}")
else()
message("couldn't find branch")
set(GIT_BRANCH "detached-head")
endif() endif()
else() else()
# Extract remote name if the output contains a remote/branch format # Extract remote name if the output contains a remote/branch format
@ -148,6 +159,7 @@ else()
endif() endif()
# Get remote link # Get remote link
message("getting remote link")
execute_process( execute_process(
COMMAND git config --get remote.${GIT_REMOTE_NAME}.url COMMAND git config --get remote.${GIT_REMOTE_NAME}.url
OUTPUT_VARIABLE GIT_REMOTE_URL OUTPUT_VARIABLE GIT_REMOTE_URL
@ -156,6 +168,8 @@ execute_process(
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY)
message("end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}")
find_package(Boost 1.84.0 CONFIG) find_package(Boost 1.84.0 CONFIG)
find_package(FFmpeg 5.1.2 MODULE) find_package(FFmpeg 5.1.2 MODULE)
find_package(fmt 10.2.0 CONFIG) find_package(fmt 10.2.0 CONFIG)
@ -643,9 +657,6 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/aerolib/aerolib.h src/core/aerolib/aerolib.h
src/core/address_space.cpp src/core/address_space.cpp
src/core/address_space.h src/core/address_space.h
src/core/crypto/crypto.cpp
src/core/crypto/crypto.h
src/core/crypto/keys.h
src/core/devices/base_device.cpp src/core/devices/base_device.cpp
src/core/devices/base_device.h src/core/devices/base_device.h
src/core/devices/ioccom.h src/core/devices/ioccom.h
@ -663,10 +674,6 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/devices/srandom_device.cpp src/core/devices/srandom_device.cpp
src/core/devices/srandom_device.h src/core/devices/srandom_device.h
src/core/file_format/pfs.h src/core/file_format/pfs.h
src/core/file_format/pkg.cpp
src/core/file_format/pkg.h
src/core/file_format/pkg_type.cpp
src/core/file_format/pkg_type.h
src/core/file_format/psf.cpp src/core/file_format/psf.cpp
src/core/file_format/psf.h src/core/file_format/psf.h
src/core/file_format/playgo_chunk.cpp src/core/file_format/playgo_chunk.cpp
@ -675,8 +682,6 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/file_format/trp.h src/core/file_format/trp.h
src/core/file_sys/fs.cpp src/core/file_sys/fs.cpp
src/core/file_sys/fs.h src/core/file_sys/fs.h
src/core/loader.cpp
src/core/loader.h
src/core/loader/dwarf.cpp src/core/loader/dwarf.cpp
src/core/loader/dwarf.h src/core/loader/dwarf.h
src/core/loader/elf.cpp src/core/loader/elf.cpp
@ -966,10 +971,6 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
src/qt_gui/game_grid_frame.h src/qt_gui/game_grid_frame.h
src/qt_gui/game_install_dialog.cpp src/qt_gui/game_install_dialog.cpp
src/qt_gui/game_install_dialog.h src/qt_gui/game_install_dialog.h
src/qt_gui/install_dir_select.cpp
src/qt_gui/install_dir_select.h
src/qt_gui/pkg_viewer.cpp
src/qt_gui/pkg_viewer.h
src/qt_gui/trophy_viewer.cpp src/qt_gui/trophy_viewer.cpp
src/qt_gui/trophy_viewer.h src/qt_gui/trophy_viewer.h
src/qt_gui/elf_viewer.cpp src/qt_gui/elf_viewer.cpp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 658 KiB

View File

@ -13,7 +13,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
- [**RAM**](#ram) - [**RAM**](#ram)
- [**OS**](#os) - [**OS**](#os)
- [**Have the latest WIP version**](#how-to-run-the-latest-work-in-progress-builds-of-shadps4) - [**Have the latest WIP version**](#how-to-run-the-latest-work-in-progress-builds-of-shadps4)
- [**Install PKG files (Games and Updates)**](#install-pkg-files)
- [**Configure the emulator**](#configure-the-emulator) - [**Configure the emulator**](#configure-the-emulator)
## Minimum PC requirements ## Minimum PC requirements
@ -48,13 +47,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
2. Once downloaded, extract to its own folder, and run shadPS4's executable from the extracted folder. 2. Once downloaded, extract to its own folder, and run shadPS4's executable from the extracted folder.
3. Upon first launch, shadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that shadPS4 can use to install your PKG files to. 3. Upon first launch, shadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that contains your dumped games.
## Install PKG files
To install PKG files (game and updates), you will need the Qt application (with UI). You will have to go to "File" then to "Install Packages (PKG)", a window will open then you will have to select the files. You can install multiple PKG files at once. Once finished, the game should appear in the application.
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/2.png" width="800">
## Configure the emulator ## Configure the emulator

View File

@ -108,7 +108,7 @@ Now run the emulator. If Qt was enabled at configure time:
./build/shadps4 ./build/shadps4
``` ```
Otherwise, specify the path to your PKG's boot file: Otherwise, specify the path to your game's boot file:
```bash ```bash
./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin ./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin

View File

@ -41,7 +41,7 @@ static u32 screenWidth = 1280;
static u32 screenHeight = 720; static u32 screenHeight = 720;
static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
static std::string logFilter; static std::string logFilter;
static std::string logType = "async"; static std::string logType = "sync";
static std::string userName = "shadPS4"; static std::string userName = "shadPS4";
static std::string updateChannel; static std::string updateChannel;
static std::string chooseHomeTab; static std::string chooseHomeTab;
@ -98,7 +98,6 @@ u32 m_slider_pos_grid = 0;
u32 m_table_mode = 0; u32 m_table_mode = 0;
u32 m_window_size_W = 1280; u32 m_window_size_W = 1280;
u32 m_window_size_H = 720; u32 m_window_size_H = 720;
std::vector<std::string> m_pkg_viewer;
std::vector<std::string> m_elf_viewer; std::vector<std::string> m_elf_viewer;
std::vector<std::string> m_recent_files; std::vector<std::string> m_recent_files;
std::string emulator_language = "en_US"; std::string emulator_language = "en_US";
@ -601,11 +600,6 @@ void setMainWindowHeight(u32 height) {
m_window_size_H = height; m_window_size_H = height;
} }
void setPkgViewer(const std::vector<std::string>& pkgList) {
m_pkg_viewer.resize(pkgList.size());
m_pkg_viewer = pkgList;
}
void setElfViewer(const std::vector<std::string>& elfList) { void setElfViewer(const std::vector<std::string>& elfList) {
m_elf_viewer.resize(elfList.size()); m_elf_viewer.resize(elfList.size());
m_elf_viewer = elfList; m_elf_viewer = elfList;
@ -709,10 +703,6 @@ u32 getMainWindowHeight() {
return m_window_size_H; return m_window_size_H;
} }
std::vector<std::string> getPkgViewer() {
return m_pkg_viewer;
}
std::vector<std::string> getElfViewer() { std::vector<std::string> getElfViewer() {
return m_elf_viewer; return m_elf_viewer;
} }
@ -860,7 +850,7 @@ void load(const std::filesystem::path& path) {
m_window_size_H = toml::find_or<int>(gui, "mw_height", 0); m_window_size_H = toml::find_or<int>(gui, "mw_height", 0);
const auto install_dir_array = const auto install_dir_array =
toml::find_or<std::vector<std::string>>(gui, "installDirs", {}); toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {});
try { try {
install_dirs_enabled = toml::find<std::vector<bool>>(gui, "installDirsEnabled"); install_dirs_enabled = toml::find<std::vector<bool>>(gui, "installDirsEnabled");
@ -886,7 +876,6 @@ void load(const std::filesystem::path& path) {
main_window_geometry_y = toml::find_or<int>(gui, "geometry_y", 0); main_window_geometry_y = toml::find_or<int>(gui, "geometry_y", 0);
main_window_geometry_w = toml::find_or<int>(gui, "geometry_w", 0); main_window_geometry_w = toml::find_or<int>(gui, "geometry_w", 0);
main_window_geometry_h = toml::find_or<int>(gui, "geometry_h", 0); main_window_geometry_h = toml::find_or<int>(gui, "geometry_h", 0);
m_pkg_viewer = toml::find_or<std::vector<std::string>>(gui, "pkgDirs", {});
m_elf_viewer = toml::find_or<std::vector<std::string>>(gui, "elfDirs", {}); m_elf_viewer = toml::find_or<std::vector<std::string>>(gui, "elfDirs", {});
m_recent_files = toml::find_or<std::vector<std::string>>(gui, "recentFiles", {}); m_recent_files = toml::find_or<std::vector<std::string>>(gui, "recentFiles", {});
m_table_mode = toml::find_or<int>(gui, "gameTableMode", 0); m_table_mode = toml::find_or<int>(gui, "gameTableMode", 0);
@ -1105,7 +1094,6 @@ void saveMainWindow(const std::filesystem::path& path) {
data["GUI"]["geometry_y"] = main_window_geometry_y; data["GUI"]["geometry_y"] = main_window_geometry_y;
data["GUI"]["geometry_w"] = main_window_geometry_w; data["GUI"]["geometry_w"] = main_window_geometry_w;
data["GUI"]["geometry_h"] = main_window_geometry_h; data["GUI"]["geometry_h"] = main_window_geometry_h;
data["GUI"]["pkgDirs"] = m_pkg_viewer;
data["GUI"]["elfDirs"] = m_elf_viewer; data["GUI"]["elfDirs"] = m_elf_viewer;
data["GUI"]["recentFiles"] = m_recent_files; data["GUI"]["recentFiles"] = m_recent_files;
@ -1129,7 +1117,7 @@ void setDefaultValues() {
screenWidth = 1280; screenWidth = 1280;
screenHeight = 720; screenHeight = 720;
logFilter = ""; logFilter = "";
logType = "async"; logType = "sync";
userName = "shadPS4"; userName = "shadPS4";
if (Common::isRelease) { if (Common::isRelease) {
updateChannel = "Release"; updateChannel = "Release";

View File

@ -154,7 +154,6 @@ void setSliderPositionGrid(u32 pos);
void setTableMode(u32 mode); void setTableMode(u32 mode);
void setMainWindowWidth(u32 width); void setMainWindowWidth(u32 width);
void setMainWindowHeight(u32 height); void setMainWindowHeight(u32 height);
void setPkgViewer(const std::vector<std::string>& pkgList);
void setElfViewer(const std::vector<std::string>& elfList); void setElfViewer(const std::vector<std::string>& elfList);
void setRecentFiles(const std::vector<std::string>& recentFiles); void setRecentFiles(const std::vector<std::string>& recentFiles);
void setEmulatorLanguage(std::string language); void setEmulatorLanguage(std::string language);
@ -174,7 +173,6 @@ u32 getSliderPositionGrid();
u32 getTableMode(); u32 getTableMode();
u32 getMainWindowWidth(); u32 getMainWindowWidth();
u32 getMainWindowHeight(); u32 getMainWindowHeight();
std::vector<std::string> getPkgViewer();
std::vector<std::string> getElfViewer(); std::vector<std::string> getElfViewer();
std::vector<std::string> getRecentFiles(); std::vector<std::string> getRecentFiles();
std::string getEmulatorLanguage(); std::string getEmulatorLanguage();

View File

@ -1,215 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#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<CryptoPP::byte, 32> dec_key,
std::span<const CryptoPP::byte, 256> 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<CryptoPP::byte, 256> 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<const CryptoPP::byte, 64> cipher_input,
std::span<CryptoPP::byte, 32> ivkey_result) {
CryptoPP::SHA256 sha256;
std::array<CryptoPP::byte, CryptoPP::SHA256::DIGESTSIZE> 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<const CryptoPP::byte, 32> ivkey,
std::span<const CryptoPP::byte, 256> ciphertext,
std::span<CryptoPP::byte, 256> decrypted) {
std::array<CryptoPP::byte, CryptoPP::AES::DEFAULT_KEYLENGTH> key;
std::array<CryptoPP::byte, CryptoPP::AES::DEFAULT_KEYLENGTH> 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<const CryptoPP::byte, 32> ivkey,
std::span<CryptoPP::byte> ciphertext,
std::span<CryptoPP::byte> decrypted) {
std::array<CryptoPP::byte, CryptoPP::AES::DEFAULT_KEYLENGTH> key;
std::array<CryptoPP::byte, CryptoPP::AES::DEFAULT_KEYLENGTH> 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<CryptoPP::byte, 16> trophyKey,
std::span<CryptoPP::byte, 16> NPcommID,
std::span<CryptoPP::byte, 16> efsmIv, std::span<CryptoPP::byte> ciphertext,
std::span<CryptoPP::byte> decrypted) {
// step 1: Encrypt NPcommID
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encrypt;
std::vector<CryptoPP::byte> trophyIv(16, 0);
std::vector<CryptoPP::byte> trpKey(16);
encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data());
encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16);
// step 2: decrypt efsm.
CryptoPP::CBC_Mode<CryptoPP::AES>::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<const CryptoPP::byte, 32> ekpfs,
std::span<const CryptoPP::byte, 16> seed,
std::span<CryptoPP::byte, 16> dataKey,
std::span<CryptoPP::byte, 16> tweakKey) {
CryptoPP::HMAC<CryptoPP::SHA256> 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<CryptoPP::byte> 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<const CryptoPP::byte, 16> dataKey,
std::span<const CryptoPP::byte, 16> tweakKey, std::span<const u8> src_image,
std::span<CryptoPP::byte> 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<CryptoPP::AES>::Encryption encrypt(tweakKey.data(), tweakKey.size());
CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption decrypt(dataKey.data(), dataKey.size());
std::array<CryptoPP::byte, 16> tweak{};
std::array<CryptoPP::byte, 16> encryptedTweak;
std::array<CryptoPP::byte, 16> xorBuffer;
std::memcpy(tweak.data(), &current_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);
}
}
}

View File

@ -1,63 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <cryptopp/aes.h>
#include <cryptopp/filters.h>
#include <cryptopp/modes.h>
#include <cryptopp/oaep.h>
#include <cryptopp/osrng.h>
#include <cryptopp/rsa.h>
#include <cryptopp/sha.h>
#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<CryptoPP::byte, 32> dk3,
std::span<const CryptoPP::byte, 256> ciphertext,
bool is_dk3); // RSAES_PKCS1v15_
void ivKeyHASH256(std::span<const CryptoPP::byte, 64> cipher_input,
std::span<CryptoPP::byte, 32> ivkey_result);
void aesCbcCfb128Decrypt(std::span<const CryptoPP::byte, 32> ivkey,
std::span<const CryptoPP::byte, 256> ciphertext,
std::span<CryptoPP::byte, 256> decrypted);
void aesCbcCfb128DecryptEntry(std::span<const CryptoPP::byte, 32> ivkey,
std::span<CryptoPP::byte> ciphertext,
std::span<CryptoPP::byte> decrypted);
void decryptEFSM(std::span<CryptoPP::byte, 16> trophyKey,
std::span<CryptoPP::byte, 16> NPcommID, std::span<CryptoPP::byte, 16> efsmIv,
std::span<CryptoPP::byte> ciphertext, std::span<CryptoPP::byte> decrypted);
void PfsGenCryptoKey(std::span<const CryptoPP::byte, 32> ekpfs,
std::span<const CryptoPP::byte, 16> seed,
std::span<CryptoPP::byte, 16> dataKey,
std::span<CryptoPP::byte, 16> tweakKey);
void decryptPFS(std::span<const CryptoPP::byte, 16> dataKey,
std::span<const CryptoPP::byte, 16> tweakKey, std::span<const u8> src_image,
std::span<CryptoPP::byte> 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<CryptoPP::byte, 16> 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;
}
}
};

View File

@ -1,305 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cryptopp/rsa.h>
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};
};

View File

@ -1,473 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <zlib.h>
#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<char> compressed_data, std::span<char> 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<unsigned char*>(compressed_data.data());
decompressStream.avail_out = decompressed_data.size();
decompressStream.next_out = reinterpret_cast<unsigned char*>(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<const u8> 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<u8>(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<u8>(&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<u8, 64> concatenated_ivkey_dk3;
std::array<u8, 32> seed_digest;
std::array<std::array<u8, 32>, 7> digest1;
std::array<std::array<u8, 256>, 7> key1;
std::array<u8, 256> 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<u8> data;
data.resize(entry.size);
file.ReadRaw<u8>(data.data(), entry.size);
out.WriteRaw<u8>(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<u8> data;
data.resize(entry.size);
file.ReadRaw<u8>(data.data(), entry.size);
out.WriteRaw<u8>(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<u8> data;
data.resize(entry.size);
file.ReadRaw<u8>(data.data(), entry.size);
std::span<u8> cipherNp(data.data(), entry.size);
std::array<u8, 64> 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<u8, 16> 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<u8> pfsc(length);
if (length != 0) {
// Read encrypted pfs_image
std::vector<u8> pfs_encrypted(length);
file.Seek(pkgheader.pfs_image_offset);
file.Read(pfs_encrypted);
file.Close();
// Decrypt the pfs_image.
std::vector<u8> 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(&sectorMap[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<char> compressedData;
std::vector<char> 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<char> compressedData;
std::vector<char> decompressedData(0x10000);
u64 pfsc_buf_size = 0x11000; // extra 0x1000
std::vector<u8> pfsc(pfsc_buf_size);
std::vector<u8> 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<u8>(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<u8>(decompressedData.data(), write_size);
}
}
pkgFile.Close();
inflated.Close();
}
}

View File

@ -1,174 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <filesystem>
#include <string>
#include <unordered_map>
#include <vector>
#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<u8> 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<u32>(flag);
}
static constexpr std::array<std::pair<PKGContentFlag, std::string_view>, 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<int, std::filesystem::path> extractPaths;
std::vector<pfs_fs_table> fsTable;
std::vector<Inode> iNodeBuf;
std::vector<u64> sectorMap;
u64 pfsc_offset;
std::array<u8, 32> dk3_;
std::array<u8, 32> ivKey;
std::array<u8, 256> imgKey;
std::array<u8, 32> ekpfsKey;
std::array<u8, 16> dataKey;
std::array<u8, 16> tweakKey;
std::vector<u8> decNp;
std::filesystem::path pkgpath;
std::filesystem::path current_dir;
std::filesystem::path extract_path;
};

View File

@ -1,638 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include "pkg_type.h"
struct PkgEntryValue {
u32 type;
std::string_view name;
operator u32() const noexcept {
return type;
}
};
constexpr static std::array<PkgEntryValue, 611> 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 "";
}

View File

@ -1,10 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string_view>
#include "common/types.h"
/// Retrieves the PKG entry name from its type identifier.
std::string_view GetEntryNameByType(u32 type);

View File

@ -1,10 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include "common/config.h" #include "common/config.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "trp.h" #include "core/file_format/trp.h"
static void DecryptEFSM(std::span<CryptoPP::byte, 16> trophyKey,
std::span<CryptoPP::byte, 16> NPcommID,
std::span<CryptoPP::byte, 16> efsmIv, std::span<CryptoPP::byte> ciphertext,
std::span<CryptoPP::byte> decrypted) {
// step 1: Encrypt NPcommID
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encrypt;
std::vector<CryptoPP::byte> trophyIv(16, 0);
std::vector<CryptoPP::byte> trpKey(16);
encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data());
encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16);
// step 2: decrypt efsm.
CryptoPP::CBC_Mode<CryptoPP::AES>::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);
}
}
TRP::TRP() = default; TRP::TRP() = default;
TRP::~TRP() = default; TRP::~TRP() = default;
@ -115,7 +141,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit
return false; return false;
} }
file.Read(ESFM); file.Read(ESFM);
crypto.decryptEFSM(user_key, np_comm_id, esfmIv, ESFM, XML); // decrypt DecryptEFSM(user_key, np_comm_id, esfmIv, ESFM, XML); // decrypt
removePadding(XML); removePadding(XML);
std::string xml_name = entry.entry_name; std::string xml_name = entry.entry_name;
size_t pos = xml_name.find("ESFM"); size_t pos = xml_name.find("ESFM");

View File

@ -7,7 +7,6 @@
#include "common/endian.h" #include "common/endian.h"
#include "common/io_file.h" #include "common/io_file.h"
#include "common/types.h" #include "common/types.h"
#include "core/crypto/crypto.h"
struct TrpHeader { struct TrpHeader {
u32_be magic; // (0xDCA24D00) u32_be magic; // (0xDCA24D00)
@ -37,10 +36,9 @@ public:
void GetNPcommID(const std::filesystem::path& trophyPath, int index); void GetNPcommID(const std::filesystem::path& trophyPath, int index);
private: private:
Crypto crypto;
std::vector<u8> NPcommID = std::vector<u8>(12); std::vector<u8> NPcommID = std::vector<u8>(12);
std::array<u8, 16> np_comm_id{}; std::array<u8, 16> np_comm_id{};
std::array<u8, 16> esfmIv{}; std::array<u8, 16> esfmIv{};
std::filesystem::path trpFilesPath; std::filesystem::path trpFilesPath;
static constexpr int iv_len = 16; static constexpr int iv_len = 16;
}; };

View File

@ -1,28 +0,0 @@
// 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

View File

@ -1,18 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
namespace Loader {
constexpr static u32 PkgMagic = 0x544e437f;
enum class FileTypes {
Unknown,
Pkg,
};
FileTypes DetectFileType(const std::filesystem::path& filepath);
} // namespace Loader

View File

@ -185,7 +185,8 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
// Recompute if opacity changed or we switched to a different game // Recompute if opacity changed or we switched to a different game
if (opacity != m_last_opacity || game.pic_path != m_current_game_path) { if (opacity != m_last_opacity || game.pic_path != m_current_game_path) {
QImage original_image(QString::fromStdString(game.pic_path.string())); auto image_path = game.pic_path.u8string();
QImage original_image(QString::fromStdString({image_path.begin(), image_path.end()}));
if (!original_image.isNull()) { if (!original_image.isNull()) {
backgroundImage = m_game_list_utils.ChangeImageOpacity( backgroundImage = m_game_list_utils.ChangeImageOpacity(
original_image, original_image.rect(), opacity / 100.0f); original_image, original_image.rect(), opacity / 100.0f);

View File

@ -7,7 +7,6 @@
#include <QDesktopServices> #include <QDesktopServices>
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
#include <QTreeWidget>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include <qt_gui/background_music_player.h> #include <qt_gui/background_music_player.h>
@ -599,30 +598,6 @@ public:
return -1; return -1;
} }
void RequestGameMenuPKGViewer(
const QPoint& pos, QStringList m_pkg_app_list, QTreeWidget* treeWidget,
std::function<void(std::filesystem::path, int, int)> InstallDragDropPkg) {
QPoint global_pos = treeWidget->viewport()->mapToGlobal(pos); // context menu position
QTreeWidgetItem* currentItem = treeWidget->currentItem(); // current clicked item
int itemIndex = GetRowIndex(treeWidget, currentItem); // row
QMenu menu(treeWidget);
QAction installPackage(tr("Install PKG"), treeWidget);
menu.addAction(&installPackage);
auto selected = menu.exec(global_pos);
if (!selected) {
return;
}
if (selected == &installPackage) {
QStringList pkg_app_ = m_pkg_app_list[itemIndex].split(";;");
std::filesystem::path path = Common::FS::PathFromQString(pkg_app_[9]);
InstallDragDropPkg(path, 1, 1);
}
}
private: private:
bool convertPngToIco(const QString& pngFilePath, const QString& icoFilePath) { bool convertPngToIco(const QString& pngFilePath, const QString& icoFilePath) {
// Load the PNG image // Load the PNG image

View File

@ -1,94 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QDir>
#include <QFileDialog>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#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<QString> 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;
}

View File

@ -1,42 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QDialog>
#include <QListWidget>
#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;
};

View File

@ -7,6 +7,7 @@
#include <QKeyEvent> #include <QKeyEvent>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QProgressDialog> #include <QProgressDialog>
#include <QStatusBar>
#include "about_dialog.h" #include "about_dialog.h"
#include "cheats_patches.h" #include "cheats_patches.h"
@ -19,10 +20,7 @@
#include "common/string_util.h" #include "common/string_util.h"
#include "common/version.h" #include "common/version.h"
#include "control_settings.h" #include "control_settings.h"
#include "core/file_format/pkg.h"
#include "core/loader.h"
#include "game_install_dialog.h" #include "game_install_dialog.h"
#include "install_dir_select.h"
#include "kbm_gui.h" #include "kbm_gui.h"
#include "main_window.h" #include "main_window.h"
#include "settings_dialog.h" #include "settings_dialog.h"
@ -718,7 +716,6 @@ void MainWindow::CreateConnects() {
}); });
// Package install. // Package install.
connect(ui->bootInstallPkgAct, &QAction::triggered, this, &MainWindow::InstallPkg);
connect(ui->bootGameAct, &QAction::triggered, this, &MainWindow::BootGame); connect(ui->bootGameAct, &QAction::triggered, this, &MainWindow::BootGame);
connect(ui->gameInstallPathAct, &QAction::triggered, this, &MainWindow::InstallDirectory); connect(ui->gameInstallPathAct, &QAction::triggered, this, &MainWindow::InstallDirectory);
@ -726,15 +723,6 @@ void MainWindow::CreateConnects() {
connect(ui->addElfFolderAct, &QAction::triggered, m_elf_viewer.data(), connect(ui->addElfFolderAct, &QAction::triggered, m_elf_viewer.data(),
&ElfViewer::OpenElfFolder); &ElfViewer::OpenElfFolder);
// Package Viewer.
connect(ui->pkgViewerAct, &QAction::triggered, this, [this]() {
PKGViewer* pkgViewer = new PKGViewer(
m_game_info, this, [this](std::filesystem::path file, int pkgNum, int nPkg) {
this->InstallDragDropPkg(file, pkgNum, nPkg);
});
pkgViewer->show();
});
// Trophy Viewer // Trophy Viewer
connect(ui->trophyViewerAct, &QAction::triggered, this, [this]() { connect(ui->trophyViewerAct, &QAction::triggered, this, [this]() {
if (m_game_info->m_games.empty()) { if (m_game_info->m_games.empty()) {
@ -965,22 +953,6 @@ void MainWindow::SaveWindowState() const {
this->geometry().width(), this->geometry().height()); this->geometry().width(), this->geometry().height());
} }
void MainWindow::InstallPkg() {
QFileDialog dialog;
dialog.setFileMode(QFileDialog::ExistingFiles);
dialog.setNameFilter(tr("PKG File (*.PKG *.pkg)"));
if (dialog.exec()) {
QStringList fileNames = dialog.selectedFiles();
int nPkg = fileNames.size();
int pkgNum = 0;
for (const QString& file : fileNames) {
++pkgNum;
std::filesystem::path path = Common::FS::PathFromQString(file);
MainWindow::InstallDragDropPkg(path, pkgNum, nPkg);
}
}
}
void MainWindow::BootGame() { void MainWindow::BootGame() {
QFileDialog dialog; QFileDialog dialog;
dialog.setFileMode(QFileDialog::ExistingFile); dialog.setFileMode(QFileDialog::ExistingFile);
@ -1004,260 +976,6 @@ void MainWindow::BootGame() {
} }
} }
void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg) {
if (Loader::DetectFileType(file) == Loader::FileTypes::Pkg) {
std::string failreason;
pkg = PKG();
if (!pkg.Open(file, failreason)) {
QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason));
return;
}
if (!psf.Open(pkg.sfo)) {
QMessageBox::critical(this, tr("PKG ERROR"),
"Could not read SFO. Check log for details");
return;
}
auto category = psf.GetString("CATEGORY");
if (!use_for_all_queued || pkgNum == 1) {
InstallDirSelect ids;
const auto selected = ids.exec();
if (selected == QDialog::Rejected) {
return;
}
last_install_dir = ids.getSelectedDirectory();
delete_file_on_install = ids.deleteFileOnInstall();
use_for_all_queued = ids.useForAllQueued();
}
std::filesystem::path game_install_dir = last_install_dir;
QString pkgType = QString::fromStdString(pkg.GetPkgFlags());
bool use_game_update = pkgType.contains("PATCH") && Config::getSeparateUpdateEnabled();
// Default paths
auto game_folder_path = game_install_dir / pkg.GetTitleID();
auto game_update_path = use_game_update ? game_folder_path.parent_path() /
(std::string{pkg.GetTitleID()} + "-patch")
: game_folder_path;
const int max_depth = 5;
if (pkgType.contains("PATCH")) {
// For patches, try to find the game recursively
auto found_game = Common::FS::FindGameByID(game_install_dir,
std::string{pkg.GetTitleID()}, max_depth);
if (found_game.has_value()) {
game_folder_path = found_game.value().parent_path();
game_update_path = use_game_update ? game_folder_path.parent_path() /
(std::string{pkg.GetTitleID()} + "-patch")
: game_folder_path;
}
} else {
// For base games, we check if the game is already installed
auto found_game = Common::FS::FindGameByID(game_install_dir,
std::string{pkg.GetTitleID()}, max_depth);
if (found_game.has_value()) {
game_folder_path = found_game.value().parent_path();
}
// If the game is not found, we install it in the game install directory
else {
game_folder_path = game_install_dir / pkg.GetTitleID();
}
game_update_path = use_game_update ? game_folder_path.parent_path() /
(std::string{pkg.GetTitleID()} + "-patch")
: game_folder_path;
}
QString gameDirPath;
Common::FS::PathToQString(gameDirPath, game_folder_path);
QDir game_dir(gameDirPath);
if (game_dir.exists()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("PKG Extraction"));
std::string content_id;
if (auto value = psf.GetString("CONTENT_ID"); value.has_value()) {
content_id = std::string{*value};
} else {
QMessageBox::critical(this, tr("PKG ERROR"), "PSF file there is no CONTENT_ID");
return;
}
std::string entitlement_label = Common::SplitString(content_id, '-')[2];
auto addon_extract_path =
Config::getAddonInstallDir() / pkg.GetTitleID() / entitlement_label;
QString addonDirPath;
Common::FS::PathToQString(addonDirPath, addon_extract_path);
QDir addon_dir(addonDirPath);
if (pkgType.contains("PATCH")) {
QString pkg_app_version;
if (auto app_ver = psf.GetString("APP_VER"); app_ver.has_value()) {
pkg_app_version = QString::fromStdString(std::string{*app_ver});
} else {
QMessageBox::critical(this, tr("PKG ERROR"), "PSF file there is no APP_VER");
return;
}
std::filesystem::path sce_folder_path =
std::filesystem::exists(game_update_path / "sce_sys" / "param.sfo")
? game_update_path / "sce_sys" / "param.sfo"
: game_folder_path / "sce_sys" / "param.sfo";
psf.Open(sce_folder_path);
QString game_app_version;
if (auto app_ver = psf.GetString("APP_VER"); app_ver.has_value()) {
game_app_version = QString::fromStdString(std::string{*app_ver});
} else {
QMessageBox::critical(this, tr("PKG ERROR"), "PSF file there is no APP_VER");
return;
}
double appD = game_app_version.toDouble();
double pkgD = pkg_app_version.toDouble();
if (pkgD == appD) {
msgBox.setText(QString(tr("Patch detected!") + "\n" +
tr("PKG and Game versions match: ") + pkg_app_version +
"\n" + tr("Would you like to overwrite?")));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
} else if (pkgD < appD) {
msgBox.setText(QString(tr("Patch detected!") + "\n" +
tr("PKG Version %1 is older than installed version: ")
.arg(pkg_app_version) +
game_app_version + "\n" +
tr("Would you like to overwrite?")));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
} else {
msgBox.setText(QString(tr("Patch detected!") + "\n" +
tr("Game is installed: ") + game_app_version + "\n" +
tr("Would you like to install Patch: ") +
pkg_app_version + " ?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
}
int result = msgBox.exec();
if (result == QMessageBox::Yes) {
// Do nothing.
} else {
return;
}
} else if (category == "ac") {
if (!addon_dir.exists()) {
QMessageBox addonMsgBox;
addonMsgBox.setWindowTitle(tr("DLC Installation"));
addonMsgBox.setText(QString(tr("Would you like to install DLC: %1?"))
.arg(QString::fromStdString(entitlement_label)));
addonMsgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
addonMsgBox.setDefaultButton(QMessageBox::No);
int result = addonMsgBox.exec();
if (result == QMessageBox::Yes) {
game_update_path = addon_extract_path;
} else {
return;
}
} else {
msgBox.setText(QString(tr("DLC already installed:") + "\n" + addonDirPath +
"\n\n" + tr("Would you like to overwrite?")));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
int result = msgBox.exec();
if (result == QMessageBox::Yes) {
game_update_path = addon_extract_path;
} else {
return;
}
}
} else {
msgBox.setText(QString(tr("Game already installed") + "\n" + gameDirPath + "\n" +
tr("Would you like to overwrite?")));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
int result = msgBox.exec();
if (result == QMessageBox::Yes) {
// Do nothing.
} else {
return;
}
}
} else {
// Do nothing;
if (pkgType.contains("PATCH") || category == "ac") {
QMessageBox::information(
this, tr("PKG Extraction"),
tr("PKG is a patch or DLC, please install the game first!"));
return;
}
// what else?
}
if (!pkg.Extract(file, game_update_path, failreason)) {
QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason));
} else {
int nfiles = pkg.GetNumberOfFiles();
if (nfiles > 0) {
QVector<int> indices;
for (int i = 0; i < nfiles; i++) {
indices.append(i);
}
QProgressDialog dialog;
dialog.setWindowTitle(tr("PKG Extraction"));
dialog.setWindowModality(Qt::WindowModal);
QString extractmsg = QString(tr("Extracting PKG %1/%2")).arg(pkgNum).arg(nPkg);
dialog.setLabelText(extractmsg);
dialog.setAutoClose(true);
dialog.setRange(0, nfiles);
dialog.setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter,
dialog.size(), this->geometry()));
QFutureWatcher<void> futureWatcher;
connect(&futureWatcher, &QFutureWatcher<void>::finished, this, [=, this]() {
if (pkgNum == nPkg) {
QString path;
// We want to show the parent path instead of the full path
Common::FS::PathToQString(path, game_folder_path.parent_path());
QIcon windowIcon(
Common::FS::PathToUTF8String(game_folder_path / "sce_sys/icon0.png")
.c_str());
QMessageBox extractMsgBox(this);
extractMsgBox.setWindowTitle(tr("Extraction Finished"));
if (!windowIcon.isNull()) {
extractMsgBox.setWindowIcon(windowIcon);
}
extractMsgBox.setText(
QString(tr("Game successfully installed at %1")).arg(path));
extractMsgBox.addButton(QMessageBox::Ok);
extractMsgBox.setDefaultButton(QMessageBox::Ok);
connect(&extractMsgBox, &QMessageBox::buttonClicked, this,
[&](QAbstractButton* button) {
if (extractMsgBox.button(QMessageBox::Ok) == button) {
extractMsgBox.close();
emit ExtractionFinished();
}
});
extractMsgBox.exec();
}
if (delete_file_on_install) {
std::filesystem::remove(file);
}
});
connect(&dialog, &QProgressDialog::canceled, [&]() { futureWatcher.cancel(); });
connect(&futureWatcher, &QFutureWatcher<void>::progressValueChanged, &dialog,
&QProgressDialog::setValue);
futureWatcher.setFuture(
QtConcurrent::map(indices, [&](int index) { pkg.ExtractFiles(index); }));
dialog.exec();
}
}
} else {
QMessageBox::critical(this, tr("PKG ERROR"),
tr("File doesn't appear to be a valid PKG file"));
}
}
void MainWindow::InstallDirectory() { void MainWindow::InstallDirectory() {
GameInstallDialog dlg; GameInstallDialog dlg;
dlg.exec(); dlg.exec();
@ -1340,7 +1058,6 @@ QIcon MainWindow::RecolorIcon(const QIcon& icon, bool isWhite) {
} }
void MainWindow::SetUiIcons(bool isWhite) { void MainWindow::SetUiIcons(bool isWhite) {
ui->bootInstallPkgAct->setIcon(RecolorIcon(ui->bootInstallPkgAct->icon(), isWhite));
ui->bootGameAct->setIcon(RecolorIcon(ui->bootGameAct->icon(), isWhite)); ui->bootGameAct->setIcon(RecolorIcon(ui->bootGameAct->icon(), isWhite));
ui->shadFolderAct->setIcon(RecolorIcon(ui->shadFolderAct->icon(), isWhite)); ui->shadFolderAct->setIcon(RecolorIcon(ui->shadFolderAct->icon(), isWhite));
ui->exitAct->setIcon(RecolorIcon(ui->exitAct->icon(), isWhite)); ui->exitAct->setIcon(RecolorIcon(ui->exitAct->icon(), isWhite));
@ -1368,7 +1085,6 @@ void MainWindow::SetUiIcons(bool isWhite) {
ui->keyboardButton->setIcon(RecolorIcon(ui->keyboardButton->icon(), isWhite)); ui->keyboardButton->setIcon(RecolorIcon(ui->keyboardButton->icon(), isWhite));
ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite)); ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite));
ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite)); ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite));
ui->pkgViewerAct->setIcon(RecolorIcon(ui->pkgViewerAct->icon(), isWhite));
ui->trophyViewerAct->setIcon(RecolorIcon(ui->trophyViewerAct->icon(), isWhite)); ui->trophyViewerAct->setIcon(RecolorIcon(ui->trophyViewerAct->icon(), isWhite));
ui->configureAct->setIcon(RecolorIcon(ui->configureAct->icon(), isWhite)); ui->configureAct->setIcon(RecolorIcon(ui->configureAct->icon(), isWhite));
ui->addElfFolderAct->setIcon(RecolorIcon(ui->addElfFolderAct->icon(), isWhite)); ui->addElfFolderAct->setIcon(RecolorIcon(ui->addElfFolderAct->icon(), isWhite));

View File

@ -22,7 +22,6 @@
#include "game_list_utils.h" #include "game_list_utils.h"
#include "main_window_themes.h" #include "main_window_themes.h"
#include "main_window_ui.h" #include "main_window_ui.h"
#include "pkg_viewer.h"
class GameListFrame; class GameListFrame;
@ -36,7 +35,6 @@ public:
explicit MainWindow(QWidget* parent = nullptr); explicit MainWindow(QWidget* parent = nullptr);
~MainWindow(); ~MainWindow();
bool Init(); bool Init();
void InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg);
void InstallDirectory(); void InstallDirectory();
void StartGame(); void StartGame();
void PauseGame(); void PauseGame();
@ -72,7 +70,6 @@ private:
void SetLastUsedTheme(); void SetLastUsedTheme();
void SetLastIconSizeBullet(); void SetLastIconSizeBullet();
void SetUiIcons(bool isWhite); void SetUiIcons(bool isWhite);
void InstallPkg();
void BootGame(); void BootGame();
void AddRecentFiles(QString filePath); void AddRecentFiles(QString filePath);
void LoadTranslation(); void LoadTranslation();
@ -89,7 +86,6 @@ private:
QActionGroup* m_list_mode_act_group = nullptr; QActionGroup* m_list_mode_act_group = nullptr;
QActionGroup* m_theme_act_group = nullptr; QActionGroup* m_theme_act_group = nullptr;
QActionGroup* m_recent_files_group = nullptr; QActionGroup* m_recent_files_group = nullptr;
PKG pkg;
// Dockable widget frames // Dockable widget frames
WindowThemes m_window_themes; WindowThemes m_window_themes;
GameListUtils m_game_list_utils; GameListUtils m_game_list_utils;
@ -120,20 +116,6 @@ protected:
} }
} }
void dropEvent(QDropEvent* event1) override {
const QMimeData* mimeData = event1->mimeData();
if (mimeData->hasUrls()) {
QList<QUrl> urlList = mimeData->urls();
int pkgNum = 0;
int nPkg = urlList.size();
for (const QUrl& url : urlList) {
pkgNum++;
std::filesystem::path path = Common::FS::PathFromQString(url.toLocalFile());
InstallDragDropPkg(path, pkgNum, nPkg);
}
}
}
void resizeEvent(QResizeEvent* event) override; void resizeEvent(QResizeEvent* event) override;
std::filesystem::path last_install_dir = ""; std::filesystem::path last_install_dir = "";

View File

@ -9,7 +9,6 @@
class Ui_MainWindow { class Ui_MainWindow {
public: public:
QAction* bootInstallPkgAct;
QAction* bootGameAct; QAction* bootGameAct;
QAction* addElfFolderAct; QAction* addElfFolderAct;
QAction* shadFolderAct; QAction* shadFolderAct;
@ -27,7 +26,6 @@ public:
QAction* gameInstallPathAct; QAction* gameInstallPathAct;
QAction* downloadCheatsPatchesAct; QAction* downloadCheatsPatchesAct;
QAction* dumpGameListAct; QAction* dumpGameListAct;
QAction* pkgViewerAct;
QAction* trophyViewerAct; QAction* trophyViewerAct;
#ifdef ENABLE_UPDATER #ifdef ENABLE_UPDATER
QAction* updaterAct; QAction* updaterAct;
@ -87,9 +85,6 @@ public:
MainWindow->setDockNestingEnabled(true); MainWindow->setDockNestingEnabled(true);
MainWindow->setDockOptions(QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks | MainWindow->setDockOptions(QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks |
QMainWindow::AnimatedDocks | QMainWindow::GroupedDragging); QMainWindow::AnimatedDocks | QMainWindow::GroupedDragging);
bootInstallPkgAct = new QAction(MainWindow);
bootInstallPkgAct->setObjectName("bootInstallPkgAct");
bootInstallPkgAct->setIcon(QIcon(":images/file_icon.png"));
bootGameAct = new QAction(MainWindow); bootGameAct = new QAction(MainWindow);
bootGameAct->setObjectName("bootGameAct"); bootGameAct->setObjectName("bootGameAct");
bootGameAct->setIcon(QIcon(":images/play_icon.png")); bootGameAct->setIcon(QIcon(":images/play_icon.png"));
@ -148,9 +143,6 @@ public:
dumpGameListAct = new QAction(MainWindow); dumpGameListAct = new QAction(MainWindow);
dumpGameListAct->setObjectName("dumpGameList"); dumpGameListAct->setObjectName("dumpGameList");
dumpGameListAct->setIcon(QIcon(":images/dump_icon.png")); dumpGameListAct->setIcon(QIcon(":images/dump_icon.png"));
pkgViewerAct = new QAction(MainWindow);
pkgViewerAct->setObjectName("pkgViewer");
pkgViewerAct->setIcon(QIcon(":images/file_icon.png"));
trophyViewerAct = new QAction(MainWindow); trophyViewerAct = new QAction(MainWindow);
trophyViewerAct->setObjectName("trophyViewer"); trophyViewerAct->setObjectName("trophyViewer");
trophyViewerAct->setIcon(QIcon(":images/trophy_icon.png")); trophyViewerAct->setIcon(QIcon(":images/trophy_icon.png"));
@ -309,7 +301,6 @@ public:
menuBar->addAction(menuView->menuAction()); menuBar->addAction(menuView->menuAction());
menuBar->addAction(menuSettings->menuAction()); menuBar->addAction(menuSettings->menuAction());
menuBar->addAction(menuHelp->menuAction()); menuBar->addAction(menuHelp->menuAction());
menuFile->addAction(bootInstallPkgAct);
menuFile->addAction(bootGameAct); menuFile->addAction(bootGameAct);
menuFile->addSeparator(); menuFile->addSeparator();
menuFile->addAction(addElfFolderAct); menuFile->addAction(addElfFolderAct);
@ -345,7 +336,6 @@ public:
menuSettings->addAction(menuUtils->menuAction()); menuSettings->addAction(menuUtils->menuAction());
menuUtils->addAction(downloadCheatsPatchesAct); menuUtils->addAction(downloadCheatsPatchesAct);
menuUtils->addAction(dumpGameListAct); menuUtils->addAction(dumpGameListAct);
menuUtils->addAction(pkgViewerAct);
menuUtils->addAction(trophyViewerAct); menuUtils->addAction(trophyViewerAct);
#ifdef ENABLE_UPDATER #ifdef ENABLE_UPDATER
menuHelp->addAction(updaterAct); menuHelp->addAction(updaterAct);
@ -361,8 +351,6 @@ public:
MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "shadPS4", nullptr)); MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "shadPS4", nullptr));
addElfFolderAct->setText( addElfFolderAct->setText(
QCoreApplication::translate("MainWindow", "Open/Add Elf Folder", nullptr)); QCoreApplication::translate("MainWindow", "Open/Add Elf Folder", nullptr));
bootInstallPkgAct->setText(
QCoreApplication::translate("MainWindow", "Install Packages (PKG)", nullptr));
bootGameAct->setText(QCoreApplication::translate("MainWindow", "Boot Game", nullptr)); bootGameAct->setText(QCoreApplication::translate("MainWindow", "Boot Game", nullptr));
#ifdef ENABLE_UPDATER #ifdef ENABLE_UPDATER
updaterAct->setText( updaterAct->setText(
@ -371,8 +359,6 @@ public:
aboutAct->setText(QCoreApplication::translate("MainWindow", "About shadPS4", nullptr)); aboutAct->setText(QCoreApplication::translate("MainWindow", "About shadPS4", nullptr));
configureAct->setText(QCoreApplication::translate("MainWindow", "Configure...", nullptr)); configureAct->setText(QCoreApplication::translate("MainWindow", "Configure...", nullptr));
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
bootInstallPkgAct->setToolTip(QCoreApplication::translate(
"MainWindow", "Install application from a .pkg file", nullptr));
#endif // QT_CONFIG(tooltip) #endif // QT_CONFIG(tooltip)
menuRecent->setTitle(QCoreApplication::translate("MainWindow", "Recent Games", nullptr)); menuRecent->setTitle(QCoreApplication::translate("MainWindow", "Recent Games", nullptr));
shadFolderAct->setText( shadFolderAct->setText(
@ -404,7 +390,6 @@ public:
QCoreApplication::translate("MainWindow", "Download Cheats/Patches", nullptr)); QCoreApplication::translate("MainWindow", "Download Cheats/Patches", nullptr));
dumpGameListAct->setText( dumpGameListAct->setText(
QCoreApplication::translate("MainWindow", "Dump Game List", nullptr)); QCoreApplication::translate("MainWindow", "Dump Game List", nullptr));
pkgViewerAct->setText(QCoreApplication::translate("MainWindow", "PKG Viewer", nullptr));
trophyViewerAct->setText( trophyViewerAct->setText(
QCoreApplication::translate("MainWindow", "Trophy Viewer", nullptr)); QCoreApplication::translate("MainWindow", "Trophy Viewer", nullptr));
mw_searchbar->setPlaceholderText( mw_searchbar->setPlaceholderText(
@ -433,4 +418,4 @@ public:
namespace Ui { namespace Ui {
class MainWindow : public Ui_MainWindow {}; class MainWindow : public Ui_MainWindow {};
} // namespace Ui } // namespace Ui

View File

@ -1,217 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "pkg_viewer.h"
PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent,
std::function<void(std::filesystem::path, int, int)> 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);
}

View File

@ -1,62 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QFileDialog>
#include <QMenuBar>
#include <QStatusBar>
#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<GameInfoClass> game_info_get, QWidget* parent,
std::function<void(std::filesystem::path, int, int)> 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<u8> pkg;
u64 pkgSize = 0;
std::unordered_map<std::string, std::string> map_strings;
std::unordered_map<std::string, u32> map_integers;
u32_be pkg_content_flag;
std::shared_ptr<GameInfoClass> m_game_info;
GameListUtils game_list_util;
// Status bar
QStatusBar* statusBar;
std::vector<std::pair<int, QString>> 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<std::string> dir_list_std;
QTreeWidget* treeWidget = nullptr;
};

View File

@ -817,7 +817,7 @@ void SettingsDialog::ResetInstallFolders() {
if (data.contains("GUI")) { if (data.contains("GUI")) {
const toml::value& gui = data.at("GUI"); const toml::value& gui = data.at("GUI");
const auto install_dir_array = const auto install_dir_array =
toml::find_or<std::vector<std::string>>(gui, "installDirs", {}); toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {});
std::vector<bool> install_dirs_enabled; std::vector<bool> install_dirs_enabled;
try { try {

View File

@ -1149,7 +1149,7 @@
</message> </message>
<message> <message>
<source>Deadzone Offset (def 0.50):</source> <source>Deadzone Offset (def 0.50):</source>
<translation/> <translation type="unfinished">Deadzone Offset (def 0.50):</translation>
</message> </message>
<message> <message>
<source>Speed Multiplier (def 1.0):</source> <source>Speed Multiplier (def 1.0):</source>
@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation>أبدأ اللعب</translation>
</message>
<message>
<source>Pause</source>
<translation>توقف مؤقت</translation>
</message>
<message>
<source>Stop</source>
<translation>إيقاف</translation>
</message>
<message>
<source>Restart</source>
<translation>إعادة تشغيل</translation>
</message>
<message>
<source>Full Screen</source>
<translation>وضع ملء الشاشة</translation>
</message>
<message>
<source>Controllers</source>
<translation>أذرعة التحكم</translation>
</message>
<message>
<source>Keyboard</source>
<translation>لوحة المفاتيح</translation>
</message>
<message>
<source>Refresh List</source>
<translation>تحديث القائمة</translation>
</message>
<message>
<source>Resume</source>
<translation>استئناف</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation>إظهار العلامات أسفل الأيقونات</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -882,10 +882,6 @@
<source>Error creating shortcut!</source> <source>Error creating shortcut!</source>
<translation>Error creating shortcut!</translation> <translation>Error creating shortcut!</translation>
</message> </message>
<message>
<source>Install PKG</source>
<translation>Install PKG</translation>
</message>
<message> <message>
<source>Game</source> <source>Game</source>
<translation>Game</translation> <translation>Game</translation>
@ -978,25 +974,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>InstallDirSelect</name>
<message>
<source>shadPS4 - Choose directory</source>
<translation>shadPS4 - Choose directory</translation>
</message>
<message>
<source>Select which directory you want to install to.</source>
<translation>Select which directory you want to install to.</translation>
</message>
<message>
<source>Install All Queued to Selected Folder</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete PKG File on Install</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>KBMSettings</name> <name>KBMSettings</name>
<message> <message>
@ -1214,10 +1191,6 @@
<source>Open/Add Elf Folder</source> <source>Open/Add Elf Folder</source>
<translation>Open/Add Elf Folder</translation> <translation>Open/Add Elf Folder</translation>
</message> </message>
<message>
<source>Install Packages (PKG)</source>
<translation>Install Packages (PKG)</translation>
</message>
<message> <message>
<source>Boot Game</source> <source>Boot Game</source>
<translation>Boot Game</translation> <translation>Boot Game</translation>
@ -1234,10 +1207,6 @@
<source>Configure...</source> <source>Configure...</source>
<translation>Configure...</translation> <translation>Configure...</translation>
</message> </message>
<message>
<source>Install application from a .pkg file</source>
<translation>Install application from a .pkg file</translation>
</message>
<message> <message>
<source>Recent Games</source> <source>Recent Games</source>
<translation>Recent Games</translation> <translation>Recent Games</translation>
@ -1314,10 +1283,6 @@
<source>No games found. Please add your games to your library first.</source> <source>No games found. Please add your games to your library first.</source>
<translation>No games found. Please add your games to your library first.</translation> <translation>No games found. Please add your games to your library first.</translation>
</message> </message>
<message>
<source>PKG Viewer</source>
<translation>PKG Viewer</translation>
</message>
<message> <message>
<source>Search...</source> <source>Search...</source>
<translation>Search...</translation> <translation>Search...</translation>
@ -1426,70 +1391,6 @@
<source>Only one file can be selected!</source> <source>Only one file can be selected!</source>
<translation>Only one file can be selected!</translation> <translation>Only one file can be selected!</translation>
</message> </message>
<message>
<source>PKG Extraction</source>
<translation>PKG Extraction</translation>
</message>
<message>
<source>Patch detected!</source>
<translation>Patch detected!</translation>
</message>
<message>
<source>PKG and Game versions match: </source>
<translation>PKG and Game versions match: </translation>
</message>
<message>
<source>Would you like to overwrite?</source>
<translation>Would you like to overwrite?</translation>
</message>
<message>
<source>PKG Version %1 is older than installed version: </source>
<translation>PKG Version %1 is older than installed version: </translation>
</message>
<message>
<source>Game is installed: </source>
<translation>Game is installed: </translation>
</message>
<message>
<source>Would you like to install Patch: </source>
<translation>Would you like to install Patch: </translation>
</message>
<message>
<source>DLC Installation</source>
<translation>DLC Installation</translation>
</message>
<message>
<source>Would you like to install DLC: %1?</source>
<translation>Would you like to install DLC: %1?</translation>
</message>
<message>
<source>DLC already installed:</source>
<translation>DLC already installed:</translation>
</message>
<message>
<source>Game already installed</source>
<translation>Game already installed</translation>
</message>
<message>
<source>PKG ERROR</source>
<translation>PKG ERROR</translation>
</message>
<message>
<source>Extracting PKG %1/%2</source>
<translation>Extracting PKG %1/%2</translation>
</message>
<message>
<source>Extraction Finished</source>
<translation>Extraction Finished</translation>
</message>
<message>
<source>Game successfully installed at %1</source>
<translation>Game successfully installed at %1</translation>
</message>
<message>
<source>File doesn&apos;t appear to be a valid PKG file</source>
<translation>File doesn&apos;t appear to be a valid PKG file</translation>
</message>
<message> <message>
<source>Run Game</source> <source>Run Game</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -1498,14 +1399,6 @@
<source>Eboot.bin file not found</source> <source>Eboot.bin file not found</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>PKG File (*.PKG *.pkg)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PKG is a patch or DLC, please install the game first!</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>Game is already running!</source> <source>Game is already running!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -1555,73 +1448,6 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>PKGViewer</name>
<message>
<source>Open Folder</source>
<translation>Open Folder</translation>
</message>
<message>
<source>PKG ERROR</source>
<translation type="unfinished">PKG ERROR</translation>
</message>
<message>
<source>Name</source>
<translation type="unfinished">Name</translation>
</message>
<message>
<source>Serial</source>
<translation type="unfinished">Serial</translation>
</message>
<message>
<source>Installed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Size</source>
<translation type="unfinished">Size</translation>
</message>
<message>
<source>Category</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Type</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>App Ver</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>FW</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Region</source>
<translation type="unfinished">Region</translation>
</message>
<message>
<source>Flags</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Path</source>
<translation type="unfinished">Path</translation>
</message>
<message>
<source>File</source>
<translation type="unfinished">File</translation>
</message>
<message>
<source>Unknown</source>
<translation type="unfinished">Unknown</translation>
</message>
<message>
<source>Package</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>SettingsDialog</name> <name>SettingsDialog</name>
<message> <message>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>ShadPS4</translation> <translation>ShadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -407,206 +407,206 @@
<name>ControlSettings</name> <name>ControlSettings</name>
<message> <message>
<source>Configure Controls</source> <source>Configure Controls</source>
<translation type="unfinished">Configure Controls</translation> <translation>Määritä Kontrollit</translation>
</message> </message>
<message> <message>
<source>D-Pad</source> <source>D-Pad</source>
<translation type="unfinished">D-Pad</translation> <translation>D-Pad</translation>
</message> </message>
<message> <message>
<source>Up</source> <source>Up</source>
<translation type="unfinished">Up</translation> <translation>Ylös</translation>
</message> </message>
<message> <message>
<source>Left</source> <source>Left</source>
<translation type="unfinished">Left</translation> <translation>Vasen</translation>
</message> </message>
<message> <message>
<source>Right</source> <source>Right</source>
<translation type="unfinished">Right</translation> <translation>Oikea</translation>
</message> </message>
<message> <message>
<source>Down</source> <source>Down</source>
<translation type="unfinished">Down</translation> <translation>Alas</translation>
</message> </message>
<message> <message>
<source>Left Stick Deadzone (def:2 max:127)</source> <source>Left Stick Deadzone (def:2 max:127)</source>
<translation type="unfinished">Left Stick Deadzone (def:2 max:127)</translation> <translation>Vasemman Analogin Deadzone (oletus:2 max:127)</translation>
</message> </message>
<message> <message>
<source>Left Deadzone</source> <source>Left Deadzone</source>
<translation type="unfinished">Left Deadzone</translation> <translation>Vasen Deadzone</translation>
</message> </message>
<message> <message>
<source>Left Stick</source> <source>Left Stick</source>
<translation type="unfinished">Left Stick</translation> <translation>Vasen Analogi</translation>
</message> </message>
<message> <message>
<source>Config Selection</source> <source>Config Selection</source>
<translation type="unfinished">Config Selection</translation> <translation>Asetusten Valinta</translation>
</message> </message>
<message> <message>
<source>Common Config</source> <source>Common Config</source>
<translation type="unfinished">Common Config</translation> <translation>Yleinen Asetus</translation>
</message> </message>
<message> <message>
<source>Use per-game configs</source> <source>Use per-game configs</source>
<translation type="unfinished">Use per-game configs</translation> <translation>Käytä pelikohtaisia asetuksia</translation>
</message> </message>
<message> <message>
<source>L1 / LB</source> <source>L1 / LB</source>
<translation type="unfinished">L1 / LB</translation> <translation>L1 / LB</translation>
</message> </message>
<message> <message>
<source>L2 / LT</source> <source>L2 / LT</source>
<translation type="unfinished">L2 / LT</translation> <translation>L2 / LT</translation>
</message> </message>
<message> <message>
<source>Back</source> <source>Back</source>
<translation type="unfinished">Back</translation> <translation>Back</translation>
</message> </message>
<message> <message>
<source>R1 / RB</source> <source>R1 / RB</source>
<translation type="unfinished">R1 / RB</translation> <translation>R1 / RB</translation>
</message> </message>
<message> <message>
<source>R2 / RT</source> <source>R2 / RT</source>
<translation type="unfinished">R2 / RT</translation> <translation>R2 / RT</translation>
</message> </message>
<message> <message>
<source>L3</source> <source>L3</source>
<translation type="unfinished">L3</translation> <translation>L3</translation>
</message> </message>
<message> <message>
<source>Options / Start</source> <source>Options / Start</source>
<translation type="unfinished">Options / Start</translation> <translation>Options / Start</translation>
</message> </message>
<message> <message>
<source>R3</source> <source>R3</source>
<translation type="unfinished">R3</translation> <translation>R3</translation>
</message> </message>
<message> <message>
<source>Face Buttons</source> <source>Face Buttons</source>
<translation type="unfinished">Face Buttons</translation> <translation>Etunäppäimet</translation>
</message> </message>
<message> <message>
<source>Triangle / Y</source> <source>Triangle / Y</source>
<translation type="unfinished">Triangle / Y</translation> <translation>Kolmio / Y</translation>
</message> </message>
<message> <message>
<source>Square / X</source> <source>Square / X</source>
<translation type="unfinished">Square / X</translation> <translation>Neliö / X</translation>
</message> </message>
<message> <message>
<source>Circle / B</source> <source>Circle / B</source>
<translation type="unfinished">Circle / B</translation> <translation>Ympyrä / B</translation>
</message> </message>
<message> <message>
<source>Cross / A</source> <source>Cross / A</source>
<translation type="unfinished">Cross / A</translation> <translation>Rasti / A</translation>
</message> </message>
<message> <message>
<source>Right Stick Deadzone (def:2, max:127)</source> <source>Right Stick Deadzone (def:2, max:127)</source>
<translation type="unfinished">Right Stick Deadzone (def:2, max:127)</translation> <translation>Oikean Analogin Deadzone (oletus:2 max:127)</translation>
</message> </message>
<message> <message>
<source>Right Deadzone</source> <source>Right Deadzone</source>
<translation type="unfinished">Right Deadzone</translation> <translation>Oikea Deadzone</translation>
</message> </message>
<message> <message>
<source>Right Stick</source> <source>Right Stick</source>
<translation type="unfinished">Right Stick</translation> <translation>Oikea Analogi</translation>
</message> </message>
<message> <message>
<source>Color Adjustment</source> <source>Color Adjustment</source>
<translation type="unfinished">Color Adjustment</translation> <translation>Värinhallinta</translation>
</message> </message>
<message> <message>
<source>R:</source> <source>R:</source>
<translation type="unfinished">R:</translation> <translation>R:</translation>
</message> </message>
<message> <message>
<source>G:</source> <source>G:</source>
<translation type="unfinished">G:</translation> <translation>G:</translation>
</message> </message>
<message> <message>
<source>B:</source> <source>B:</source>
<translation type="unfinished">B:</translation> <translation>B:</translation>
</message> </message>
<message> <message>
<source>Override Lightbar Color</source> <source>Override Lightbar Color</source>
<translation type="unfinished">Override Lightbar Color</translation> <translation>Pakota Ohjaimen Valopalkin Väri</translation>
</message> </message>
<message> <message>
<source>Override Color</source> <source>Override Color</source>
<translation type="unfinished">Override Color</translation> <translation>Pakotettava Väri</translation>
</message> </message>
<message> <message>
<source>Unable to Save</source> <source>Unable to Save</source>
<translation type="unfinished">Unable to Save</translation> <translation>Tallentaminen Epäonnistui</translation>
</message> </message>
<message> <message>
<source>Cannot bind axis values more than once</source> <source>Cannot bind axis values more than once</source>
<translation type="unfinished">Cannot bind axis values more than once</translation> <translation>Akseliarvoja ei voi määrittää kertaa useammin</translation>
</message> </message>
<message> <message>
<source>Save</source> <source>Save</source>
<translation type="unfinished">Save</translation> <translation>Tallenna</translation>
</message> </message>
<message> <message>
<source>Apply</source> <source>Apply</source>
<translation type="unfinished">Apply</translation> <translation>Ota Käyttöön</translation>
</message> </message>
<message> <message>
<source>Restore Defaults</source> <source>Restore Defaults</source>
<translation type="unfinished">Restore Defaults</translation> <translation>Palauta Oletukset</translation>
</message> </message>
<message> <message>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished">Cancel</translation> <translation>Peruuta</translation>
</message> </message>
</context> </context>
<context> <context>
<name>EditorDialog</name> <name>EditorDialog</name>
<message> <message>
<source>Edit Keyboard + Mouse and Controller input bindings</source> <source>Edit Keyboard + Mouse and Controller input bindings</source>
<translation type="unfinished">Edit Keyboard + Mouse and Controller input bindings</translation> <translation>Muokkaa Näppäimistön + Hiiren ja Ohjaimen näppäinasetuksia</translation>
</message> </message>
<message> <message>
<source>Use Per-Game configs</source> <source>Use Per-Game configs</source>
<translation type="unfinished">Use Per-Game configs</translation> <translation>Käytä Pelikohtaisia Asetuksia</translation>
</message> </message>
<message> <message>
<source>Error</source> <source>Error</source>
<translation type="unfinished">Error</translation> <translation>Virhe</translation>
</message> </message>
<message> <message>
<source>Could not open the file for reading</source> <source>Could not open the file for reading</source>
<translation type="unfinished">Could not open the file for reading</translation> <translation>Tiedostoa ei voitu avata luettavaksi</translation>
</message> </message>
<message> <message>
<source>Could not open the file for writing</source> <source>Could not open the file for writing</source>
<translation type="unfinished">Could not open the file for writing</translation> <translation>Tiedostoa ei voitu avata kirjoitettavaksi</translation>
</message> </message>
<message> <message>
<source>Save Changes</source> <source>Save Changes</source>
<translation type="unfinished">Save Changes</translation> <translation>Tallenna Muutokset</translation>
</message> </message>
<message> <message>
<source>Do you want to save changes?</source> <source>Do you want to save changes?</source>
<translation type="unfinished">Do you want to save changes?</translation> <translation>Haluatko tallentaa muutokset?</translation>
</message> </message>
<message> <message>
<source>Help</source> <source>Help</source>
<translation type="unfinished">Help</translation> <translation>Tietoa</translation>
</message> </message>
<message> <message>
<source>Do you want to reset your custom default config to the original default config?</source> <source>Do you want to reset your custom default config to the original default config?</source>
<translation type="unfinished">Do you want to reset your custom default config to the original default config?</translation> <translation>Haluatko nollata oletusasetuksiin tekemäsi muutokset?</translation>
</message> </message>
<message> <message>
<source>Do you want to reset this config to your custom default config?</source> <source>Do you want to reset this config to your custom default config?</source>
<translation type="unfinished">Do you want to reset this config to your custom default config?</translation> <translation>Haluato palauttaa nämä asetukset takaisin määrittämiisi oletuksiin?</translation>
</message> </message>
<message> <message>
<source>Reset to Default</source> <source>Reset to Default</source>
@ -1077,35 +1077,35 @@
</message> </message>
<message> <message>
<source>L3</source> <source>L3</source>
<translation type="unfinished">L3</translation> <translation>L3</translation>
</message> </message>
<message> <message>
<source>Touchpad Click</source> <source>Touchpad Click</source>
<translation type="unfinished">Touchpad Click</translation> <translation>Kosketuslevyn Klikkaus</translation>
</message> </message>
<message> <message>
<source>Mouse to Joystick</source> <source>Mouse to Joystick</source>
<translation type="unfinished">Mouse to Joystick</translation> <translation>Hiiri Joystickinä</translation>
</message> </message>
<message> <message>
<source>*press F7 ingame to activate</source> <source>*press F7 ingame to activate</source>
<translation type="unfinished">*press F7 ingame to activate</translation> <translation>*paina F7 pelissä aktivoidaksesi</translation>
</message> </message>
<message> <message>
<source>R3</source> <source>R3</source>
<translation type="unfinished">R3</translation> <translation>R3</translation>
</message> </message>
<message> <message>
<source>Options</source> <source>Options</source>
<translation type="unfinished">Options</translation> <translation>Options</translation>
</message> </message>
<message> <message>
<source>Mouse Movement Parameters</source> <source>Mouse Movement Parameters</source>
<translation type="unfinished">Mouse Movement Parameters</translation> <translation>Hiiren Liikkeen Parametrit</translation>
</message> </message>
<message> <message>
<source>note: click Help Button/Special Keybindings for more information</source> <source>note: click Help Button/Special Keybindings for more information</source>
<translation type="unfinished">note: click Help Button/Special Keybindings for more information</translation> <translation>huomio: klikkaa apunappia/näppäintä saadaksesi lisää tietoa</translation>
</message> </message>
<message> <message>
<source>Face Buttons</source> <source>Face Buttons</source>
@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1308,11 +1308,11 @@
</message> </message>
<message> <message>
<source>Trophy Viewer</source> <source>Trophy Viewer</source>
<translation type="unfinished">Trophy Viewer</translation> <translation>Visualizzatore Trofei</translation>
</message> </message>
<message> <message>
<source>No games found. Please add your games to your library first.</source> <source>No games found. Please add your games to your library first.</source>
<translation type="unfinished">No games found. Please add your games to your library first.</translation> <translation>Nessun gioco trovato. Aggiungi prima i tuoi giochi alla tua libreria.</translation>
</message> </message>
<message> <message>
<source>PKG Viewer</source> <source>PKG Viewer</source>
@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>
@ -2201,7 +2241,7 @@
</message> </message>
<message> <message>
<source>Select Game:</source> <source>Select Game:</source>
<translation type="unfinished">Select Game:</translation> <translation>Seleziona Gioco:</translation>
</message> </message>
<message> <message>
<source>Progress</source> <source>Progress</source>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -912,7 +912,7 @@
</message> </message>
<message> <message>
<source>Are you sure you want to delete %1&apos;s %2 directory?</source> <source>Are you sure you want to delete %1&apos;s %2 directory?</source>
<translation>Tem certeza de que deseja excluir o diretório %2 de %1?</translation> <translation>Tem certeza de que deseja excluir o diretório do %2 %1?</translation>
</message> </message>
<message> <message>
<source>Open Update Folder</source> <source>Open Update Folder</source>
@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation>Jogar</translation>
</message>
<message>
<source>Pause</source>
<translation>Pausar</translation>
</message>
<message>
<source>Stop</source>
<translation>Parar</translation>
</message>
<message>
<source>Restart</source>
<translation>Reiniciar</translation>
</message>
<message>
<source>Full Screen</source>
<translation>Tela Cheia</translation>
</message>
<message>
<source>Controllers</source>
<translation>Controles</translation>
</message>
<message>
<source>Keyboard</source>
<translation>Teclado</translation>
</message>
<message>
<source>Refresh List</source>
<translation>Atualizar Lista</translation>
</message>
<message>
<source>Resume</source>
<translation>Continuar</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation>Mostrar Rótulos Sob Ícones</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation>Играть</translation>
</message>
<message>
<source>Pause</source>
<translation>Пауза</translation>
</message>
<message>
<source>Stop</source>
<translation>Остановить</translation>
</message>
<message>
<source>Restart</source>
<translation>Перезапустить</translation>
</message>
<message>
<source>Full Screen</source>
<translation>Полный экран</translation>
</message>
<message>
<source>Controllers</source>
<translation>Контроллеры</translation>
</message>
<message>
<source>Keyboard</source>
<translation>Клавиатура</translation>
</message>
<message>
<source>Refresh List</source>
<translation>Обновить список</translation>
</message>
<message>
<source>Resume</source>
<translation>Продолжить</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation>Показывать метки под значками</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation>Başlat</translation>
</message>
<message>
<source>Pause</source>
<translation>Duraklat</translation>
</message>
<message>
<source>Stop</source>
<translation>Durdur</translation>
</message>
<message>
<source>Restart</source>
<translation>Yeniden Başlat</translation>
</message>
<message>
<source>Full Screen</source>
<translation>Tam Ekran</translation>
</message>
<message>
<source>Controllers</source>
<translation>Kontrolcüler</translation>
</message>
<message>
<source>Keyboard</source>
<translation>Klavye</translation>
</message>
<message>
<source>Refresh List</source>
<translation>Listeyi Yenile</translation>
</message>
<message>
<source>Resume</source>
<translation>Devam Et</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation>Simgelerin Altında Etiketleri Göster</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation>shadPS4</translation> <translation>shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation></translation>
</message>
<message>
<source>Pause</source>
<translation></translation>
</message>
<message>
<source>Stop</source>
<translation></translation>
</message>
<message>
<source>Restart</source>
<translation></translation>
</message>
<message>
<source>Full Screen</source>
<translation></translation>
</message>
<message>
<source>Controllers</source>
<translation></translation>
</message>
<message>
<source>Keyboard</source>
<translation></translation>
</message>
<message>
<source>Refresh List</source>
<translation></translation>
</message>
<message>
<source>Resume</source>
<translation></translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>

View File

@ -1514,6 +1514,46 @@
<source>shadPS4</source> <source>shadPS4</source>
<translation type="unfinished">shadPS4</translation> <translation type="unfinished">shadPS4</translation>
</message> </message>
<message>
<source>Play</source>
<translation type="unfinished">Play</translation>
</message>
<message>
<source>Pause</source>
<translation type="unfinished">Pause</translation>
</message>
<message>
<source>Stop</source>
<translation type="unfinished">Stop</translation>
</message>
<message>
<source>Restart</source>
<translation type="unfinished">Restart</translation>
</message>
<message>
<source>Full Screen</source>
<translation type="unfinished">Full Screen</translation>
</message>
<message>
<source>Controllers</source>
<translation type="unfinished">Controllers</translation>
</message>
<message>
<source>Keyboard</source>
<translation type="unfinished">Keyboard</translation>
</message>
<message>
<source>Refresh List</source>
<translation type="unfinished">Refresh List</translation>
</message>
<message>
<source>Resume</source>
<translation type="unfinished">Resume</translation>
</message>
<message>
<source>Show Labels Under Icons</source>
<translation type="unfinished">Show Labels Under Icons</translation>
</message>
</context> </context>
<context> <context>
<name>PKGViewer</name> <name>PKGViewer</name>