mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-04-23 21:14:45 +00:00
misc: Remove dead code. (#2702)
This commit is contained in:
parent
be22674f8c
commit
31e1d4f839
13 changed files with 2 additions and 709 deletions
|
@ -678,8 +678,6 @@ set(CORE src/core/aerolib/stubs.cpp
|
|||
src/core/file_format/trp.h
|
||||
src/core/file_sys/fs.cpp
|
||||
src/core/file_sys/fs.h
|
||||
src/core/loader.cpp
|
||||
src/core/loader.h
|
||||
src/core/loader/dwarf.cpp
|
||||
src/core/loader/dwarf.h
|
||||
src/core/loader/elf.cpp
|
||||
|
@ -969,8 +967,6 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
|
|||
src/qt_gui/game_grid_frame.h
|
||||
src/qt_gui/game_install_dialog.cpp
|
||||
src/qt_gui/game_install_dialog.h
|
||||
src/qt_gui/install_dir_select.cpp
|
||||
src/qt_gui/install_dir_select.h
|
||||
src/qt_gui/trophy_viewer.cpp
|
||||
src/qt_gui/trophy_viewer.h
|
||||
src/qt_gui/elf_viewer.cpp
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 658 KiB |
|
@ -13,7 +13,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||
- [**RAM**](#ram)
|
||||
- [**OS**](#os)
|
||||
- [**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)
|
||||
|
||||
## 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.
|
||||
|
||||
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.
|
||||
|
||||
## 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">
|
||||
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.
|
||||
|
||||
## Configure the emulator
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ Now run the emulator. If Qt was enabled at configure time:
|
|||
./build/shadps4
|
||||
```
|
||||
|
||||
Otherwise, specify the path to your PKG's boot file:
|
||||
Otherwise, specify the path to your game's boot file:
|
||||
|
||||
```bash
|
||||
./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin
|
||||
|
|
|
@ -98,7 +98,6 @@ u32 m_slider_pos_grid = 0;
|
|||
u32 m_table_mode = 0;
|
||||
u32 m_window_size_W = 1280;
|
||||
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_recent_files;
|
||||
std::string emulator_language = "en_US";
|
||||
|
@ -601,11 +600,6 @@ void setMainWindowHeight(u32 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) {
|
||||
m_elf_viewer.resize(elfList.size());
|
||||
m_elf_viewer = elfList;
|
||||
|
@ -709,10 +703,6 @@ u32 getMainWindowHeight() {
|
|||
return m_window_size_H;
|
||||
}
|
||||
|
||||
std::vector<std::string> getPkgViewer() {
|
||||
return m_pkg_viewer;
|
||||
}
|
||||
|
||||
std::vector<std::string> getElfViewer() {
|
||||
return m_elf_viewer;
|
||||
}
|
||||
|
@ -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_w = toml::find_or<int>(gui, "geometry_w", 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_recent_files = toml::find_or<std::vector<std::string>>(gui, "recentFiles", {});
|
||||
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_w"] = main_window_geometry_w;
|
||||
data["GUI"]["geometry_h"] = main_window_geometry_h;
|
||||
data["GUI"]["pkgDirs"] = m_pkg_viewer;
|
||||
data["GUI"]["elfDirs"] = m_elf_viewer;
|
||||
data["GUI"]["recentFiles"] = m_recent_files;
|
||||
|
||||
|
|
|
@ -154,7 +154,6 @@ void setSliderPositionGrid(u32 pos);
|
|||
void setTableMode(u32 mode);
|
||||
void setMainWindowWidth(u32 width);
|
||||
void setMainWindowHeight(u32 height);
|
||||
void setPkgViewer(const std::vector<std::string>& pkgList);
|
||||
void setElfViewer(const std::vector<std::string>& elfList);
|
||||
void setRecentFiles(const std::vector<std::string>& recentFiles);
|
||||
void setEmulatorLanguage(std::string language);
|
||||
|
@ -174,7 +173,6 @@ u32 getSliderPositionGrid();
|
|||
u32 getTableMode();
|
||||
u32 getMainWindowWidth();
|
||||
u32 getMainWindowHeight();
|
||||
std::vector<std::string> getPkgViewer();
|
||||
std::vector<std::string> getElfViewer();
|
||||
std::vector<std::string> getRecentFiles();
|
||||
std::string getEmulatorLanguage();
|
||||
|
|
|
@ -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(§orMap[i], pfsc.data() + pfsChdr.block_offsets + i * 8, 8);
|
||||
}
|
||||
}
|
||||
|
||||
u32 ent_size = 0;
|
||||
u32 ndinode = 0;
|
||||
int ndinode_counter = 0;
|
||||
bool dinode_reached = false;
|
||||
bool uroot_reached = false;
|
||||
std::vector<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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -7,7 +7,6 @@
|
|||
#include <QDesktopServices>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QTreeWidget>
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
#include <qt_gui/background_music_player.h>
|
||||
|
@ -599,30 +598,6 @@ public:
|
|||
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:
|
||||
bool convertPngToIco(const QString& pngFilePath, const QString& icoFilePath) {
|
||||
// Load the PNG image
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -20,9 +20,7 @@
|
|||
#include "common/string_util.h"
|
||||
#include "common/version.h"
|
||||
#include "control_settings.h"
|
||||
#include "core/loader.h"
|
||||
#include "game_install_dialog.h"
|
||||
#include "install_dir_select.h"
|
||||
#include "kbm_gui.h"
|
||||
#include "main_window.h"
|
||||
#include "settings_dialog.h"
|
||||
|
|
Loading…
Add table
Reference in a new issue