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
main.py
PkgToolBox / main.py
1import struct
2import sys
3import os
4import logging
5import ctypes
6import argparse
7import io
8import json
9from contextlib import redirect_stdout
10 
11# Aggiungi la directory root al path di Python
12sys.path.append(os.path.dirname(os.path.abspath(__file__)))
13 
14# Import dei moduli
15from GraphicUserInterface.main_window import MainWindow
16from packages import PackagePS4, PackagePS5, PackagePS3
17from Utilities.Trophy import Archiver, TrophyFile, TRPCreator, TRPReader
18from file_operations import extract_file, inject_file, modify_file_header
19from Utilities import Logger, SettingsManager
20from tools.repack import Repack
21from tools.PS5_Game_Info import PS5GameInfo
22from PyQt5.QtWidgets import QApplication
23 
24# Configure logging
25logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
26 
27def check_settings_file_presence():
28 """Check and create necessary directories and settings file"""
29 temp_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), "PS4PKGToolTemp")
30 if not os.path.exists(temp_directory):
31 os.makedirs(temp_directory)
32 Logger.log_information("Creating PS4PKGToolTemp directory...")
33
34 settings_file_path = os.path.join(temp_directory, "settings.json")
35 if not os.path.exists(settings_file_path) or os.path.getsize(settings_file_path) == 0:
36 create_default_settings(settings_file_path)
37
38 return temp_directory, settings_file_path
39 
40def create_default_settings(settings_file_path):
41 """Create default settings file"""
42 default_settings = {
43 "theme": "Light",
44 "night_mode": False,
45 "font": "Arial",
46 "font_size": 12,
47 "bg_color": "#ffffff",
48 "text_color": "#000000",
49 "accent_color": "#3498db",
50 "auto_expand": True,
51 "show_hidden": False,
52 "confirm_exit": True,
53 "output_path": "",
54 "temp_path": "",
55 "pkg_directories": [],
56 "scan_recursive": False,
57 "play_bgm": False,
58 "show_directory_settings_at_startup": True,
59 "auto_sort_row": False,
60 "local_server_ip": "",
61 "ps4_ip": "",
62 "nodejs_installed": False,
63 "http_server_installed": False,
64 "official_update_download_directory": "",
65 "pkg_color_label": False,
66 "game_pkg_forecolor": 0xDDDDDD,
67 "patch_pkg_forecolor": 0xDDDDDD,
68 "addon_pkg_forecolor": 0xDDDDDD,
69 "app_pkg_forecolor": 0xDDDDDD,
70 "game_pkg_backcolor": 0x333333,
71 "patch_pkg_backcolor": 0x333333,
72 "addon_pkg_backcolor": 0x333333,
73 "app_pkg_backcolor": 0x333333,
74 "rename_custom_format": "",
75 "ps5bc_json_download_date": "",
76 "psvr_neo_ps5bc_check": False,
77 "pkg_titleId_column": True,
78 "pkg_contentId_column": True,
79 "pkg_region_column": True,
80 "pkg_minimum_firmware_column": True,
81 "pkg_version_column": True,
82 "pkg_type_column": True,
83 "pkg_category_column": True,
84 "pkg_size_column": True,
85 "pkg_location_column": True,
86 "pkg_backport_column": True
87 }
88
89 with open(settings_file_path, 'w') as f:
90 json.dump(default_settings, f, indent=4)
91 Logger.log_information("Default settings created.")
92 
93def execute_command(cmd, pkg, file, out, update_callback_info):
94 """Execute PKG related commands"""
95 logging.debug(f"execute_command called with cmd={cmd}, pkg={pkg}, file={file}, out={out}")
96 if not cmd or not pkg:
97 raise ValueError("The 'Command' and 'PKG' fields are required.")
98 
99 try:
100 # Determine package type and create appropriate instance
101 with open(pkg, "rb") as fp:
102 magic = struct.unpack(">I", fp.read(4))[0]
103 if magic == PackagePS4.MAGIC_PS4:
104 target = PackagePS4(pkg)
105 elif magic == PackagePS5.MAGIC_PS5:
106 target = PackagePS5(pkg)
107 elif magic == PackagePS3.MAGIC_PS3:
108 target = PackagePS3(pkg)
109 else:
110 raise ValueError(f"Unknown PKG format: {magic:08X}")
111 
112 # Execute requested command
113 if cmd == "info":
114 return get_pkg_info(target, update_callback_info)
115 elif cmd == "extract":
116 return extract_pkg_file(target, file, out, update_callback_info)
117 elif cmd == "dump":
118 return dump_pkg(target, out, update_callback_info)
119 elif cmd == "inject":
120 return inject_pkg_file(target, file, out)
121 elif cmd == "modify":
122 return modify_pkg_header(target, file, out)
123 else:
124 raise ValueError(f"Unknown command: {cmd}")
125 
126 except Exception as e:
127 logging.error(f"Error executing command: {str(e)}")
128 raise
129 
130def get_pkg_info(package, callback):
131 """Get PKG information"""
132 f = io.StringIO()
133 with redirect_stdout(f):
134 package.info()
135 info_output = f.getvalue()
136
137 if not info_output:
138 raise ValueError("No information found in the PKG file.")
139
140 info_dict = {}
141 for line in info_output.split('\n'):
142 if ':' in line:
143 key, value = line.split(':', 1)
144 info_dict[key.strip()] = value.strip()
145
146 callback(info_dict)
147 return info_output
148 
149def extract_pkg_file(package, file_path, output_path, callback):
150 """Extract file from PKG"""
151 file_info = package.get_file_info(file_path)
152 extract_file(package.original_file, file_info, output_path, callback)
153 return f"File extracted: {file_path}"
154 
155def dump_pkg(package, output_path, callback):
156 """Dump PKG contents"""
157 try:
158 result = package.dump(output_path, callback)
159 return result
160 except Exception as e:
161 raise ValueError(f"Error during dump: {str(e)}")
162 
163def inject_pkg_file(package, file_path, input_path):
164 """Inject file into PKG"""
165 file_info = package.get_file_info(file_path)
166 injected_size = inject_file(package.original_file, file_info, input_path)
167 return f"Injected {injected_size} bytes"
168 
169def modify_pkg_header(package, offset, new_data):
170 """Modify PKG header"""
171 modified_size = modify_file_header(package.original_file, int(offset, 16), new_data.encode())
172 return f"Modified {modified_size} bytes"
173 
174def is_admin():
175 """Check if running with admin privileges"""
176 try:
177 return ctypes.windll.shell32.IsUserAnAdmin()
178 except:
179 return False
180 
181def main():
182 """Main application entry point"""
183 # Initialize application
184 app = QApplication(sys.argv)
185 app.setStyle('Fusion')
186
187 # Setup directories and settings
188 temp_directory, settings_file_path = check_settings_file_presence()
189
190 # Create and show main window
191 window = MainWindow(temp_directory)
192 window.show()
193
194 # Start application
195 sys.exit(app.exec_())
196 
197if __name__ == "__main__":
198 main()