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>
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;
24 static u64 partition_raw_offset
;
25 static u64 partition_data_offset
;
26 static u64 partition_data_size
;
27 static u8 h3
[0x18000];
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
)
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
)
61 offset
= partition_data_offset
+ 0x8000 * blockno
;
62 partition_raw_read(offset
, raw
, 0x8000);
65 memcpy(iv
, raw
+ 0x3d0, 16);
66 aes_cbc_dec(disc_key
, iv
, raw
+ 0x400, 0x7c00, block
);
70 aes_cbc_dec(disc_key
, iv
, raw
, 0x400, raw
);
76 b2
= (blockno
>> 3) & 7;
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",
91 if (memcmp(h1
+ 20*b1
, h
, 20)) {
92 fprintf(stderr
, "H1 mismatch for %llx\n", blockno
);
98 if (memcmp(h2
+ 20*b2
, h
, 20)) {
99 fprintf(stderr
, "H2 mismatch for %llx\n", blockno
);
105 if (memcmp(h3
+ 20*b3
, h
, 20)) {
106 fprintf(stderr
, "H3 mismatch for %llx\n", blockno
);
111 static void partition_read(u64 offset
, u8
*data
, u32 len
)
117 if (just_a_partition
)
118 disc_read(offset
, data
, len
);
120 offset_in_block
= offset
% 0x7c00;
121 len_in_block
= 0x7c00 - offset_in_block
;
122 if (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
;
132 static void spinner(u64 x
, u64 max
)
135 static time_t start_time
;
136 static u32 expected_total
;
142 start_time
= time(0);
143 expected_total
= 300;
147 fprintf(stderr
, "Done. \n");
151 d
= time(0) - start_time
;
154 expected_total
= (15 * expected_total
+ d
* max
/ x
) / 16;
156 if (expected_total
> d
)
157 d
= expected_total
- d
;
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
);
171 static void do_data(u64 size
)
179 size
= (size
/ 0x8000) * 0x7c00;
181 fp
= fopen("###dat###", "wb");
183 fatal("cannot open partition output file");
185 fprintf(stderr
, "\nDumping partition contents...\n");
187 remaining_size
= size
;
188 while (remaining_size
) {
189 spinner(offset
, size
);
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
;
207 static void copy_file(const char *name
, u64 offset
, u64 size
)
213 fp
= fopen(name
, "wb");
215 fatal("cannot open output file");
218 block_size
= sizeof data
;
219 if (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
;
233 static void do_yaz0(u8
*in
, u32 in_size
, u8
*out
, u32 out_size
)
242 for (nout
= 0; nout
< out_size
; ) {
248 if ((bits
& 0x80) != 0) {
254 d
|= (n
<< 8) & 0xf00;
261 for (i
= 0; i
< n
; i
++) {
273 static void do_fst_file(const char *name
, u64 offset
, u64 size
)
278 if (size
> max_size_to_auto_analyse
) {
279 copy_file(name
, offset
, size
);
287 partition_read(offset
, data
, size
);
289 if (uncompress_yaz0
&& size
>= 8 && memcmp(data
, "Yaz0", 4) == 0) {
293 fprintf(stderr
, " [Yaz0]");
295 dec_size
= be32(data
+ 4);
296 dec
= malloc(dec_size
);
300 do_yaz0(data
, size
, dec
, dec_size
);
307 if (unpack_rarc
&& size
>= 8 && memcmp(data
, "RARC", 4) == 0) {
308 fprintf(stderr
, " [RARC]");
311 fp
= fopen(name
, "wb");
313 fatal("cannot open output file");
314 if (fwrite(data
, size
, 1, fp
) != 1)
315 fatal("error writing output file");
321 static u32
do_fst(u8
*fst
, const char *names
, u32 i
, char *indent
, int is_last
)
329 name
= names
+ (be32(fst
+ 12*i
) & 0x00ffffff);
330 size
= be32(fst
+ 12*i
+ 8);
333 fprintf(stderr
, "/\n");
335 for (j
= 1; j
< size
; )
336 j
= do_fst(fst
, names
, j
, indent
, (j
== size
- 1));
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
);
349 if (mkdir(name
, 0777))
354 fprintf(stderr
, "\n");
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;
371 offset
= be34(fst
+ 12*i
+ 4);
372 do_fst_file(name
, offset
, size
);
374 fprintf(stderr
, "\n");
380 static void do_files(void)
382 u8 b
[0x480]; // XXX: determine actual header size
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
);
412 partition_read(fst_offset
, fst
, fst_size
);
413 n_files
= be32(fst
+ 8);
415 fprintf(stderr
, "%d entries\n", n_files
);
419 do_fst(fst
, (char *)fst
+ 12*n_files
, 0, indent
, 0);
424 static void do_partition(void)
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");
469 fatal("chdir partition");
471 if (dump_partition_data
)
472 do_data(partition_data_size
);
477 fatal("chdir up out of partition");
480 static void do_disc(void)
483 u64 partition_offset
[32]; // XXX: don't know the real maximum
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
];
513 //break; // XXX SII: for testing
517 int main(int argc
, char **argv
)
520 fprintf(stderr
, "Usage: %s <disc file>\n", argv
[0]);
524 disc_fp
= fopen(argv
[1], "rb");
526 fatal("open disc file");
528 if (just_a_partition
)
536 fprintf(stderr
, "\n\nErrors detected:\n");
538 fprintf(stderr
, "H0 mismatch\n");
540 fprintf(stderr
, "H1 mismatch\n");
542 fprintf(stderr
, "H2 mismatch\n");
544 fprintf(stderr
, "H3 mismatch\n");