twintig: Be less verbose by default
[svpe-tools.git] / negentig.c
blobbd84f0e3dac9d4de6ed10cd9ae9612f643f3d5cc
1 // Copyright 2007,2008 Segher Boessenkool <segher@kernel.crashing.org>
2 // Licensed under the terms of the GNU GPL, version 2
3 // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
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 #include "tools.h"
15 static int just_a_partition = 0;
16 static int dump_partition_data = 0;
17 static u32 max_size_to_auto_analyse = 0x1000000;
18 static int uncompress_yaz0 = 1;
19 static int unpack_rarc = 1;
21 static FILE *disc_fp;
23 static u64 partition_raw_offset;
24 static u64 partition_data_offset;
25 static u64 partition_data_size;
26 static u8 h3[0x18000];
28 u8 disc_key[16];
30 static u32 errors = 0;
32 static void seek(u64 offset)
34 if (fseeko(disc_fp, offset, SEEK_SET))
35 fatal("error seeking in disc file");
38 static void disc_read(u64 offset, u8 *data, u32 len)
40 seek(offset);
41 if (fread(data, len, 1, disc_fp) != 1)
42 fatal("error reading disc");
45 static void partition_raw_read(u64 offset, u8 *data, u32 len)
47 disc_read(partition_raw_offset + offset, data, len);
50 static void partition_read_block(u64 blockno, u8 *block)
52 u8 raw[0x8000];
53 u8 iv[16];
54 u8 h[20];
55 u8 *h0, *h1, *h2;
56 u32 b1, b2, b3;
57 u64 offset;
58 u32 i;
60 offset = partition_data_offset + 0x8000 * blockno;
61 partition_raw_read(offset, raw, 0x8000);
63 // decrypt data
64 memcpy(iv, raw + 0x3d0, 16);
65 aes_cbc_dec(disc_key, iv, raw + 0x400, 0x7c00, block);
67 // decrypt hashes
68 memset(iv, 0, 16);
69 aes_cbc_dec(disc_key, iv, raw, 0x400, raw);
71 h0 = raw;
72 h1 = raw + 0x280;
73 h2 = raw + 0x340;
74 b1 = blockno & 7;
75 b2 = (blockno >> 3) & 7;
76 b3 = blockno >> 6;
78 // check H0s
79 for (i = 0; i < 31; i++) {
80 sha(block + 0x400*i, 0x400, h);
81 if (memcmp(h0 + 20*i, h, 20)) {
82 fprintf(stderr, "H0 mismatch for %llx.%02x\n",
83 blockno, i);
84 errors |= 1;
88 // check H1
89 sha(h0, 620, h);
90 if (memcmp(h1 + 20*b1, h, 20)) {
91 fprintf(stderr, "H1 mismatch for %llx\n", blockno);
92 errors |= 2;
95 // check H2
96 sha(h1, 160, h);
97 if (memcmp(h2 + 20*b2, h, 20)) {
98 fprintf(stderr, "H2 mismatch for %llx\n", blockno);
99 errors |= 4;
102 // check H3
103 sha(h2, 160, h);
104 if (memcmp(h3 + 20*b3, h, 20)) {
105 fprintf(stderr, "H3 mismatch for %llx\n", blockno);
106 errors |= 8;
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 if (just_a_partition)
117 disc_read(offset, data, len);
118 else while(len) {
119 offset_in_block = offset % 0x7c00;
120 len_in_block = 0x7c00 - offset_in_block;
121 if (len_in_block > len)
122 len_in_block = len;
123 partition_read_block(offset / 0x7c00, block);
124 memcpy(data, block + offset_in_block, len_in_block);
125 data += len_in_block;
126 offset += len_in_block;
127 len -= len_in_block;
131 static void spinner(u64 x, u64 max)
133 static u32 spin;
134 static time_t start_time;
135 static u32 expected_total;
136 u32 d;
137 double percent;
138 u32 h, m, s;
140 if (x == 0) {
141 start_time = time(0);
142 expected_total = 300;
145 if (x == max) {
146 fprintf(stderr, "Done. \n");
147 return;
150 d = time(0) - start_time;
152 if (d != 0)
153 expected_total = (15 * expected_total + d * max / x) / 16;
155 if (expected_total > d)
156 d = expected_total - d;
157 else
158 d = 0;
160 h = d / 3600;
161 m = (d / 60) % 60;
162 s = d % 60;
163 percent = 100.0 * x / max;
165 fprintf(stderr, "%5.2f%% (%c) ETA: %d:%02d:%02d \r",
166 percent, "/|\\-"[(spin++ / 64) % 4], h, m, s);
167 fflush(stderr);
170 static void do_data(u64 size)
172 u8 data[0x7c00];
173 u64 offset;
174 u64 remaining_size;
175 u32 block_size;
176 FILE *fp;
178 size = (size / 0x8000) * 0x7c00;
180 fp = fopen("###dat###", "wb");
181 if (fp == 0)
182 fatal("cannot open partition output file");
184 fprintf(stderr, "\nDumping partition contents...\n");
185 offset = 0;
186 remaining_size = size;
187 while (remaining_size) {
188 spinner(offset, size);
190 block_size = 0x7c00;
191 if (block_size > remaining_size)
192 block_size = remaining_size;
194 partition_read(offset, data, block_size);
195 if (fwrite(data, block_size, 1, fp) != 1)
196 fatal("error writing partition");
198 offset += block_size;
199 remaining_size -= block_size;
201 spinner(0, 0);
203 fclose(fp);
206 static void copy_file(const char *name, u64 offset, u64 size)
208 u8 data[0x80000];
209 FILE *fp;
210 u32 block_size;
212 fp = fopen(name, "wb");
213 if (fp == 0)
214 fatal("cannot open output file");
216 while (size) {
217 block_size = sizeof data;
218 if (block_size > size)
219 block_size = size;
221 partition_read(offset, data, block_size);
222 if (fwrite(data, block_size, 1, fp) != 1)
223 fatal("error writing output file");
225 offset += block_size;
226 size -= block_size;
229 fclose(fp);
232 static void do_fst_file(const char *name, u64 offset, u64 size)
234 FILE *fp;
235 u8 *data;
237 if (size > max_size_to_auto_analyse) {
238 copy_file(name, offset, size);
240 return;
243 data = malloc(size);
244 if (data == 0)
245 fatal("malloc");
246 partition_read(offset, data, size);
248 if (uncompress_yaz0 && size >= 8 && memcmp(data, "Yaz0", 4) == 0) {
249 u8 *dec;
250 u32 dec_size;
252 fprintf(stderr, " [Yaz0]");
254 dec_size = be32(data + 4);
255 dec = malloc(dec_size);
256 if (dec == 0)
257 fatal("malloc");
259 do_yaz0(data, size, dec, dec_size);
261 free(data);
262 data = dec;
263 size = dec_size;
266 if (unpack_rarc && size >= 8 && memcmp(data, "RARC", 4) == 0) {
267 fprintf(stderr, " [RARC]");
270 fp = fopen(name, "wb");
271 if (fp == 0)
272 fatal("cannot open output file");
273 if (size && fwrite(data, size, 1, fp) != 1)
274 fatal("error writing output file");
275 fclose(fp);
277 free(data);
280 static u32 do_fst(u8 *fst, const char *names, u32 i, char *indent, int is_last)
282 u64 offset;
283 u32 size;
284 const char *name;
285 u32 parent;
286 u32 j;
288 name = names + (be32(fst + 12*i) & 0x00ffffff);
289 size = be32(fst + 12*i + 8);
291 if (i == 0) {
292 fprintf(stderr, "/\n");
294 for (j = 1; j < size; )
295 j = do_fst(fst, names, j, indent, (j == size - 1));
297 return size;
300 if (fst[12*i]) {
301 parent = be32(fst + 12*i + 4);
302 is_last = (be32(fst + 12*parent + 8) == size);
305 fprintf(stderr, "%s%c-- %s", indent, "|+"[is_last], name);
307 if (fst[12*i]) {
308 if (mkdir(name, 0777))
309 fatal("mkdir");
310 if (chdir(name))
311 fatal("chdir");
313 fprintf(stderr, "\n");
315 if (is_last)
316 strcat(indent, " ");
317 else
318 strcat(indent, "| ");
320 for (j = i + 1; j < size; )
321 j = do_fst(fst, names, j, indent, (j == size - 1));
323 indent[strlen(indent) - 4] = 0;
325 if (chdir(".."))
326 fatal("chdir up");
328 return size;
329 } else {
330 offset = be34(fst + 12*i + 4);
331 do_fst_file(name, offset, size);
333 fprintf(stderr, "\n");
335 return i + 1;
339 static void do_files(void)
341 u8 b[0x480]; // XXX: determine actual header size
342 u64 dol_offset;
343 u64 fst_offset;
344 u32 fst_size;
345 u64 apl_offset;
346 u32 apl_size;
347 u8 apl_header[0x20];
348 u8 *fst;
349 char indent[999];
350 u32 n_files;
352 partition_read(0, b, sizeof b);
354 fprintf(stderr, "\n");
355 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
356 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
357 fprintf(stderr, "Name: %s\n", b + 0x20);
358 fprintf(stderr, "\n");
360 dol_offset = be34(b + 0x0420);
361 fst_offset = be34(b + 0x0424);
362 fst_size = be34(b + 0x0428);
364 apl_offset = 0x2440;
365 partition_read(apl_offset, apl_header, sizeof apl_header);
366 apl_size = 0x20 + be32(apl_header + 0x14) + be32(apl_header + 0x18);
368 fprintf(stderr, "\tDOL @ %09llx\n", dol_offset);
369 fprintf(stderr, "\tFST @ %09llx (size %08x)\n", fst_offset, fst_size);
370 fprintf(stderr, "\tAPL @ %09llx (size %08x)\n", apl_offset, apl_size);
372 copy_file("###apl###", apl_offset, apl_size);
373 copy_file("###dol###", dol_offset, fst_offset - dol_offset);
374 // XXX: read the header for getting the size
376 fst = malloc(fst_size);
377 if (fst == 0)
378 fatal("malloc fst");
379 partition_read(fst_offset, fst, fst_size);
380 n_files = be32(fst + 8);
382 fprintf(stderr, "%d entries\n", n_files);
384 indent[0] = 0;
385 if (n_files > 1)
386 do_fst(fst, (char *)fst + 12*n_files, 0, indent, 0);
388 free(fst);
391 static void do_partition(void)
393 u8 tik[0x2a4];
394 u8 b[0x1c];
395 u64 title_id;
396 u64 tmd_offset;
397 u32 tmd_size;
398 u8 *tmd;
399 u64 cert_offset;
400 u32 cert_size;
401 u8 *cert;
402 u64 h3_offset;
403 u8 h[20];
404 u32 ret;
405 char dirname[17];
407 // read ticket, and read some offsets and sizes
408 partition_raw_read(0, tik, sizeof tik);
409 partition_raw_read(0x2a4, b, sizeof b);
411 tmd_size = be32(b);
412 tmd_offset = be34(b + 4);
413 cert_size = be32(b + 8);
414 cert_offset = be34(b + 0x0c);
415 h3_offset = be34(b + 0x10);
416 partition_data_offset = be34(b + 0x14);
417 partition_data_size = be34(b + 0x18);
419 title_id = be64(tik + 0x01dc);
420 fprintf(stderr, "\ttitle id = %016llx\n", title_id);
422 fprintf(stderr, "\ttmd size = %08x\n", tmd_size);
423 fprintf(stderr, "\ttmd offset = %09llx\n", tmd_offset);
424 fprintf(stderr, "\tcert size = %08x\n", cert_size);
425 fprintf(stderr, "\tcert offset = %09llx\n", cert_offset);
426 fprintf(stderr, "\tdata offset = %09llx\n", partition_data_offset);
427 fprintf(stderr, "\tdata size = %09llx\n", partition_data_size);
429 tmd = malloc(tmd_size);
430 if (tmd == 0)
431 fatal("malloc tmd");
432 partition_raw_read(tmd_offset, tmd, tmd_size);
434 cert = malloc(cert_size);
435 if (cert == 0)
436 fatal("malloc cert");
437 partition_raw_read(cert_offset, cert, cert_size);
439 ret = check_cert_chain(tik, sizeof tik, cert, cert_size);
440 if (ret) {
441 fprintf(stderr, "ticket cert failure (%d)\n", ret);
442 errors |= 0x20;
444 ret = check_cert_chain(tmd, tmd_size, cert, cert_size);
445 if (ret) {
446 fprintf(stderr, "tmd cert failure (%d)\n", ret);
447 errors |= 0x40;
450 decrypt_title_key(tik, disc_key);
452 partition_raw_read(h3_offset, h3, 0x18000);
454 sha(h3, 0x18000, h);
455 if (memcmp(tmd + 0x1f4, h, 20)) {
456 fprintf(stderr, "H4 mismatch\n");
457 errors |= 0x10;
460 free(cert);
461 free(tmd);
463 snprintf(dirname, sizeof dirname, "%016llx", title_id);
465 if (mkdir(dirname, 0777))
466 fatal("mkdir partition");
467 if (chdir(dirname))
468 fatal("chdir partition");
470 if (dump_partition_data)
471 do_data(partition_data_size);
473 do_files();
475 if (chdir(".."))
476 fatal("chdir up out of partition");
479 static void do_disc(void)
481 u8 b[0x100];
482 u64 partition_offset[32]; // XXX: don't know the real maximum
483 u32 n_partitions;
484 u32 i;
486 disc_read(0, b, sizeof b);
488 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
489 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
490 fprintf(stderr, "Name: %s\n", b + 0x20);
491 fprintf(stderr, "\n");
493 disc_read(0x40000, b, sizeof b);
494 n_partitions = be32(b);
496 disc_read(be34(b + 4), b, sizeof b);
497 for (i = 0; i < n_partitions; i++)
498 partition_offset[i] = be34(b + 8 * i);
500 fprintf(stderr, "%d partitions:\n", n_partitions);
501 for (i = 0; i < n_partitions; i++)
502 fprintf(stderr, "\tpartition #%d @ %09llx\n", i,
503 partition_offset[i]);
505 for (i = 0; i < n_partitions; i++) {
506 fprintf(stderr, "\nDoing partition %d...\n", i);
507 fprintf(stderr, "--------------------------------\n");
509 partition_raw_offset = partition_offset[i];
510 do_partition();
512 //break; // XXX SII: for testing
516 int main(int argc, char **argv)
518 if (argc != 2) {
519 fprintf(stderr, "Usage: %s <disc file>\n", argv[0]);
520 return 1;
523 disc_fp = fopen(argv[1], "rb");
524 if (disc_fp == 0)
525 fatal("open disc file");
527 if (just_a_partition)
528 do_files();
529 else
530 do_disc();
532 fclose(disc_fp);
534 if (errors)
535 fprintf(stderr, "\n\nErrors detected:\n");
536 if (errors & 1)
537 fprintf(stderr, "H0 mismatch\n");
538 if (errors & 2)
539 fprintf(stderr, "H1 mismatch\n");
540 if (errors & 4)
541 fprintf(stderr, "H2 mismatch\n");
542 if (errors & 8)
543 fprintf(stderr, "H3 mismatch\n");
544 if (errors & 0x10)
545 fprintf(stderr, "H4 mismatch\n");
546 if (errors & 0x20)
547 fprintf(stderr, "ticket cert failure\n");
548 if (errors & 0x40)
549 fprintf(stderr, "tmd cert failure\n");
551 return 0;