Skip to content

Commit db9d6ad

Browse files
committed
update
include pup extraction beta
1 parent 4ff6002 commit db9d6ad

File tree

8 files changed

+1875
-729
lines changed

8 files changed

+1875
-729
lines changed

CPP/7zip/Archive/PSX/PSX.cpp

Lines changed: 300 additions & 128 deletions
Large diffs are not rendered by default.

CPP/7zip/Archive/PSX/PSX.h

Lines changed: 248 additions & 598 deletions
Large diffs are not rendered by default.

CPP/7zip/Archive/PSX/log.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#pragma once
2+
3+
#ifdef _DEBUG
4+
5+
// Trait to detect if T can be streamed into std::ostream
6+
template <typename T>
7+
class is_streamable {
8+
private:
9+
template <typename U>
10+
static auto test(int) -> decltype(std::declval<std::ostream&>() << std::declval<U>(), std::true_type());
11+
12+
template <typename>
13+
static std::false_type test(...);
14+
15+
public:
16+
static constexpr bool value = decltype(test<T>(0))::value;
17+
};
18+
19+
// Helper to stream a single argument
20+
template <typename T>
21+
inline typename std::enable_if<is_streamable<T>::value>::type
22+
logDebugHelper(std::ostringstream& oss, T&& arg) {
23+
oss << std::forward<T>(arg);
24+
}
25+
26+
template <typename T>
27+
inline typename std::enable_if<!is_streamable<T>::value>::type
28+
logDebugHelper(std::ostringstream& oss, T&&) {
29+
oss << "[unstreamable type]";
30+
}
31+
32+
// Variadic helper to stream multiple arguments
33+
template <typename T, typename... Args>
34+
inline void logDebugHelper(std::ostringstream& oss, T&& first, Args&&... rest) {
35+
logDebugHelper(oss, std::forward<T>(first));
36+
logDebugHelper(oss, std::forward<Args>(rest)...);
37+
}
38+
39+
// Main logging function
40+
template <typename... Args>
41+
inline void logDebug(Args&&... args) {
42+
static std::string logPath;
43+
if (logPath.empty()) {
44+
wchar_t desktop[MAX_PATH] = {};
45+
if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_DESKTOP, nullptr, 0, desktop))) {
46+
std::wstring wpath = std::wstring(desktop) + L"\\unified_pkg_debug.log";
47+
int sz = WideCharToMultiByte(CP_UTF8, 0, wpath.c_str(), -1, nullptr, 0, nullptr, nullptr);
48+
if (sz > 0) {
49+
logPath.resize(sz - 1);
50+
WideCharToMultiByte(CP_UTF8, 0, wpath.c_str(), -1, &logPath[0], sz - 1, nullptr, nullptr);
51+
}
52+
}
53+
}
54+
55+
if (logPath.empty()) return;
56+
57+
std::ofstream f(logPath, std::ios::app);
58+
if (!f.is_open()) return;
59+
60+
auto now = std::chrono::system_clock::now();
61+
std::time_t t_c = std::chrono::system_clock::to_time_t(now);
62+
f << "[" << std::put_time(std::localtime(&t_c), "%Y-%m-%d %H:%M:%S") << "] ";
63+
64+
std::ostringstream oss;
65+
logDebugHelper(oss, std::forward<Args>(args)...);
66+
f << oss.str() << "\n";
67+
}
68+
#endif // _DEBUG

CPP/7zip/Archive/PSX/ps3_pkg.h

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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

Comments
 (0)