Seregon/ShadPKG

A tool for deriving PKG packet encryption keys for ps4 written in c++

C++/47.3 KB/No license
core/file_format/psf.cpp
ShadPKG / core / file_format / psf.cpp
1// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstring>
5
6#include "common/assert.h"
7#include "common/io_file.h"
8#include "common/logging/log.h"
9#include "core/file_format/psf.h"
10#include <optional>
11
12#include "common/logging/log.h"
13
14static const std::unordered_map<std::string_view, u32> psf_known_max_sizes = {
15 {"ACCOUNT_ID", 8}, {"CATEGORY", 4}, {"DETAIL", 1024}, {"FORMAT", 4},
16 {"MAINTITLE", 128}, {"PARAMS", 1024}, {"SAVEDATA_BLOCKS", 8}, {"SAVEDATA_DIRECTORY", 32},
17 {"SUBTITLE", 128}, {"TITLE_ID", 12},
18};
19static inline u32 get_max_size(std::string_view key, u32 default_value) {
20 if (const auto& v = psf_known_max_sizes.find(key); v != psf_known_max_sizes.end()) {
21 return v->second;
22 }
23 return default_value;
24}
25
26bool PSF::Open(const std::filesystem::path& filepath) {
27 using namespace std::chrono;
28 if (std::filesystem::exists(filepath)) {
29 const auto t = std::filesystem::last_write_time(filepath);
30 const auto rel =
31 duration_cast<seconds>(t - std::filesystem::file_time_type::clock::now()).count();
32 const auto tp = system_clock::to_time_t(system_clock::now() + seconds{rel});
33 last_write = system_clock::from_time_t(tp);
34 }
35
36 Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
37 if (!file.IsOpen()) {
38 return false;
39 }
40
41 const u64 psfSize = file.GetSize();
42 std::vector<u8> psf(psfSize);
43 file.Seek(0);
44 file.Read(psf);
45 file.Close();
46 return Open(psf);
47}
48
49bool PSF::Open(const std::vector<u8>& psf_buffer) {
50 const u8* psf_data = psf_buffer.data();
51
52 entry_list.clear();
53 map_binaries.clear();
54 map_strings.clear();
55 map_integers.clear();
56
57 // Parse file contents
58 PSFHeader header{};
59 std::memcpy(&header, psf_data, sizeof(header));
60
61 if (header.magic != PSF_MAGIC) {
62 LOG_ERROR(Core, "Invalid PSF magic number");
63 return false;
64 }
65 if (header.version != PSF_VERSION_1_1 && header.version != PSF_VERSION_1_0) {
66 LOG_ERROR(Core, "Unsupported PSF version: 0x{:08x}", header.version);
67 return false;
68 }
69
70 for (u32 i = 0; i < header.index_table_entries; i++) {
71 PSFRawEntry raw_entry{};
72 std::memcpy(&raw_entry, psf_data + sizeof(PSFHeader) + i * sizeof(PSFRawEntry),
73 sizeof(raw_entry));
74
75 Entry& entry = entry_list.emplace_back();
76 entry.key = std::string{(char*)(psf_data + header.key_table_offset + raw_entry.key_offset)};
77 entry.param_fmt = static_cast<PSFEntryFmt>(raw_entry.param_fmt.Raw());
78 entry.max_len = raw_entry.param_max_len;
79
80 const u8* data = psf_data + header.data_table_offset + raw_entry.data_offset;
81
82 switch (entry.param_fmt) {
83 case PSFEntryFmt::Binary: {
84 std::vector<u8> value(raw_entry.param_len);
85 std::memcpy(value.data(), data, raw_entry.param_len);
86 map_binaries.emplace(i, std::move(value));
87 } break;
88 case PSFEntryFmt::Text: {
89 std::string c_str{reinterpret_cast<const char*>(data)};
90 map_strings.emplace(i, std::move(c_str));
91 } break;
92 case PSFEntryFmt::Integer: {
93 ASSERT_MSG(raw_entry.param_len == sizeof(s32), "PSF integer entry size mismatch");
94 s32 integer = *(s32*)data;
95 map_integers.emplace(i, integer);
96 } break;
97 default:
98 UNREACHABLE_MSG("Unknown PSF entry format");
99 }
100 }
101 return true;
102}
103
104bool PSF::Encode(const std::filesystem::path& filepath) const {
105 Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Write);
106 if (!file.IsOpen()) {
107 return false;
108 }
109
110 last_write = std::chrono::system_clock::now();
111
112 const auto psf_buffer = Encode();
113 const size_t written = file.Write(psf_buffer);
114 if (written != psf_buffer.size()) {
115 LOG_ERROR(Core, "Failed to write PSF file. Written {} Expected {}", written,
116 psf_buffer.size());
117 }
118 return written == psf_buffer.size();
119}
120
121std::vector<u8> PSF::Encode() const {
122 std::vector<u8> psf_buffer;
123 Encode(psf_buffer);
124 return psf_buffer;
125}
126
127void PSF::Encode(std::vector<u8>& psf_buffer) const {
128 psf_buffer.resize(sizeof(PSFHeader) + sizeof(PSFRawEntry) * entry_list.size());
129
130 {
131 auto& header = *(PSFHeader*)psf_buffer.data();
132 header.magic = PSF_MAGIC;
133 header.version = PSF_VERSION_1_1;
134 header.index_table_entries = entry_list.size();
135 }
136
137 const size_t key_table_offset = psf_buffer.size();
138 ((PSFHeader*)psf_buffer.data())->key_table_offset = key_table_offset;
139 for (size_t i = 0; i < entry_list.size(); i++) {
140 auto& raw_entry = ((PSFRawEntry*)(psf_buffer.data() + sizeof(PSFHeader)))[i];
141 const Entry& entry = entry_list[i];
142 raw_entry.key_offset = psf_buffer.size() - key_table_offset;
143 raw_entry.param_fmt.FromRaw(static_cast<u16>(entry.param_fmt));
144 raw_entry.param_max_len = entry.max_len;
145 std::ranges::copy(entry.key, std::back_inserter(psf_buffer));
146 psf_buffer.push_back(0); // NULL terminator
147 }
148
149 const size_t data_table_offset = psf_buffer.size();
150 ((PSFHeader*)psf_buffer.data())->data_table_offset = data_table_offset;
151 for (size_t i = 0; i < entry_list.size(); i++) {
152 if (psf_buffer.size() % 4 != 0) {
153 std::ranges::fill_n(std::back_inserter(psf_buffer), 4 - psf_buffer.size() % 4, 0);
154 }
155 auto& raw_entry = ((PSFRawEntry*)(psf_buffer.data() + sizeof(PSFHeader)))[i];
156 const Entry& entry = entry_list[i];
157 raw_entry.data_offset = psf_buffer.size() - data_table_offset;
158
159 s32 additional_padding = s32(raw_entry.param_max_len);
160
161 switch (entry.param_fmt) {
162 case PSFEntryFmt::Binary: {
163 const auto& value = map_binaries.at(i);
164 raw_entry.param_len = value.size();
165 additional_padding -= s32(raw_entry.param_len);
166 std::ranges::copy(value, std::back_inserter(psf_buffer));
167 } break;
168 case PSFEntryFmt::Text: {
169 const auto& value = map_strings.at(i);
170 raw_entry.param_len = value.size() + 1;
171 additional_padding -= s32(raw_entry.param_len);
172 std::ranges::copy(value, std::back_inserter(psf_buffer));
173 psf_buffer.push_back(0); // NULL terminator
174 } break;
175 case PSFEntryFmt::Integer: {
176 const auto& value = map_integers.at(i);
177 raw_entry.param_len = sizeof(s32);
178 additional_padding -= s32(raw_entry.param_len);
179 const auto value_bytes = reinterpret_cast<const u8*>(&value);
180 std::ranges::copy(value_bytes, value_bytes + sizeof(s32),
181 std::back_inserter(psf_buffer));
182 } break;
183 default:
184 UNREACHABLE_MSG("Unknown PSF entry format");
185 }
186 ASSERT_MSG(additional_padding >= 0, "PSF entry max size mismatch");
187 std::ranges::fill_n(std::back_inserter(psf_buffer), additional_padding, 0);
188 }
189}
190
191std::optional<std::span<const u8>> PSF::GetBinary(std::string_view key) const {
192 const auto& [it, index] = FindEntry(key);
193 if (it == entry_list.end()) {
194 return {};
195 }
196 ASSERT(it->param_fmt == PSFEntryFmt::Binary);
197 return std::span{map_binaries.at(index)};
198}
199
200std::optional<std::string_view> PSF::GetString(std::string_view key) const {
201 const auto& [it, index] = FindEntry(key);
202 if (it == entry_list.end()) {
203 return {};
204 }
205 ASSERT(it->param_fmt == PSFEntryFmt::Text);
206 return std::string_view{map_strings.at(index)};
207}
208
209std::optional<s32> PSF::GetInteger(std::string_view key) const {
210 const auto& [it, index] = FindEntry(key);
211 if (it == entry_list.end()) {
212 return {};
213 }
214 ASSERT(it->param_fmt == PSFEntryFmt::Integer);
215 return map_integers.at(index);
216}
217
218void PSF::AddBinary(std::string key, std::vector<u8> value, bool update) {
219 auto [it, index] = FindEntry(key);
220 bool exist = it != entry_list.end();
221 if (exist && !update) {
222 LOG_ERROR(Core, "PSF: Tried to add binary key that already exists: {}", key);
223 return;
224 }
225 if (exist) {
226 ASSERT_MSG(it->param_fmt == PSFEntryFmt::Binary, "PSF: Change format is not supported");
227 it->max_len = get_max_size(key, value.size());
228 map_binaries.at(index) = std::move(value);
229 return;
230 }
231 Entry& entry = entry_list.emplace_back();
232 entry.max_len = get_max_size(key, value.size());
233 entry.key = std::move(key);
234 entry.param_fmt = PSFEntryFmt::Binary;
235 map_binaries.emplace(entry_list.size() - 1, std::move(value));
236}
237
238void PSF::AddBinary(std::string key, uint64_t value, bool update) {
239 std::vector<u8> data(8);
240 std::memcpy(data.data(), &value, 8);
241 return AddBinary(std::move(key), std::move(data), update);
242}
243
244void PSF::AddString(std::string key, std::string value, bool update) {
245 auto [it, index] = FindEntry(key);
246 bool exist = it != entry_list.end();
247 if (exist && !update) {
248 LOG_ERROR(Core, "PSF: Tried to add string key that already exists: {}", key);
249 return;
250 }
251 if (exist) {
252 ASSERT_MSG(it->param_fmt == PSFEntryFmt::Text, "PSF: Change format is not supported");
253 it->max_len = get_max_size(key, value.size() + 1);
254 map_strings.at(index) = std::move(value);
255 return;
256 }
257 Entry& entry = entry_list.emplace_back();
258 entry.max_len = get_max_size(key, value.size() + 1);
259 entry.key = std::move(key);
260 entry.param_fmt = PSFEntryFmt::Text;
261 map_strings.emplace(entry_list.size() - 1, std::move(value));
262}
263
264void PSF::AddInteger(std::string key, s32 value, bool update) {
265 auto [it, index] = FindEntry(key);
266 bool exist = it != entry_list.end();
267 if (exist && !update) {
268 LOG_ERROR(Core, "PSF: Tried to add integer key that already exists: {}", key);
269 return;
270 }
271 if (exist) {
272 ASSERT_MSG(it->param_fmt == PSFEntryFmt::Integer, "PSF: Change format is not supported");
273 it->max_len = sizeof(s32);
274 map_integers.at(index) = value;
275 return;
276 }
277 Entry& entry = entry_list.emplace_back();
278 entry.key = std::move(key);
279 entry.param_fmt = PSFEntryFmt::Integer;
280 entry.max_len = sizeof(s32);
281 map_integers.emplace(entry_list.size() - 1, value);
282}
283
284std::pair<std::vector<PSF::Entry>::iterator, size_t> PSF::FindEntry(std::string_view key) {
285 auto entry =
286 std::ranges::find_if(entry_list, [&](const auto& entry) { return entry.key == key; });
287 return {entry, std::distance(entry_list.begin(), entry)};
288}
289
290std::pair<std::vector<PSF::Entry>::const_iterator, size_t> PSF::FindEntry(
291 std::string_view key) const {
292 auto entry =
293 std::ranges::find_if(entry_list, [&](const auto& entry) { return entry.key == key; });
294 return {entry, std::distance(entry_list.begin(), entry)};
295}
296