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];
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
)
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
)
59 offset
= partition_data_offset
+ 0x8000 * blockno
;
60 partition_raw_read(offset
, raw
, 0x8000);
63 memcpy(iv
, raw
+ 0x3d0, 16);
64 aes_cbc_dec(disc_key
, iv
, raw
+ 0x400, 0x7c00, block
);
68 aes_cbc_dec(disc_key
, iv
, raw
, 0x400, raw
);
74 b2
= (blockno
>> 3) & 7;
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",
89 if (memcmp(h1
+ 20*b1
, h
, 20)) {
90 fprintf(stderr
, "H1 mismatch for %llx\n", blockno
);
96 if (memcmp(h2
+ 20*b2
, h
, 20)) {
97 fprintf(stderr
, "H2 mismatch for %llx\n", blockno
);
103 if (memcmp(h3
+ 20*b3
, h
, 20)) {
104 fprintf(stderr
, "H3 mismatch for %llx\n", blockno
);
109 static void partition_read(u64 offset
, u8
*data
, u32 len
)
115 if (just_a_partition
)
116 disc_read(offset
, data
, len
);
118 offset_in_block
= offset
% 0x7c00;
119 len_in_block
= 0x7c00 - offset_in_block
;
120 if (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
;
130 static void spinner(u64 x
, u64 max
)
133 static time_t start_time
;
134 static u32 expected_total
;
140 start_time
= time(0);
141 expected_total
= 300;
145 fprintf(stderr
, "Done. \n");
149 d
= time(0) - start_time
;
152 expected_total
= (15 * expected_total
+ d
* max
/ x
) / 16;
154 if (expected_total
> d
)
155 d
= expected_total
- d
;
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
);
169 static void do_data(u64 size
)
177 size
= (size
/ 0x8000) * 0x7c00;
179 fp
= fopen("###dat###", "wb");
181 fatal("cannot open partition output file");
183 fprintf(stderr
, "\nDumping partition contents...\n");
185 remaining_size
= size
;
186 while (remaining_size
) {
187 spinner(offset
, size
);
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
;
205 static void copy_file(const char *name
, u64 offset
, u64 size
)
211 fp
= fopen(name
, "wb");
213 fatal("cannot open output file");
216 block_size
= sizeof data
;
217 if (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
;
231 static void do_yaz0(u8
*in
, u32 in_size
, u8
*out
, u32 out_size
)
240 for (nout
= 0; nout
< out_size
; ) {
246 if ((bits
& 0x80) != 0) {
252 d
|= (n
<< 8) & 0xf00;
259 for (i
= 0; i
< n
; i
++) {
271 static void do_fst_file(const char *name
, u64 offset
, u64 size
)
276 if (size
> max_size_to_auto_analyse
) {
277 copy_file(name
, offset
, size
);
285 partition_read(offset
, data
, size
);
287 if (uncompress_yaz0
&& size
>= 8 && memcmp(data
, "Yaz0", 4) == 0) {
291 fprintf(stderr
, " [Yaz0]");
293 dec_size
= be32(data
+ 4);
294 dec
= malloc(dec_size
);
298 do_yaz0(data
, size
, dec
, dec_size
);
305 if (unpack_rarc
&& size
>= 8 && memcmp(data
, "RARC", 4) == 0) {
306 fprintf(stderr
, " [RARC]");
309 fp
= fopen(name
, "wb");
311 fatal("cannot open output file");
312 if (fwrite(data
, size
, 1, fp
) != 1)
313 fatal("error writing output file");
319 static u32
do_fst(u8
*fst
, const char *names
, u32 i
, char *indent
, int is_last
)
327 name
= names
+ (be32(fst
+ 12*i
) & 0x00ffffff);
328 size
= be32(fst
+ 12*i
+ 8);
331 fprintf(stderr
, "/\n");
333 for (j
= 1; j
< size
; )
334 j
= do_fst(fst
, names
, j
, indent
, (j
== size
- 1));
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
);
347 if (mkdir(name
, 0777))
352 fprintf(stderr
, "\n");
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;
369 offset
= be34(fst
+ 12*i
+ 4);
370 do_fst_file(name
, offset
, size
);
372 fprintf(stderr
, "\n");
378 static void do_files(void)
380 u8 b
[0x480]; // XXX: determine actual header size
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
);
410 partition_read(fst_offset
, fst
, fst_size
);
411 n_files
= be32(fst
+ 8);
413 fprintf(stderr
, "%d entries\n", n_files
);
417 do_fst(fst
, (char *)fst
+ 12*n_files
, 0, indent
, 0);
422 static void do_partition(void)
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");
467 fatal("chdir partition");
469 if (dump_partition_data
)
470 do_data(partition_data_size
);
475 fatal("chdir up out of partition");
478 static void do_disc(void)
481 u64 partition_offset
[32]; // XXX: don't know the real maximum
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
];
511 //break; // XXX SII: for testing
515 int main(int argc
, char **argv
)
518 fprintf(stderr
, "Usage: %s <disc file>\n", argv
[0]);
522 disc_fp
= fopen(argv
[1], "rb");
524 fatal("open disc file");
526 if (just_a_partition
)
534 fprintf(stderr
, "\n\nErrors detected:\n");
536 fprintf(stderr
, "H0 mismatch\n");
538 fprintf(stderr
, "H1 mismatch\n");
540 fprintf(stderr
, "H2 mismatch\n");
542 fprintf(stderr
, "H3 mismatch\n");