Seregon/PkgToolBox

Toolbox for analyzing and editing pkg application files for psp,ps3, ps4 and ps5, includes the most useful functions you might need.

Python/57.3 KB/No license
GraphicUserInterface/widgets/pfs_info_tab.py
1"""
2PFS Info tab widget for inspecting PS4 PKG PFS structure via shadPKG
3"""
4from PyQt5.QtWidgets import (
5 QVBoxLayout,
6 QHBoxLayout,
7 QLineEdit,
8 QPushButton,
9 QTextEdit,
10 QMessageBox,
11 QGroupBox,
12 QCheckBox,
13)
14from PyQt5.QtCore import Qt, QObject, QThread, pyqtSignal
15from .base_tab import BaseTab
16from packages import PackagePS4
17 
18 
19class 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