Split some common code into a separate "tools" file
[svpe-tools.git] / negentig.c
blob90735a1fbb99a0404a835c0ec7618b14701a0d89
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 static u32 errors = 0;
31 static void seek(u64 offset)
33 if (fseeko(disc_fp, offset, SEEK_SET))
34 fatal("error seeking in disc file");
37 static void disc_read(u64 offset, u8 *data, u32 len)
39 seek(offset);
40 if (fread(data, len, 1, disc_fp) != 1)
41 fatal("error reading disc");
44 static void partition_raw_read(u64 offset, u8 *data, u32 len)
46 disc_read(partition_raw_offset + offset, data, len);
49 static void partition_read_block(u64 blockno, u8 *block)
51 u8 raw[0x8000];
52 u8 iv[16];
53 u8 h[20];
54 u8 *h0, *h1, *h2;
55 u32 b1, b2, b3;
56 u64 offset;
57 u32 i;
59 offset = partition_data_offset + 0x8000 * blockno;
60 partition_raw_read(offset, raw, 0x8000);
62 // decrypt data
63 memcpy(iv, raw + 0x3d0, 16);
64 aes_cbc_dec(disc_key, iv, raw + 0x400, 0x7c00, block);
66 // decrypt hashes
67 memset(iv, 0, 16);
68 aes_cbc_dec(disc_key, iv, raw, 0x400, raw);
70 h0 = raw;
71 h1 = raw + 0x280;
72 h2 = raw + 0x340;
73 b1 = blockno & 7;
74 b2 = (blockno >> 3) & 7;
75 b3 = blockno >> 6;
77 // check H0s
78 for (i = 0; i < 31; i++) {
79 SHA1(block + 0x400*i, 0x400, h);
80 if (memcmp(h0 + 20*i, h, 20)) {
81 fprintf(stderr, "H0 mismatch for %llx.%02x\n",
82 blockno, i);
83 errors |= 1;
87 // check H1
88 SHA1(h0, 620, h);
89 if (memcmp(h1 + 20*b1, h, 20)) {
90 fprintf(stderr, "H1 mismatch for %llx\n", blockno);
91 errors |= 2;
94 // check H2
95 SHA1(h1, 160, h);
96 if (memcmp(h2 + 20*b2, h, 20)) {
97 fprintf(stderr, "H2 mismatch for %llx\n", blockno);
98 errors |= 4;
101 // check H3
102 SHA1(h2, 160, h);
103 if (memcmp(h3 + 20*b3, h, 20)) {
104 fprintf(stderr, "H3 mismatch for %llx\n", blockno);
105 errors |= 8;
109 static void partition_read(u64 offset, u8 *data, u32 len)
111 u8 block[0x8000];
112 u32 offset_in_block;
113 u32 len_in_block;
115 if (just_a_partition)
116 disc_read(offset, data, len);
117 else while(len) {
118 offset_in_block = offset % 0x7c00;
119 len_in_block = 0x7c00 - offset_in_block;
120 if (len_in_block > len)
121 len_in_block = len;
122 partition_read_block(offset / 0x7c00, block);
123 memcpy(data, block + offset_in_block, len_in_block);
124 data += len_in_block;
125 offset += len_in_block;
126 len -= len_in_block;
130 static void spinner(u64 x, u64 max)
132 static u32 spin;
133 static time_t start_time;
134 static u32 expected_total;
135 u32 d;
136 double percent;
137 u32 h, m, s;
139 if (x == 0) {
140 start_time = time(0);
141 expected_total = 300;
144 if (x == max) {
145 fprintf(stderr, "Done. \n");
146 return;
149 d = time(0) - start_time;
151 if (d != 0)
152 expected_total = (15 * expected_total + d * max / x) / 16;
154 if (expected_total > d)
155 d = expected_total - d;
156 else
157 d = 0;
159 h = d / 3600;
160 m = (d / 60) % 60;
161 s = d % 60;
162 percent = 100.0 * x / max;
164 fprintf(stderr, "%5.2f%% (%c) ETA: %d:%02d:%02d \r",
165 percent, "/|\\-"[(spin++ / 64) % 4], h, m, s);
166 fflush(stderr);
169 static void do_data(u64 size)
171 u8 data[0x7c00];
172 u64 offset;
173 u64 remaining_size;
174 u32 block_size;
175 FILE *fp;
177 size = (size / 0x8000) * 0x7c00;
179 fp = fopen("###dat###", "wb");
180 if (fp == 0)
181 fatal("cannot open partition output file");
183 fprintf(stderr, "\nDumping partition contents...\n");
184 offset = 0;
185 remaining_size = size;
186 while (remaining_size) {
187 spinner(offset, size);
189 block_size = 0x7c00;
190 if (block_size > remaining_size)
191 block_size = remaining_size;
193 partition_read(offset, data, block_size);
194 if (fwrite(data, block_size, 1, fp) != 1)
195 fatal("error writing partition");
197 offset += block_size;
198 remaining_size -= block_size;
200 spinner(0, 0);
202 fclose(fp);
205 static void copy_file(const char *name, u64 offset, u64 size)
207 u8 data[0x80000];
208 FILE *fp;
209 u32 block_size;
211 fp = fopen(name, "wb");
212 if (fp == 0)
213 fatal("cannot open output file");
215 while (size) {
216 block_size = sizeof data;
217 if (block_size > size)
218 block_size = size;
220 partition_read(offset, data, block_size);
221 if (fwrite(data, block_size, 1, fp) != 1)
222 fatal("error writing output file");
224 offset += block_size;
225 size -= block_size;
228 fclose(fp);
231 static void do_yaz0(u8 *in, u32 in_size, u8 *out, u32 out_size)
233 u32 nout;
234 u8 bits;
235 u32 nbits;
236 u32 n, d, i;
238 nbits = 0;
239 in += 0x10;
240 for (nout = 0; nout < out_size; ) {
241 if (nbits == 0) {
242 bits = *in++;
243 nbits = 8;
246 if ((bits & 0x80) != 0) {
247 *out++ = *in++;
248 nout++;
249 } else {
250 n = *in++;
251 d = *in++;
252 d |= (n << 8) & 0xf00;
253 n >>= 4;
254 if (n == 0)
255 n = 0x10 + *in++;
256 n += 2;
257 d++;
259 for (i = 0; i < n; i++) {
260 *out = *(out - d);
261 out++;
263 nout += n;
266 nbits--;
267 bits <<= 1;
271 static void do_fst_file(const char *name, u64 offset, u64 size)
273 FILE *fp;
274 u8 *data;
276 if (size > max_size_to_auto_analyse) {
277 copy_file(name, offset, size);
279 return;
282 data = malloc(size);
283 if (data == 0)
284 fatal("malloc");
285 partition_read(offset, data, size);
287 if (uncompress_yaz0 && size >= 8 && memcmp(data, "Yaz0", 4) == 0) {
288 u8 *dec;
289 u32 dec_size;
291 fprintf(stderr, " [Yaz0]");
293 dec_size = be32(data + 4);
294 dec = malloc(dec_size);
295 if (dec == 0)
296 fatal("malloc");
298 do_yaz0(data, size, dec, dec_size);
300 free(data);
301 data = dec;
302 size = dec_size;
305 if (unpack_rarc && size >= 8 && memcmp(data, "RARC", 4) == 0) {
306 fprintf(stderr, " [RARC]");
309 fp = fopen(name, "wb");
310 if (fp == 0)
311 fatal("cannot open output file");
312 if (fwrite(data, size, 1, fp) != 1)
313 fatal("error writing output file");
314 fclose(fp);
316 free(data);
319 static u32 do_fst(u8 *fst, const char *names, u32 i, char *indent, int is_last)
321 u64 offset;
322 u32 size;
323 const char *name;
324 u32 parent;
325 u32 j;
327 name = names + (be32(fst + 12*i) & 0x00ffffff);
328 size = be32(fst + 12*i + 8);
330 if (i == 0) {
331 fprintf(stderr, "/\n");
333 for (j = 1; j < size; )
334 j = do_fst(fst, names, j, indent, (j == size - 1));
336 return size;
339 if (fst[12*i]) {
340 parent = be32(fst + 12*i + 4);
341 is_last = (be32(fst + 12*parent + 8) == size);
344 fprintf(stderr, "%s%c-- %s", indent, "|+"[is_last], name);
346 if (fst[12*i]) {
347 if (mkdir(name, 0777))
348 fatal("mkdir");
349 if (chdir(name))
350 fatal("chdir");
352 fprintf(stderr, "\n");
354 if (is_last)
355 strcat(indent, " ");
356 else
357 strcat(indent, "| ");
359 for (j = i + 1; j < size; )
360 j = do_fst(fst, names, j, indent, (j == size - 1));
362 indent[strlen(indent) - 4] = 0;
364 if (chdir(".."))
365 fatal("chdir up");
367 return size;
368 } else {
369 offset = be34(fst + 12*i + 4);
370 do_fst_file(name, offset, size);
372 fprintf(stderr, "\n");
374 return i + 1;
378 static void do_files(void)
380 u8 b[0x480]; // XXX: determine actual header size
381 u64 dol_offset;
382 u64 fst_offset;
383 u32 fst_size;
384 u8 *fst;
385 char indent[999];
386 u32 n_files;
388 partition_read(0, b, sizeof b);
390 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
391 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
392 fprintf(stderr, "Name: %s\n", b + 0x20);
393 fprintf(stderr, "\n");
395 dol_offset = be34(b + 0x0420);
396 fst_offset = be34(b + 0x0424);
397 fst_size = be34(b + 0x0428);
399 fprintf(stderr, "\tDOL @ %09llx\n", dol_offset);
400 fprintf(stderr, "\tFST @ %09llx (size %08x)\n", fst_offset, fst_size);
402 copy_file("###apl###", 0x2440, dol_offset - 0x2440);
403 // XXX: wrong way to get this size, there is a header
404 copy_file("###dol###", dol_offset, fst_offset - dol_offset);
405 // XXX: similar, perhaps
407 fst = malloc(fst_size);
408 if (fst == 0)
409 fatal("malloc fst");
410 partition_read(fst_offset, fst, fst_size);
411 n_files = be32(fst + 8);
413 fprintf(stderr, "%d entries\n", n_files);
415 indent[0] = 0;
416 if (n_files > 1)
417 do_fst(fst, (char *)fst + 12*n_files, 0, indent, 0);
419 free(fst);
422 static void do_partition(void)
424 u8 b[0x02c0];
425 u64 title_id;
426 u32 tmd_offset;
427 u64 tmd_size;
428 u32 cert_size;
429 u64 cert_offset;
430 u64 h3_offset;
431 char dirname[] = "title-0000000000000000";
433 partition_raw_read(0, b, 0x02c0);
435 decrypt_title_key(b + 0x01bf, b + 0x01dc);
437 title_id = be64(b + 0x01dc);
439 fprintf(stderr, "\ttitle id = %016llx\n", title_id);
441 // XXX: we should check the cert chain here, and read the tmd
443 tmd_offset = be32(b + 0x02a4);
444 tmd_size = be34(b + 0x02a8);
445 cert_size = be32(b + 0x02ac);
446 cert_offset = be34(b + 0x02b0);
447 h3_offset = be34(b + 0x02b4);
448 partition_data_offset = be34(b + 0x02b8);
449 partition_data_size = be34(b + 0x02bc);
451 fprintf(stderr, "\ttmd offset = %08x\n", tmd_offset);
452 fprintf(stderr, "\ttmd size = %09llx\n", tmd_size);
453 fprintf(stderr, "\tcert size = %08x\n", cert_size);
454 fprintf(stderr, "\tcert offset = %09llx\n", cert_offset);
455 fprintf(stderr, "\tdata offset = %09llx\n", partition_data_offset);
456 fprintf(stderr, "\tdata size = %09llx\n", partition_data_size);
458 partition_raw_read(h3_offset, h3, 0x18000);
460 // XXX: check h3 against h4 here
462 snprintf(dirname, sizeof dirname, "%016llx", title_id);
464 if (mkdir(dirname, 0777))
465 fatal("mkdir partition");
466 if (chdir(dirname))
467 fatal("chdir partition");
469 if (dump_partition_data)
470 do_data(partition_data_size);
472 do_files();
474 if (chdir(".."))
475 fatal("chdir up out of partition");
478 static void do_disc(void)
480 u8 b[0x100];
481 u64 partition_offset[32]; // XXX: don't know the real maximum
482 u32 n_partitions;
483 u32 i;
485 disc_read(0, b, sizeof b);
487 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
488 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
489 fprintf(stderr, "Name: %s\n", b + 0x20);
490 fprintf(stderr, "\n");
492 disc_read(0x40000, b, sizeof b);
493 n_partitions = be32(b);
495 disc_read(be34(b + 4), b, sizeof b);
496 for (i = 0; i < n_partitions; i++)
497 partition_offset[i] = be34(b + 8 * i);
499 fprintf(stderr, "%d partitions:\n", n_partitions);
500 for (i = 0; i < n_partitions; i++)
501 fprintf(stderr, "\tpartition #%d @ %09llx\n", i,
502 partition_offset[i]);
504 for (i = 0; i < n_partitions; i++) {
505 fprintf(stderr, "\nDoing partition %d...\n", i);
506 fprintf(stderr, "--------------------------------\n");
508 partition_raw_offset = partition_offset[i];
509 do_partition();
511 //break; // XXX SII: for testing
515 int main(int argc, char **argv)
517 if (argc != 2) {
518 fprintf(stderr, "Usage: %s <disc file>\n", argv[0]);
519 return 1;
522 disc_fp = fopen(argv[1], "rb");
523 if (disc_fp == 0)
524 fatal("open disc file");
526 if (just_a_partition)
527 do_files();
528 else
529 do_disc();
531 fclose(disc_fp);
533 if (errors)
534 fprintf(stderr, "\n\nErrors detected:\n");
535 if (errors & 1)
536 fprintf(stderr, "H0 mismatch\n");
537 if (errors & 2)
538 fprintf(stderr, "H1 mismatch\n");
539 if (errors & 4)
540 fprintf(stderr, "H2 mismatch\n");
541 if (errors & 8)
542 fprintf(stderr, "H3 mismatch\n");
544 return 0;