1 #include "xbox_portable.h"
2 #ifndef XBOX_PORTABLE_NO_DISC_WRAPPER
5 #include "xbox_region.h"
11 #define XBOX_SECTOR_SIZE 2048U
12 #define XBOX_XDFS_MAGIC "MICROSOFT*XBOX*MEDIA"
13 #define XBOX_XDFS_MAGIC_SIZE 20U
14 #define XBOX_XDFS_STANDARD_VOLUME_LBA 32U
15 #define XBOX_XDFS_LEGACY_VOLUME_LBA 306112U
16 #define XBOX_MAX_ROOT_DIRECTORY_SECTORS 4096U
17 #define XBOX_XBE_CERTIFICATE_MIN_SIZE 0x0B0U
19 static uint16_t xbox_le16(const unsigned char *p)
21 return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
24 static uint32_t xbox_le32(const unsigned char *p)
26 return (uint32_t)p[0] |
27 ((uint32_t)p[1] << 8) |
28 ((uint32_t)p[2] << 16) |
29 ((uint32_t)p[3] << 24);
32 static int xbox_ascii_case_equal(const unsigned char *value,
37 size_t expected_length;
39 if (!value || !expected)
42 expected_length = strlen(expected);
43 if (value_length != expected_length)
46 for (index = 0; index < value_length; ++index) {
47 if (tolower((unsigned char)value[index]) !=
48 tolower((unsigned char)expected[index]))
55 static size_t xbox_utf8_append(char *output,
60 if (!output || output_size == 0 || position >= output_size)
63 if (codepoint <= 0x7FU) {
64 if (position + 1 >= output_size)
66 output[position++] = (char)codepoint;
67 } else if (codepoint <= 0x7FFU) {
68 if (position + 2 >= output_size)
70 output[position++] = (char)(0xC0U | (codepoint >> 6));
71 output[position++] = (char)(0x80U | (codepoint & 0x3FU));
73 if (position + 3 >= output_size)
75 output[position++] = (char)(0xE0U | (codepoint >> 12));
76 output[position++] = (char)(0x80U | ((codepoint >> 6) & 0x3FU));
77 output[position++] = (char)(0x80U | (codepoint & 0x3FU));
80 output[position] = '\0';
84 static void xbox_decode_title(const unsigned char *certificate,
85 size_t certificate_size,
92 if (!output || output_size == 0)
96 if (!certificate || certificate_size < 0x00CU + 80U)
100 for (index = 0; index < 40U; ++index) {
101 uint16_t codepoint = xbox_le16(certificate + 0x00CU + (index * 2U));
104 if (codepoint >= 0xD800U && codepoint <= 0xDFFFU)
105 codepoint = (uint16_t)'?';
106 position = xbox_utf8_append(output,
113 static int xbox_parse_media_id(const unsigned char *dmi,
121 if (!output || output_size == 0)
125 if (!dmi || dmi_size <= 8U)
129 for (index = 8U; index < dmi_size && index < 24U; ++index) {
130 unsigned char c = dmi[index];
132 if (position + 1U >= output_size)
134 output[position++] = (char)c;
137 output[position] = '\0';
139 return position >= 4U;
142 static int xbox_find_default_xbe(xbox_portable_read10_func read10,
148 unsigned char sector[XBOX_SECTOR_SIZE];
150 uint32_t sector_index;
152 if (!read10 || !xbe_lba || root_size == 0)
155 sectors = (root_size + XBOX_SECTOR_SIZE - 1U) / XBOX_SECTOR_SIZE;
156 if (sectors > XBOX_MAX_ROOT_DIRECTORY_SECTORS)
157 sectors = XBOX_MAX_ROOT_DIRECTORY_SECTORS;
159 for (sector_index = 0; sector_index < sectors; ++sector_index) {
162 root_lba + sector_index,
168 while (offset + 14U <= sizeof(sector)) {
174 left_entry = xbox_le16(sector + offset);
175 name_length = sector[offset + 13U];
176 if (left_entry == 0xFFFFU || name_length == 0U ||
177 name_length == 0xFFU)
179 if (offset + 14U + name_length > sizeof(sector))
182 start_lba = xbox_le32(sector + offset + 4U);
183 if (xbox_ascii_case_equal(sector + offset + 14U,
186 *xbe_lba = start_lba;
190 next_offset = (14U + (size_t)name_length + 3U) & ~((size_t)3U);
191 if (next_offset == 0)
193 offset += next_offset;
200 static int xbox_parse_xbe_certificate(xbox_portable_read10_func read10,
203 xbox_portable_metadata *metadata)
205 unsigned char header[XBOX_SECTOR_SIZE];
206 unsigned char certificate[XBOX_SECTOR_SIZE * 2U];
208 uint32_t certificate_va;
209 uint32_t file_offset;
210 uint32_t certificate_lba;
212 uint32_t sectors_to_read;
213 const unsigned char *cert;
215 if (read10(context, xbe_lba, 1, header, sizeof(header)) < 0)
217 if (xbox_le32(header) != 0x48454258U)
220 base_va = xbox_le32(header + 0x104U);
221 certificate_va = xbox_le32(header + 0x118U);
222 if (certificate_va < base_va)
225 file_offset = certificate_va - base_va;
226 certificate_lba = xbe_lba + (file_offset / XBOX_SECTOR_SIZE);
227 inner_offset = file_offset % XBOX_SECTOR_SIZE;
229 (inner_offset + XBOX_XBE_CERTIFICATE_MIN_SIZE > XBOX_SECTOR_SIZE) ? 2U : 1U;
231 memset(certificate, 0, sizeof(certificate));
236 sizeof(certificate)) < 0)
239 cert = certificate + inner_offset;
240 metadata->title_id = xbox_le32(cert + 0x008U);
241 metadata->allowed_media = xbox_le32(cert + 0x09CU);
242 metadata->game_region = xbox_le32(cert + 0x0A0U);
243 metadata->game_ratings = xbox_le32(cert + 0x0A4U);
244 metadata->disc_number = xbox_le32(cert + 0x0A8U);
245 metadata->version = xbox_le32(cert + 0x0ACU);
246 metadata->have_game_region = 1;
247 xbox_decode_title(cert,
248 XBOX_XBE_CERTIFICATE_MIN_SIZE,
250 sizeof(metadata->title));
251 xbox_region_format(metadata->game_region,
253 sizeof(metadata->region));
258 void xbox_portable_metadata_init(xbox_portable_metadata *metadata)
261 memset(metadata, 0, sizeof(*metadata));
264 int xbox_portable_read_metadata(xbox_portable_read10_func read10,
265 xbox_portable_read_structure_func read_structure,
267 xbox_portable_metadata *metadata)
269 static const uint32_t volume_candidates[] = {
270 XBOX_XDFS_STANDARD_VOLUME_LBA,
271 XBOX_XDFS_LEGACY_VOLUME_LBA
273 unsigned char volume[XBOX_SECTOR_SIZE];
274 unsigned char dmi[XBOX_SECTOR_SIZE];
275 uint32_t root_lba = 0;
276 uint32_t root_size = 0;
277 uint32_t xbe_lba = 0;
278 size_t candidate_index;
280 if (!read10 || !metadata)
283 xbox_portable_metadata_init(metadata);
285 for (candidate_index = 0;
286 candidate_index < sizeof(volume_candidates) / sizeof(volume_candidates[0]);
288 memset(volume, 0, sizeof(volume));
290 volume_candidates[candidate_index],
295 if (memcmp(volume, XBOX_XDFS_MAGIC, XBOX_XDFS_MAGIC_SIZE) != 0)
298 metadata->xdfs_volume_lba = volume_candidates[candidate_index];
299 root_lba = xbox_le32(volume + 0x14U);
300 root_size = xbox_le32(volume + 0x18U);
304 if (root_lba == 0 || root_size == 0)
306 if (!xbox_find_default_xbe(read10,
312 if (!xbox_parse_xbe_certificate(read10,
318 if (read_structure) {
319 memset(dmi, 0, sizeof(dmi));
320 if (read_structure(context,
325 xbox_parse_media_id(dmi,
328 sizeof(metadata->media_id));
335 #ifndef XBOX_PORTABLE_NO_DISC_WRAPPER
336 static int xbox_disc_read10(void *context,
339 unsigned char *buffer,
342 return disc_xbox_read_10((disc *)context,
349 static int xbox_disc_read_structure(void *context,
352 unsigned char *buffer,
355 return disc_xbox_read_dvd_structure((disc *)context,
362 int xbox_portable_read_metadata_from_disc(struct disc_s *disc_value,
363 xbox_portable_metadata *metadata)
368 return xbox_portable_read_metadata(xbox_disc_read10,
369 xbox_disc_read_structure,