Lazy developer Makefile rules
[svpe-tools.git] / negentig.c
blob559845b82b63667b07907172ec6ddb546eeb4627
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 <openssl/sha.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <time.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdio.h>
14 #include "tools.h"
16 static int just_a_partition = 0;
17 static int dump_partition_data = 0;
18 static u32 max_size_to_auto_analyse = 0x1000000;
19 static int uncompress_yaz0 = 1;
20 static int unpack_rarc = 1;
22 static FILE *disc_fp;
24 static u64 partition_raw_offset;
25 static u64 partition_data_offset;
26 static u64 partition_data_size;
27 static u8 h3[0x18000];
29 u8 disc_key[16];
31 static u32 errors = 0;
33 static void seek(u64 offset)
35 if (fseeko(disc_fp, offset, SEEK_SET))
36 fatal("error seeking in disc file");
39 static void disc_read(u64 offset, u8 *data, u32 len)
41 seek(offset);
42 if (fread(data, len, 1, disc_fp) != 1)
43 fatal("error reading disc");
46 static void partition_raw_read(u64 offset, u8 *data, u32 len)
48 disc_read(partition_raw_offset + offset, data, len);
51 static void partition_read_block(u64 blockno, u8 *block)
53 u8 raw[0x8000];
54 u8 iv[16];
55 u8 h[20];
56 u8 *h0, *h1, *h2;
57 u32 b1, b2, b3;
58 u64 offset;
59 u32 i;
61 offset = partition_data_offset + 0x8000 * blockno;
62 partition_raw_read(offset, raw, 0x8000);
64 // decrypt data
65 memcpy(iv, raw + 0x3d0, 16);
66 aes_cbc_dec(disc_key, iv, raw + 0x400, 0x7c00, block);
68 // decrypt hashes
69 memset(iv, 0, 16);
70 aes_cbc_dec(disc_key, iv, raw, 0x400, raw);
72 h0 = raw;
73 h1 = raw + 0x280;
74 h2 = raw + 0x340;
75 b1 = blockno & 7;
76 b2 = (blockno >> 3) & 7;
77 b3 = blockno >> 6;
79 // check H0s
80 for (i = 0; i < 31; i++) {
81 SHA1(block + 0x400*i, 0x400, h);
82 if (memcmp(h0 + 20*i, h, 20)) {
83 fprintf(stderr, "H0 mismatch for %llx.%02x\n",
84 blockno, i);
85 errors |= 1;
89 // check H1
90 SHA1(h0, 620, h);
91 if (memcmp(h1 + 20*b1, h, 20)) {
92 fprintf(stderr, "H1 mismatch for %llx\n", blockno);
93 errors |= 2;
96 // check H2
97 SHA1(h1, 160, h);
98 if (memcmp(h2 + 20*b2, h, 20)) {
99 fprintf(stderr, "H2 mismatch for %llx\n", blockno);
100 errors |= 4;
103 // check H3
104 SHA1(h2, 160, h);
105 if (memcmp(h3 + 20*b3, h, 20)) {
106 fprintf(stderr, "H3 mismatch for %llx\n", blockno);
107 errors |= 8;
111 static void partition_read(u64 offset, u8 *data, u32 len)
113 u8 block[0x8000];
114 u32 offset_in_block;
115 u32 len_in_block;
117 if (just_a_partition)
118 disc_read(offset, data, len);
119 else while(len) {
120 offset_in_block = offset % 0x7c00;
121 len_in_block = 0x7c00 - offset_in_block;
122 if (len_in_block > len)
123 len_in_block = len;
124 partition_read_block(offset / 0x7c00, block);
125 memcpy(data, block + offset_in_block, len_in_block);
126 data += len_in_block;
127 offset += len_in_block;
128 len -= len_in_block;
132 static void spinner(u64 x, u64 max)
134 static u32 spin;
135 static time_t start_time;
136 static u32 expected_total;
137 u32 d;
138 double percent;
139 u32 h, m, s;
141 if (x == 0) {
142 start_time = time(0);
143 expected_total = 300;
146 if (x == max) {
147 fprintf(stderr, "Done. \n");
148 return;
151 d = time(0) - start_time;
153 if (d != 0)
154 expected_total = (15 * expected_total + d * max / x) / 16;
156 if (expected_total > d)
157 d = expected_total - d;
158 else
159 d = 0;
161 h = d / 3600;
162 m = (d / 60) % 60;
163 s = d % 60;
164 percent = 100.0 * x / max;
166 fprintf(stderr, "%5.2f%% (%c) ETA: %d:%02d:%02d \r",
167 percent, "/|\\-"[(spin++ / 64) % 4], h, m, s);
168 fflush(stderr);
171 static void do_data(u64 size)
173 u8 data[0x7c00];
174 u64 offset;
175 u64 remaining_size;
176 u32 block_size;
177 FILE *fp;
179 size = (size / 0x8000) * 0x7c00;
181 fp = fopen("###dat###", "wb");
182 if (fp == 0)
183 fatal("cannot open partition output file");
185 fprintf(stderr, "\nDumping partition contents...\n");
186 offset = 0;
187 remaining_size = size;
188 while (remaining_size) {
189 spinner(offset, size);
191 block_size = 0x7c00;
192 if (block_size > remaining_size)
193 block_size = remaining_size;
195 partition_read(offset, data, block_size);
196 if (fwrite(data, block_size, 1, fp) != 1)
197 fatal("error writing partition");
199 offset += block_size;
200 remaining_size -= block_size;
202 spinner(0, 0);
204 fclose(fp);
207 static void copy_file(const char *name, u64 offset, u64 size)
209 u8 data[0x80000];
210 FILE *fp;
211 u32 block_size;
213 fp = fopen(name, "wb");
214 if (fp == 0)
215 fatal("cannot open output file");
217 while (size) {
218 block_size = sizeof data;
219 if (block_size > size)
220 block_size = size;
222 partition_read(offset, data, block_size);
223 if (fwrite(data, block_size, 1, fp) != 1)
224 fatal("error writing output file");
226 offset += block_size;
227 size -= block_size;
230 fclose(fp);
233 static void do_yaz0(u8 *in, u32 in_size, u8 *out, u32 out_size)
235 u32 nout;
236 u8 bits;
237 u32 nbits;
238 u32 n, d, i;
240 nbits = 0;
241 in += 0x10;
242 for (nout = 0; nout < out_size; ) {
243 if (nbits == 0) {
244 bits = *in++;
245 nbits = 8;
248 if ((bits & 0x80) != 0) {
249 *out++ = *in++;
250 nout++;
251 } else {
252 n = *in++;
253 d = *in++;
254 d |= (n << 8) & 0xf00;
255 n >>= 4;
256 if (n == 0)
257 n = 0x10 + *in++;
258 n += 2;
259 d++;
261 for (i = 0; i < n; i++) {
262 *out = *(out - d);
263 out++;
265 nout += n;
268 nbits--;
269 bits <<= 1;
273 static void do_fst_file(const char *name, u64 offset, u64 size)
275 FILE *fp;
276 u8 *data;
278 if (size > max_size_to_auto_analyse) {
279 copy_file(name, offset, size);
281 return;
284 data = malloc(size);
285 if (data == 0)
286 fatal("malloc");
287 partition_read(offset, data, size);
289 if (uncompress_yaz0 && size >= 8 && memcmp(data, "Yaz0", 4) == 0) {
290 u8 *dec;
291 u32 dec_size;
293 fprintf(stderr, " [Yaz0]");
295 dec_size = be32(data + 4);
296 dec = malloc(dec_size);
297 if (dec == 0)
298 fatal("malloc");
300 do_yaz0(data, size, dec, dec_size);
302 free(data);
303 data = dec;
304 size = dec_size;
307 if (unpack_rarc && size >= 8 && memcmp(data, "RARC", 4) == 0) {
308 fprintf(stderr, " [RARC]");
311 fp = fopen(name, "wb");
312 if (fp == 0)
313 fatal("cannot open output file");
314 if (fwrite(data, size, 1, fp) != 1)
315 fatal("error writing output file");
316 fclose(fp);
318 free(data);
321 static u32 do_fst(u8 *fst, const char *names, u32 i, char *indent, int is_last)
323 u64 offset;
324 u32 size;
325 const char *name;
326 u32 parent;
327 u32 j;
329 name = names + (be32(fst + 12*i) & 0x00ffffff);
330 size = be32(fst + 12*i + 8);
332 if (i == 0) {
333 fprintf(stderr, "/\n");
335 for (j = 1; j < size; )
336 j = do_fst(fst, names, j, indent, (j == size - 1));
338 return size;
341 if (fst[12*i]) {
342 parent = be32(fst + 12*i + 4);
343 is_last = (be32(fst + 12*parent + 8) == size);
346 fprintf(stderr, "%s%c-- %s", indent, "|+"[is_last], name);
348 if (fst[12*i]) {
349 if (mkdir(name, 0777))
350 fatal("mkdir");
351 if (chdir(name))
352 fatal("chdir");
354 fprintf(stderr, "\n");
356 if (is_last)
357 strcat(indent, " ");
358 else
359 strcat(indent, "| ");
361 for (j = i + 1; j < size; )
362 j = do_fst(fst, names, j, indent, (j == size - 1));
364 indent[strlen(indent) - 4] = 0;
366 if (chdir(".."))
367 fatal("chdir up");
369 return size;
370 } else {
371 offset = be34(fst + 12*i + 4);
372 do_fst_file(name, offset, size);
374 fprintf(stderr, "\n");
376 return i + 1;
380 static void do_files(void)
382 u8 b[0x480]; // XXX: determine actual header size
383 u64 dol_offset;
384 u64 fst_offset;
385 u32 fst_size;
386 u8 *fst;
387 char indent[999];
388 u32 n_files;
390 partition_read(0, b, sizeof b);
392 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
393 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
394 fprintf(stderr, "Name: %s\n", b + 0x20);
395 fprintf(stderr, "\n");
397 dol_offset = be34(b + 0x0420);
398 fst_offset = be34(b + 0x0424);
399 fst_size = be34(b + 0x0428);
401 fprintf(stderr, "\tDOL @ %09llx\n", dol_offset);
402 fprintf(stderr, "\tFST @ %09llx (size %08x)\n", fst_offset, fst_size);
404 copy_file("###apl###", 0x2440, dol_offset - 0x2440);
405 // XXX: wrong way to get this size, there is a header
406 copy_file("###dol###", dol_offset, fst_offset - dol_offset);
407 // XXX: similar, perhaps
409 fst = malloc(fst_size);
410 if (fst == 0)
411 fatal("malloc fst");
412 partition_read(fst_offset, fst, fst_size);
413 n_files = be32(fst + 8);
415 fprintf(stderr, "%d entries\n", n_files);
417 indent[0] = 0;
418 if (n_files > 1)
419 do_fst(fst, (char *)fst + 12*n_files, 0, indent, 0);
421 free(fst);
424 static void do_partition(void)
426 u8 b[0x02c0];
427 u64 title_id;
428 u32 tmd_offset;
429 u64 tmd_size;
430 u32 cert_size;
431 u64 cert_offset;
432 u64 h3_offset;
433 char dirname[] = "title-0000000000000000";
435 partition_raw_read(0, b, 0x02c0);
437 decrypt_title_key(b + 0x01bf, b + 0x01dc, disc_key);
439 title_id = be64(b + 0x01dc);
441 fprintf(stderr, "\ttitle id = %016llx\n", title_id);
443 // XXX: we should check the cert chain here, and read the tmd
445 tmd_offset = be32(b + 0x02a4);
446 tmd_size = be34(b + 0x02a8);
447 cert_size = be32(b + 0x02ac);
448 cert_offset = be34(b + 0x02b0);
449 h3_offset = be34(b + 0x02b4);
450 partition_data_offset = be34(b + 0x02b8);
451 partition_data_size = be34(b + 0x02bc);
453 fprintf(stderr, "\ttmd offset = %08x\n", tmd_offset);
454 fprintf(stderr, "\ttmd size = %09llx\n", tmd_size);
455 fprintf(stderr, "\tcert size = %08x\n", cert_size);
456 fprintf(stderr, "\tcert offset = %09llx\n", cert_offset);
457 fprintf(stderr, "\tdata offset = %09llx\n", partition_data_offset);
458 fprintf(stderr, "\tdata size = %09llx\n", partition_data_size);
460 partition_raw_read(h3_offset, h3, 0x18000);
462 // XXX: check h3 against h4 here
464 snprintf(dirname, sizeof dirname, "%016llx", title_id);
466 if (mkdir(dirname, 0777))
467 fatal("mkdir partition");
468 if (chdir(dirname))
469 fatal("chdir partition");
471 if (dump_partition_data)
472 do_data(partition_data_size);
474 do_files();
476 if (chdir(".."))
477 fatal("chdir up out of partition");
480 static void do_disc(void)
482 u8 b[0x100];
483 u64 partition_offset[32]; // XXX: don't know the real maximum
484 u32 n_partitions;
485 u32 i;
487 disc_read(0, b, sizeof b);
489 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
490 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
491 fprintf(stderr, "Name: %s\n", b + 0x20);
492 fprintf(stderr, "\n");
494 disc_read(0x40000, b, sizeof b);
495 n_partitions = be32(b);
497 disc_read(be34(b + 4), b, sizeof b);
498 for (i = 0; i < n_partitions; i++)
499 partition_offset[i] = be34(b + 8 * i);
501 fprintf(stderr, "%d partitions:\n", n_partitions);
502 for (i = 0; i < n_partitions; i++)
503 fprintf(stderr, "\tpartition #%d @ %09llx\n", i,
504 partition_offset[i]);
506 for (i = 0; i < n_partitions; i++) {
507 fprintf(stderr, "\nDoing partition %d...\n", i);
508 fprintf(stderr, "--------------------------------\n");
510 partition_raw_offset = partition_offset[i];
511 do_partition();
513 //break; // XXX SII: for testing
517 int main(int argc, char **argv)
519 if (argc != 2) {
520 fprintf(stderr, "Usage: %s <disc file>\n", argv[0]);
521 return 1;
524 disc_fp = fopen(argv[1], "rb");
525 if (disc_fp == 0)
526 fatal("open disc file");
528 if (just_a_partition)
529 do_files();
530 else
531 do_disc();
533 fclose(disc_fp);
535 if (errors)
536 fprintf(stderr, "\n\nErrors detected:\n");
537 if (errors & 1)
538 fprintf(stderr, "H0 mismatch\n");
539 if (errors & 2)
540 fprintf(stderr, "H1 mismatch\n");
541 if (errors & 4)
542 fprintf(stderr, "H2 mismatch\n");
543 if (errors & 8)
544 fprintf(stderr, "H3 mismatch\n");
546 return 0;