.
[svpe-tools.git] / negentig.c
blobd54dbb504168cc3179b5df87a47711eff4730ced
1 // Copyright 2007,2008 Segher Boessenkool <segher@kernel.crashing.org>
2 // Licensed under the terms of the GNU GPL, version 2
4 #include <openssl/aes.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <time.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
13 typedef unsigned char u8;
14 typedef unsigned short u16;
15 typedef unsigned int u32;
16 typedef unsigned long long u64;
18 static int dump_partition_data = 0;
19 static u32 max_size_to_auto_analyse = 0x1000000;
20 static int uncompress_yaz0 = 1;
21 static int unpack_rarc = 1;
23 static FILE *disc_fp;
25 static u64 partition_raw_offset;
26 static u64 partition_data_offset;
27 static u64 partition_data_size;
28 static u8 h3[0x18000];
30 static u8 disc_key[16];
32 static void print_bytes(u8 *x, u32 n)
34 u32 i;
36 for (i = 0; i < n; i++)
37 fprintf(stderr, "%02x", x[i]);
40 static void aes_cbc_dec(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out)
42 AES_KEY aes_key;
44 AES_set_decrypt_key(key, 128, &aes_key);
45 AES_cbc_encrypt(in, out, len, &aes_key, iv, AES_DECRYPT);
48 static void decrypt_title_key(u8 *title_key, u8 *title_id)
50 u8 common_key[16];
51 u8 iv[16];
52 FILE *fp;
54 fp = fopen("common-key", "rb");
55 fread(common_key, 1, 16, fp);
56 fclose(fp);
58 memset(iv, 0, sizeof iv);
59 memcpy(iv, title_id, 8);
61 aes_cbc_dec(common_key, iv, title_key, 16, disc_key);
64 static u32 be32(u8 *p)
66 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
69 static u64 be64(u8 *p)
71 return ((u64)be32(p) << 32) | be32(p + 4);
74 static u64 be34(u8 *p)
76 return 4 * (u64)be32(p);
79 static void seek(u64 offset)
81 fseeko(disc_fp, offset, SEEK_SET);
84 static void disc_read(u64 offset, u8 *data, u32 len)
86 seek(offset);
87 fread(data, 1, len, disc_fp);
90 static void partition_raw_read(u64 offset, u8 *data, u32 len)
92 disc_read(partition_raw_offset + offset, data, len);
95 static void partition_read_block(u64 blockno, u8 *block)
97 u8 raw[0x8000];
98 u8 iv[16];
99 u64 offset;
101 offset = partition_data_offset + 0x8000 * blockno;
102 partition_raw_read(offset, raw, 0x8000);
104 // XXX: check H0, H1, H2 here
106 memcpy(iv, raw + 0x3d0, 16);
107 aes_cbc_dec(disc_key, iv, raw + 0x400, 0x7c00, block);
110 static void partition_read(u64 offset, u8 *data, u32 len)
112 u8 block[0x8000];
113 u32 offset_in_block;
114 u32 len_in_block;
116 while(len) {
117 offset_in_block = offset % 0x7c00;
118 len_in_block = 0x7c00 - offset_in_block;
119 if (len_in_block > len)
120 len_in_block = len;
121 partition_read_block(offset / 0x7c00, block);
122 memcpy(data, block + offset_in_block, len_in_block);
123 data += len_in_block;
124 offset += len_in_block;
125 len -= len_in_block;
129 static void spinner(u64 x, u64 max)
131 static u32 spin;
132 static time_t start_time;
133 static u32 expected_total;
134 u32 d;
135 double percent;
136 u32 h, m, s;
138 if (x == 0) {
139 start_time = time(0);
140 expected_total = 300;
143 if (x == max) {
144 fprintf(stderr, "Done. \n");
145 return;
148 d = time(0) - start_time;
150 if (d != 0)
151 expected_total = (15 * expected_total + d * max / x) / 16;
153 if (expected_total > d)
154 d = expected_total - d;
155 else
156 d = 0;
158 h = d / 3600;
159 m = (d / 60) % 60;
160 s = d % 60;
161 percent = 100.0 * x / max;
163 fprintf(stderr, "%5.2f%% (%c) ETA: %d:%02d:%02d \r",
164 percent, "/|\\-"[(spin++ / 64) % 4], h, m, s);
165 fflush(stderr);
168 static void do_data(u64 size)
170 u8 data[0x7c00];
171 u64 offset;
172 u64 remaining_size;
173 u32 block_size;
174 FILE *fp;
176 size = (size / 0x8000) * 0x7c00;
178 fp = fopen("###dat###", "wb");
180 fprintf(stderr, "\nDumping partition contents...\n");
181 offset = 0;
182 remaining_size = size;
183 while (remaining_size) {
184 spinner(offset, size);
186 block_size = 0x7c00;
187 if (block_size > remaining_size)
188 block_size = remaining_size;
190 partition_read(offset, data, block_size);
191 fwrite(data, 1, block_size, fp);
193 offset += block_size;
194 remaining_size -= block_size;
196 spinner(0, 0);
198 fclose(fp);
201 static void copy_file(const char *name, u64 offset, u64 size)
203 u8 data[0x80000];
204 FILE *fp;
205 u32 block_size;
207 fp = fopen(name, "wb");
209 while (size) {
210 block_size = sizeof data;
211 if (block_size > size)
212 block_size = size;
214 partition_read(offset, data, block_size);
215 fwrite(data, 1, block_size, fp);
217 offset += block_size;
218 size -= block_size;
221 fclose(fp);
224 static void do_yaz0(u8 *in, u32 in_size, u8 *out, u32 out_size)
226 u32 nout;
227 u8 bits;
228 u32 nbits;
229 u32 n, d, i;
231 nbits = 0;
232 in += 0x10;
233 for (nout = 0; nout < out_size; ) {
234 if (nbits == 0) {
235 bits = *in++;
236 nbits = 8;
239 if ((bits & 0x80) != 0) {
240 *out++ = *in++;
241 nout++;
242 } else {
243 n = *in++;
244 d = *in++;
245 d |= (n << 8) & 0xf00;
246 n >>= 4;
247 if (n == 0)
248 n = 0x10 + *in++;
249 n += 2;
250 d++;
252 for (i = 0; i < n; i++) {
253 *out = *(out - d);
254 out++;
256 nout += n;
259 nbits--;
260 bits <<= 1;
264 static void do_fst_file(const char *name, u64 offset, u64 size)
266 FILE *fp;
267 u8 *data;
269 if (size > max_size_to_auto_analyse) {
270 copy_file(name, offset, size);
272 return;
275 data = malloc(size);
276 partition_read(offset, data, size);
278 if (uncompress_yaz0 && size >= 8 && memcmp(data, "Yaz0", 4) == 0) {
279 u8 *dec;
280 u32 dec_size;
282 fprintf(stderr, " [Yaz0]");
284 dec_size = be32(data + 4);
285 dec = malloc(dec_size);
287 do_yaz0(data, size, dec, dec_size);
289 free(data);
290 data = dec;
291 size = dec_size;
294 if (unpack_rarc && size >= 8 && memcmp(data, "RARC", 4) == 0) {
295 fprintf(stderr, " [RARC]");
298 fp = fopen(name, "wb");
299 fwrite(data, 1, size, fp);
300 fclose(fp);
302 free(data);
305 static u32 do_fst(u8 *fst, const char *names, u32 i, char *indent, int is_last)
307 u64 offset;
308 u32 size;
309 const char *name;
310 u32 parent;
311 u32 j;
313 name = names + (be32(fst + 12*i) & 0x00ffffff);
314 size = be32(fst + 12*i + 8);
316 if (i == 0) {
317 fprintf(stderr, "/\n");
319 for (j = 1; j < size; )
320 j = do_fst(fst, names, j, indent, (j == size - 1));
322 return size;
325 if (fst[12*i]) {
326 parent = be32(fst + 12*i + 4);
327 is_last = (be32(fst + 12*parent + 8) == size);
330 fprintf(stderr, "%s%c-- %s", indent, "|+"[is_last], name);
332 if (fst[12*i]) {
333 mkdir(name, 0777);
334 chdir(name);
336 fprintf(stderr, "\n");
338 if (is_last)
339 strcat(indent, " ");
340 else
341 strcat(indent, "| ");
343 for (j = i + 1; j < size; )
344 j = do_fst(fst, names, j, indent, (j == size - 1));
346 indent[strlen(indent) - 4] = 0;
348 chdir("..");
350 return size;
351 } else {
352 offset = be34(fst + 12*i + 4);
353 do_fst_file(name, offset, size);
355 fprintf(stderr, "\n");
357 return i + 1;
361 static void do_files(void)
363 u8 b[0x480]; // XXX: determine actual header size
364 u64 dol_offset;
365 u64 fst_offset;
366 u32 fst_size;
367 u8 *fst;
368 char indent[999];
369 u32 n_files;
371 partition_read(0, b, sizeof b);
373 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
374 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
375 fprintf(stderr, "Name: %s\n", b + 0x20);
376 fprintf(stderr, "\n");
378 dol_offset = be34(b + 0x0420);
379 fst_offset = be34(b + 0x0424);
380 fst_size = be34(b + 0x0428);
382 fprintf(stderr, "\tDOL @ %09llx\n", dol_offset);
383 fprintf(stderr, "\tFST @ %09llx (size %08x)\n", fst_offset, fst_size);
385 copy_file("###apl###", 0x2440, dol_offset - 0x2440);
386 // XXX: wrong way to get this size, there is a header
387 copy_file("###dol###", dol_offset, fst_offset - dol_offset);
388 // XXX: similar, perhaps
390 fst = malloc(fst_size);
391 partition_read(fst_offset, fst, fst_size);
392 n_files = be32(fst + 8);
394 fprintf(stderr, "%d entries\n", n_files);
396 indent[0] = 0;
397 if (n_files > 1)
398 do_fst(fst, (char *)fst + 12*n_files, 0, indent, 0);
400 free(fst);
403 static void do_partition(void)
405 u8 b[0x02c0];
406 u64 title_id;
407 u32 tmd_offset;
408 u64 tmd_size;
409 u32 cert_size;
410 u64 cert_offset;
411 u64 h3_offset;
412 char dirname[] = "title-0000000000000000";
414 partition_raw_read(0, b, 0x02c0);
416 decrypt_title_key(b + 0x01bf, b + 0x01dc);
418 title_id = be64(b + 0x01dc);
420 fprintf(stderr, "\ttitle id = %016llx\n", title_id);
422 // XXX: we should check the cert chain here, and read the tmd
424 tmd_offset = be32(b + 0x02a4);
425 tmd_size = be34(b + 0x02a8);
426 cert_size = be32(b + 0x02ac);
427 cert_offset = be34(b + 0x02b0);
428 h3_offset = be34(b + 0x02b4);
429 partition_data_offset = be34(b + 0x02b8);
430 partition_data_size = be34(b + 0x02bc);
432 fprintf(stderr, "\ttmd offset = %08x\n", tmd_offset);
433 fprintf(stderr, "\ttmd size = %09llx\n", tmd_size);
434 fprintf(stderr, "\tcert size = %08x\n", cert_size);
435 fprintf(stderr, "\tcert offset = %09llx\n", cert_offset);
436 fprintf(stderr, "\tdata offset = %09llx\n", partition_data_offset);
437 fprintf(stderr, "\tdata size = %09llx\n", partition_data_size);
439 partition_raw_read(h3_offset, h3, 0x18000);
441 // XXX: check h3 against h4 here
443 snprintf(dirname, sizeof dirname, "%016llx", title_id);
445 mkdir(dirname, 0777);
446 chdir(dirname);
448 if (dump_partition_data)
449 do_data(partition_data_size);
451 do_files();
453 chdir("..");
456 static void do_disc(void)
458 u8 b[0x100];
459 u64 partition_offset[32]; // XXX: don't know the real maximum
460 u32 n_partitions;
461 u32 i;
463 disc_read(0, b, sizeof b);
465 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
466 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
467 fprintf(stderr, "Name: %s\n", b + 0x20);
468 fprintf(stderr, "\n");
470 disc_read(0x40000, b, sizeof b);
471 n_partitions = be32(b);
473 disc_read(be34(b + 4), b, sizeof b);
474 for (i = 0; i < n_partitions; i++)
475 partition_offset[i] = be34(b + 8 * i);
477 fprintf(stderr, "%d partitions:\n", n_partitions);
478 for (i = 0; i < n_partitions; i++)
479 fprintf(stderr, "\tpartition #%d @ %09llx\n", i,
480 partition_offset[i]);
482 for (i = 0; i < n_partitions; i++) {
483 fprintf(stderr, "\nDoing partition %d...\n", i);
484 fprintf(stderr, "--------------------------------\n");
486 partition_raw_offset = partition_offset[i];
487 do_partition();
489 //break; // XXX SII: for testing
493 int main(int argc, char **argv)
495 if (argc != 2) {
496 fprintf(stderr, "Usage: %s <disc file>\n", argv[0]);
497 return 1;
500 disc_fp = fopen(argv[1], "rb");
502 do_disc();
504 fclose(disc_fp);
506 return 0;