CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/ELF/ParamSFO.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <cstdio>
19
#include <cstring>
20
21
#include "Common/CommonTypes.h"
22
#include "Common/Log.h"
23
#include "Common/StringUtils.h"
24
#include "Common/Swap.h"
25
#include "Core/ELF/ParamSFO.h"
26
#include "Core/Core.h"
27
28
struct Header
29
{
30
u32_le magic; /* Always PSF */
31
u32_le version; /* Usually 1.1 */
32
u32_le key_table_start; /* Start position of key_table */
33
u32_le data_table_start; /* Start position of data_table */
34
u32_le index_table_entries; /* Number of entries in index_table*/
35
};
36
37
struct IndexTable
38
{
39
u16_le key_table_offset; /* Offset of the param_key from start of key_table */
40
u16_le param_fmt; /* Type of data of param_data in the data_table */
41
u32_le param_len; /* Used Bytes by param_data in the data_table */
42
u32_le param_max_len; /* Total bytes reserved for param_data in the data_table */
43
u32_le data_table_offset; /* Offset of the param_data from start of data_table */
44
};
45
46
void ParamSFOData::SetValue(const std::string &key, unsigned int value, int max_size) {
47
values[key].type = VT_INT;
48
values[key].i_value = value;
49
values[key].max_size = max_size;
50
}
51
void ParamSFOData::SetValue(const std::string &key, const std::string &value, int max_size) {
52
values[key].type = VT_UTF8;
53
values[key].s_value = value;
54
values[key].max_size = max_size;
55
}
56
57
void ParamSFOData::SetValue(const std::string &key, const u8 *value, unsigned int size, int max_size) {
58
values[key].type = VT_UTF8_SPE;
59
values[key].SetData(value, size);
60
values[key].max_size = max_size;
61
}
62
63
int ParamSFOData::GetValueInt(const std::string &key) const {
64
std::map<std::string,ValueData>::const_iterator it = values.find(key);
65
if (it == values.end() || it->second.type != VT_INT)
66
return 0;
67
return it->second.i_value;
68
}
69
70
std::string ParamSFOData::GetValueString(const std::string &key) const {
71
std::map<std::string,ValueData>::const_iterator it = values.find(key);
72
if (it == values.end() || (it->second.type != VT_UTF8))
73
return "";
74
return it->second.s_value;
75
}
76
77
bool ParamSFOData::HasKey(const std::string &key) const {
78
return values.find(key) != values.end();
79
}
80
81
const u8 *ParamSFOData::GetValueData(const std::string &key, unsigned int *size) const {
82
std::map<std::string,ValueData>::const_iterator it = values.find(key);
83
if (it == values.end() || (it->second.type != VT_UTF8_SPE)) {
84
return 0;
85
}
86
if (size) {
87
*size = it->second.u_size;
88
}
89
return it->second.u_value;
90
}
91
92
std::vector<std::string> ParamSFOData::GetKeys() const {
93
std::vector<std::string> result;
94
for (const auto &pair : values) {
95
result.push_back(pair.first);
96
}
97
return result;
98
}
99
100
std::string ParamSFOData::GetDiscID() {
101
const std::string discID = GetValueString("DISC_ID");
102
if (discID.empty()) {
103
std::string fakeID = GenerateFakeID(Path());
104
WARN_LOG(Log::Loader, "No DiscID found - generating a fake one: '%s' (from %s)", fakeID.c_str(), PSP_CoreParameter().fileToStart.c_str());
105
ValueData data;
106
data.type = VT_UTF8;
107
data.s_value = fakeID;
108
values["DISC_ID"] = data;
109
return fakeID;
110
}
111
return discID;
112
}
113
114
// I'm so sorry Ced but this is highly endian unsafe :(
115
bool ParamSFOData::ReadSFO(const u8 *paramsfo, size_t size) {
116
if (size < sizeof(Header))
117
return false;
118
const Header *header = (const Header *)paramsfo;
119
if (header->magic != 0x46535000)
120
return false;
121
if (header->version != 0x00000101)
122
WARN_LOG(Log::Loader, "Unexpected SFO header version: %08x", header->version);
123
124
const IndexTable *indexTables = (const IndexTable *)(paramsfo + sizeof(Header));
125
126
if (header->key_table_start > size || header->data_table_start > size) {
127
return false;
128
}
129
130
const u8 *data_start = paramsfo + header->data_table_start;
131
132
auto readStringCapped = [paramsfo, size](size_t offset, size_t maxLen) -> std::string {
133
std::string str;
134
while (offset < size) {
135
char c = (char)(paramsfo[offset]);
136
if (c) {
137
str.push_back(c);
138
} else {
139
break;
140
}
141
offset++;
142
if (maxLen != 0 && str.size() == maxLen)
143
break;
144
}
145
return str;
146
};
147
148
for (u32 i = 0; i < header->index_table_entries; i++)
149
{
150
size_t key_offset = header->key_table_start + indexTables[i].key_table_offset;
151
if (key_offset >= size) {
152
return false;
153
}
154
155
size_t data_offset = header->data_table_start + indexTables[i].data_table_offset;
156
if (data_offset >= size) {
157
return false;
158
}
159
160
std::string key = readStringCapped(key_offset, 0);
161
if (key.empty())
162
continue; // Likely ran into a truncated PARAMSFO.
163
164
switch (indexTables[i].param_fmt) {
165
case 0x0404:
166
{
167
if (data_offset + 4 > size)
168
continue;
169
// Unsigned int
170
const u32_le *data = (const u32_le *)(paramsfo + data_offset);
171
SetValue(key, *data, indexTables[i].param_max_len);
172
VERBOSE_LOG(Log::Loader, "%s %08x", key.c_str(), *data);
173
}
174
break;
175
case 0x0004:
176
// Special format UTF-8
177
{
178
if (data_offset + indexTables[i].param_len > size)
179
continue;
180
const u8 *utfdata = (const u8 *)(paramsfo + data_offset);
181
VERBOSE_LOG(Log::Loader, "%s %s", key.c_str(), utfdata);
182
SetValue(key, utfdata, indexTables[i].param_len, indexTables[i].param_max_len);
183
}
184
break;
185
case 0x0204:
186
// Regular UTF-8
187
{
188
// TODO: Likely should use param_len here, but there's gotta be a reason we avoided it before.
189
std::string str = readStringCapped(data_offset, indexTables[i].param_max_len);
190
VERBOSE_LOG(Log::Loader, "%s %s", key.c_str(), str.c_str());
191
SetValue(key, str, indexTables[i].param_max_len);
192
}
193
break;
194
default:
195
break;
196
}
197
}
198
199
return true;
200
}
201
202
int ParamSFOData::GetDataOffset(const u8 *paramsfo, const char *dataName) {
203
const Header *header = (const Header *)paramsfo;
204
if (header->magic != 0x46535000)
205
return -1;
206
if (header->version != 0x00000101)
207
WARN_LOG(Log::Loader, "Unexpected SFO header version: %08x", header->version);
208
209
const IndexTable *indexTables = (const IndexTable *)(paramsfo + sizeof(Header));
210
211
const u8 *key_start = paramsfo + header->key_table_start;
212
int data_start = header->data_table_start;
213
214
for (u32 i = 0; i < header->index_table_entries; i++)
215
{
216
const char *key = (const char *)(key_start + indexTables[i].key_table_offset);
217
if (!strcmp(key, dataName))
218
{
219
return data_start + indexTables[i].data_table_offset;
220
}
221
}
222
223
return -1;
224
}
225
226
void ParamSFOData::WriteSFO(u8 **paramsfo, size_t *size) const {
227
size_t total_size = 0;
228
size_t key_size = 0;
229
size_t data_size = 0;
230
231
Header header;
232
header.magic = 0x46535000;
233
header.version = 0x00000101;
234
header.index_table_entries = 0;
235
236
total_size += sizeof(Header);
237
238
// Get size info
239
for (const auto &[k, v] : values)
240
{
241
key_size += k.size() + 1;
242
data_size += v.max_size;
243
244
header.index_table_entries++;
245
}
246
247
// Padding
248
while ((key_size % 4) != 0) key_size++;
249
250
header.key_table_start = sizeof(Header) + header.index_table_entries * sizeof(IndexTable);
251
header.data_table_start = header.key_table_start + (u32)key_size;
252
253
total_size += sizeof(IndexTable) * header.index_table_entries;
254
total_size += key_size;
255
total_size += data_size;
256
*size = total_size;
257
258
size_t aligned_size = (total_size + 15) & ~15;
259
u8* data = new u8[aligned_size];
260
*paramsfo = data;
261
memset(data, 0, aligned_size);
262
memcpy(data, &header, sizeof(Header));
263
264
// Now fill
265
IndexTable *index_ptr = (IndexTable*)(data + sizeof(Header));
266
u8* key_ptr = data + header.key_table_start;
267
u8* data_ptr = data + header.data_table_start;
268
269
for (const auto &[k, v] : values)
270
{
271
u16 offset = (u16)(key_ptr - (data+header.key_table_start));
272
index_ptr->key_table_offset = offset;
273
offset = (u16)(data_ptr - (data+header.data_table_start));
274
index_ptr->data_table_offset = offset;
275
index_ptr->param_max_len = v.max_size;
276
if (v.type == VT_INT)
277
{
278
index_ptr->param_fmt = 0x0404;
279
index_ptr->param_len = 4;
280
281
*(s32_le *)data_ptr = v.i_value;
282
}
283
else if (v.type == VT_UTF8_SPE)
284
{
285
index_ptr->param_fmt = 0x0004;
286
index_ptr->param_len = v.u_size;
287
288
memset(data_ptr,0,index_ptr->param_max_len);
289
memcpy(data_ptr,v.u_value,index_ptr->param_len);
290
}
291
else if (v.type == VT_UTF8)
292
{
293
index_ptr->param_fmt = 0x0204;
294
index_ptr->param_len = (u32)v.s_value.size()+1;
295
296
memcpy(data_ptr,v.s_value.c_str(),index_ptr->param_len);
297
data_ptr[index_ptr->param_len] = 0;
298
}
299
300
memcpy(key_ptr,k.c_str(),k.size());
301
key_ptr[k.size()] = 0;
302
303
data_ptr += index_ptr->param_max_len;
304
key_ptr += k.size() + 1;
305
index_ptr++;
306
307
}
308
}
309
310
void ParamSFOData::Clear() {
311
values.clear();
312
}
313
314
void ParamSFOData::ValueData::SetData(const u8* data, int size) {
315
if (u_value) {
316
delete[] u_value;
317
u_value = 0;
318
}
319
if (size > 0) {
320
u_value = new u8[size];
321
memcpy(u_value, data, size);
322
}
323
u_size = size;
324
}
325
326
std::string ParamSFOData::GenerateFakeID(const Path &filename) const {
327
// Generates fake gameID for homebrew based on it's folder name.
328
// Should probably not be a part of ParamSFO, but it'll be called in same places.
329
// FileToStart here is actually a directory name, not a file, so taking GetFilename on it gets what we want.
330
Path path = PSP_CoreParameter().fileToStart;
331
if (!filename.empty())
332
path = filename;
333
334
std::string file = path.GetFilename();
335
336
int sumOfAllLetters = 0;
337
for (char &c : file) {
338
sumOfAllLetters += c;
339
// Get rid of some garbage characters than can arise when opening content URIs. Well, I've only seen '%', but...
340
if (strchr("%() []", c) != nullptr) {
341
c = 'X';
342
} else {
343
c = toupper(c);
344
}
345
}
346
347
if (file.size() < 4) {
348
file += "HOME";
349
}
350
file = file.substr(0, 4);
351
352
std::string fakeID = file + StringFromFormat("%05d", sumOfAllLetters);
353
return fakeID;
354
}
355
356