Toolbox for analyzing and editing pkg application files for psp,ps3, ps4 and ps5, includes the most useful functions you might need.
| 1 | """ |
| 2 | PFS Info tab widget for inspecting PS4 PKG PFS structure via shadPKG |
| 3 | """ |
| 4 | from PyQt5.QtWidgets import ( |
| 5 | QVBoxLayout, |
| 6 | QHBoxLayout, |
| 7 | QLineEdit, |
| 8 | QPushButton, |
| 9 | QTextEdit, |
| 10 | QMessageBox, |
| 11 | QGroupBox, |
| 12 | QCheckBox, |
| 13 | ) |
| 14 | from PyQt5.QtCore import Qt, QObject, QThread, pyqtSignal |
| 15 | from .base_tab import BaseTab |
| 16 | from packages import PackagePS4 |
| 17 | |
| 18 | |
| 19 | class PfsInfoTab(BaseTab): |
| 20 | """Tab to run shadPKG pfs-info on the currently loaded PKG""" |
| 21 | |
| 22 | def setup_ui(self): |
| 23 | # Controls group |
| 24 | controls_group = QGroupBox("PFS Info Controls") |
| 25 | controls_layout = QVBoxLayout() |
| 26 | |
| 27 | row = QHBoxLayout() |
| 28 | self.run_btn = QPushButton("Run PFS Info") |
| 29 | self.run_btn.clicked.connect(self.run_pfs_info) |
| 30 | self.json_chk = QCheckBox("JSON output") |
| 31 | row.addWidget(self.run_btn) |
| 32 | row.addWidget(self.json_chk) |
| 33 | row.addStretch(1) |
| 34 | controls_layout.addLayout(row) |
| 35 | controls_group.setLayout(controls_layout) |
| 36 | |
| 37 | # Output group |
| 38 | output_group = QGroupBox("PFS Info Output") |
| 39 | output_layout = QVBoxLayout() |
| 40 | self.output_view = QTextEdit() |
| 41 | self.output_view.setReadOnly(True) |
| 42 | self.output_view.setMinimumHeight(360) |
| 43 | self.output_view.setPlaceholderText("Click 'Run PFS Info' to analyze the loaded PS4 PKG") |
| 44 | output_layout.addWidget(self.output_view) |
| 45 | output_group.setLayout(output_layout) |
| 46 | |
| 47 | # Assemble |
| 48 | self.layout.addWidget(controls_group) |
| 49 | self.layout.addWidget(output_group) |
| 50 | self.layout.addStretch(1) |
| 51 | |
| 52 | def run_pfs_info(self): |
| 53 | package = self.get_package() |
| 54 | if not package: |
| 55 | QMessageBox.warning(self, "PFS Info", "Please load a PKG file first") |
| 56 | return |
| 57 | if not isinstance(package, PackagePS4): |
| 58 | QMessageBox.warning(self, "PFS Info", "PFS Info is only available for PS4 PKG") |
| 59 | return |
| 60 | |
| 61 | as_json = self.json_chk.isChecked() |
| 62 | self.run_btn.setEnabled(False) |
| 63 | self.output_view.clear() |
| 64 | self.output_view.append("[+] Running shadPKG pfs-info{}...\n".format(" --json" if as_json else "")) |
| 65 | |
| 66 | class Worker(QObject): |
| 67 | finished = pyqtSignal(str) |
| 68 | failed = pyqtSignal(str) |
| 69 | |
| 70 | def __init__(self, pkg, json_flag): |
| 71 | super().__init__() |
| 72 | self._pkg = pkg |
| 73 | self._json = json_flag |
| 74 | |
| 75 | def run(self): |
| 76 | try: |
| 77 | out = self._pkg.get_pfs_info(as_json=self._json) |
| 78 | self.finished.emit(out) |
| 79 | except Exception as e: |
| 80 | self.failed.emit(str(e)) |
| 81 | |
| 82 | try: |
| 83 | self._thr = QThread(self) |
| 84 | self._wrk = Worker(package, as_json) |
| 85 | self._wrk.moveToThread(self._thr) |
| 86 | self._thr.started.connect(self._wrk.run) |
| 87 | |
| 88 | def _done(text: str): |
| 89 | try: |
| 90 | self.output_view.clear() |
| 91 | self.output_view.append(text or "<no output>") |
| 92 | finally: |
| 93 | self._thr.quit() |
| 94 | self.run_btn.setEnabled(True) |
| 95 | |
| 96 | def _fail(err: str): |
| 97 | try: |
| 98 | QMessageBox.critical(self, "PFS Info", f"Failed: {err}") |
| 99 | finally: |
| 100 | self._thr.quit() |
| 101 | self.run_btn.setEnabled(True) |
| 102 | |
| 103 | self._wrk.finished.connect(_done) |
| 104 | self._wrk.failed.connect(_fail) |
| 105 | self._thr.finished.connect(self._thr.deleteLater) |
| 106 | self._thr.start() |
| 107 | except Exception as e: |
| 108 | self.run_btn.setEnabled(True) |
| 109 | QMessageBox.critical(self, "PFS Info", f"Failed to start pfs-info: {e}") |
| 110 |