1+ #pragma once
2+ #include < windows.h>
3+ #include < vector>
4+ #include < cstdint>
5+ #include < algorithm>
6+ #include < string>
7+ #include " PS3_PKG_Crypto.h"
8+
9+ // ---------------------------------------------------------------------
10+ // PS3 PKG constants
11+ // ---------------------------------------------------------------------
12+ #define PKG_MAGIC_PS3 0x7F504B47 // PS3 PKG
13+ #define PS3_SELF 0x53434500
14+
15+ enum PKG_TYPE {
16+ PKG_TYPE_PS3 = 0x0001 ,
17+ PKG_TYPE_PSP = 0x0002 ,
18+ PKG_TYPE_PSV = 0x0003 ,
19+ PKG_TYPE_PSM = 0x0004
20+ };
21+
22+ enum PKG_ENTRY_TYPE {
23+ ENTRY_TYPE_NPDRM = 0x1 ,
24+ ENTRY_TYPE_NPDRMEDAT = 0x3 ,
25+ ENTRY_TYPE_REGULAR = 0x4 ,
26+ ENTRY_TYPE_FOLDER = 0x5 ,
27+ };
28+
29+ // ---------------------------------------------------------------------
30+ // PS3 PKG header (packed)
31+ // ---------------------------------------------------------------------
32+ #pragma pack(push, 1)
33+ struct PKG_HEADER_PS3 {
34+ uint32_t magic; // 0x00
35+ uint16_t pkg_revision; // 0x04
36+ uint16_t pkg_type; // 0x06
37+ uint32_t pkg_metadata_offset; // 0x08
38+ uint32_t pkg_metadata_count; // 0x0C
39+ uint32_t pkg_metadata_size; // 0x10
40+ uint32_t item_count; // 0x14
41+ uint64_t total_size; // 0x18
42+ uint64_t data_offset; // 0x20
43+ uint64_t data_size; // 0x28
44+ char content_id[0x30 ]; // 0x30
45+ uint8_t digest[0x10 ]; // 0x60
46+ uint8_t pkg_data_riv[0x10 ]; // 0x70
47+ };
48+
49+ struct PKG_FILE_ENTRY_PS3 {
50+ uint32_t name_offset;
51+ uint32_t name_size;
52+ uint64_t data_offset;
53+ uint64_t data_size;
54+ uint32_t flags;
55+ uint32_t padding;
56+ };
57+ #pragma pack(pop)
58+
59+ // ---------------------------------------------------------------------
60+ // PS3 PKG Handler Class
61+ // ---------------------------------------------------------------------
62+ class PS3PKGHandler {
63+ private:
64+ PKG_HEADER_PS3 header = {};
65+ bool isEncrypted = false ;
66+
67+ uint32_t SwapEndian32 (uint32_t v) { return _byteswap_ulong (v); }
68+ uint64_t SwapEndian64 (uint64_t v) { return _byteswap_uint64 (v); }
69+ uint16_t SwapEndian16 (uint16_t v) { return _byteswap_ushort (v); }
70+
71+ void DecryptData (uint8_t * data, size_t size, uint64_t relativeOffset) {
72+ if (!isEncrypted || size == 0 ) return ;
73+ try {
74+ PS3Crypto::DecryptPS3PKG (
75+ data,
76+ size,
77+ relativeOffset,
78+ header.digest ,
79+ header.pkg_type
80+ );
81+ }
82+ catch (...) {
83+ // Decryption failed
84+ }
85+ }
86+
87+ HRESULT ReadAndDecryptBlock (
88+ IInStream* stream,
89+ UInt64 absOffset,
90+ size_t size,
91+ std::vector<uint8_t >& buf)
92+ {
93+ buf.resize (size);
94+ UInt32 read = 0 ;
95+
96+ RINOK (stream->Seek (absOffset, STREAM_SEEK_SET, nullptr ));
97+ RINOK (stream->Read (buf.data (), static_cast <UInt32>(size), &read));
98+ if (read != size) return E_FAIL;
99+
100+ if (isEncrypted) {
101+ uint64_t rel = absOffset - header.data_offset ;
102+ DecryptData (buf.data (), size, rel);
103+ }
104+
105+ return S_OK;
106+ }
107+
108+ public:
109+ struct FileInfo {
110+ uint64_t offset = 0 ;
111+ uint64_t size = 0 ;
112+ uint32_t flags = 0 ;
113+ uint32_t type = 0 ;
114+ std::string path;
115+ std::vector<uint8_t > fileData;
116+ bool isFolder = false ;
117+ int64_t fileTime1 = 0 ;
118+ int64_t fileTime2 = 0 ;
119+ int64_t fileTime3 = 0 ;
120+ };
121+
122+ HRESULT ParseHeader (IInStream* stream) {
123+ RINOK (stream->Seek (0 , STREAM_SEEK_SET, nullptr ));
124+
125+ UInt32 read = 0 ;
126+
127+ RINOK (stream->Read (&header.magic , 4 , &read));
128+ header.magic = SwapEndian32 (header.magic );
129+ if (header.magic != PKG_MAGIC_PS3) return E_FAIL;
130+
131+ RINOK (stream->Read (&header.pkg_revision , 2 , &read));
132+ header.pkg_revision = SwapEndian16 (header.pkg_revision );
133+
134+ RINOK (stream->Read (&header.pkg_type , 2 , &read));
135+ header.pkg_type = SwapEndian16 (header.pkg_type );
136+
137+ RINOK (stream->Read (&header.pkg_metadata_offset , 4 , &read));
138+ header.pkg_metadata_offset = SwapEndian32 (header.pkg_metadata_offset );
139+
140+ RINOK (stream->Read (&header.pkg_metadata_count , 4 , &read));
141+ header.pkg_metadata_count = SwapEndian32 (header.pkg_metadata_count );
142+
143+ RINOK (stream->Read (&header.pkg_metadata_size , 4 , &read));
144+ header.pkg_metadata_size = SwapEndian32 (header.pkg_metadata_size );
145+
146+ RINOK (stream->Read (&header.item_count , 4 , &read));
147+ header.item_count = SwapEndian32 (header.item_count );
148+
149+ RINOK (stream->Read (&header.total_size , 8 , &read));
150+ header.total_size = SwapEndian64 (header.total_size );
151+
152+ RINOK (stream->Read (&header.data_offset , 8 , &read));
153+ header.data_offset = SwapEndian64 (header.data_offset );
154+
155+ RINOK (stream->Read (&header.data_size , 8 , &read));
156+ header.data_size = SwapEndian64 (header.data_size );
157+
158+ RINOK (stream->Read (header.content_id , 0x30 , nullptr ));
159+ header.content_id [0x2F ] = ' \0 ' ;
160+
161+ RINOK (stream->Read (header.digest , 0x10 , nullptr ));
162+ RINOK (stream->Read (header.pkg_data_riv , 0x10 , nullptr ));
163+
164+ isEncrypted = (header.pkg_type == PKG_TYPE_PS3);
165+ return S_OK;
166+ }
167+
168+ HRESULT ParseFileTable (IInStream* stream, std::vector<FileInfo>& items) {
169+ uint64_t tableStart = header.data_offset ;
170+ std::vector<uint8_t > sizeBuf;
171+
172+ RINOK (ReadAndDecryptBlock (stream, tableStart, 32 , sizeBuf));
173+
174+ uint64_t tableSize1 = SwapEndian64 (*(uint64_t *)(sizeBuf.data () + 8 ));
175+ uint64_t tableSize2 = SwapEndian64 (*(uint64_t *)(sizeBuf.data () + 16 ));
176+
177+ uint64_t tableSize = (tableSize2 > 0 && tableSize2 < header.data_size ) ? tableSize2 : tableSize1;
178+
179+ if (tableSize == 0 || tableSize > header.data_size ) return E_FAIL;
180+
181+ std::vector<uint8_t > tableData;
182+ RINOK (ReadAndDecryptBlock (stream, tableStart, tableSize, tableData));
183+
184+ const size_t ENTRY_SIZE = 32 ;
185+ size_t nameReadOffset = header.item_count * ENTRY_SIZE;
186+
187+ for (uint32_t i = 0 ; i < header.item_count ; i++) {
188+ size_t entryPos = i * ENTRY_SIZE;
189+
190+ if (entryPos + ENTRY_SIZE > tableData.size ()) break ;
191+
192+ const uint8_t * p = tableData.data () + entryPos;
193+
194+ uint32_t nameLen = SwapEndian32 (*(uint32_t *)(p + 0x04 ));
195+ uint64_t dataOff = SwapEndian64 (*(uint64_t *)(p + 0x08 ));
196+ uint64_t dataSz = SwapEndian64 (*(uint64_t *)(p + 0x10 ));
197+ uint32_t flags = SwapEndian32 (*(uint32_t *)(p + 0x18 ));
198+
199+ if (nameLen == 0 || nameLen > 4096 ) continue ;
200+ if (dataOff > header.data_size ) continue ;
201+
202+ FileInfo fi;
203+ fi.offset = header.data_offset + dataOff;
204+ fi.size = dataSz;
205+ fi.flags = flags;
206+ fi.type = (flags >> 24 ) & 0xFF ;
207+ fi.isFolder = (fi.type == ENTRY_TYPE_FOLDER);
208+
209+ if (nameReadOffset < tableData.size () && nameReadOffset + nameLen <= tableData.size ()) {
210+ const char * namePtr = reinterpret_cast <const char *>(tableData.data () + nameReadOffset);
211+ fi.path .assign (namePtr, nameLen);
212+ size_t nul = fi.path .find (' \0 ' );
213+ if (nul != std::string::npos) fi.path .resize (nul);
214+ std::replace (fi.path .begin (), fi.path .end (), ' \\ ' , ' /' );
215+ }
216+ else {
217+ fi.path = " file_" + std::to_string (i);
218+ }
219+
220+ items.push_back (std::move (fi));
221+
222+ nameReadOffset += nameLen;
223+ while (nameReadOffset % 16 != 0 ) nameReadOffset++;
224+ }
225+
226+ return S_OK;
227+ }
228+
229+ HRESULT ExtractFileData (IInStream* stream, FileInfo& fi) {
230+ if (fi.isFolder || fi.size == 0 ) return S_OK;
231+
232+ fi.fileData .resize (fi.size );
233+ UInt32 read = 0 ;
234+ RINOK (stream->Seek (fi.offset , STREAM_SEEK_SET, nullptr ));
235+ RINOK (stream->Read (fi.fileData .data (), static_cast <UInt32>(fi.size ), &read));
236+ if (read != fi.size ) return E_FAIL;
237+
238+ if (isEncrypted) {
239+ uint64_t rel = fi.offset - header.data_offset ;
240+ DecryptData (fi.fileData .data (), fi.size , rel);
241+ }
242+ return S_OK;
243+ }
244+
245+ const PKG_HEADER_PS3& GetHeader () const { return header; }
246+ bool IsEncrypted () const { return isEncrypted; }
247+ };
0 commit comments