1 // Copyright 2007 Segher Boessenkool <segher@kernel.crashing.org>
2 // Licensed under the terms of the GNU GPL, version 2
4 #include <openssl/aes.h>
13 static int dump_partition_data
= 0;
14 static int decompress_yaz0
= 1;
16 #define SPINNER_SPEED 64
18 typedef unsigned char u8
;
19 typedef unsigned short u16
;
20 typedef unsigned int u32
;
21 typedef unsigned long long u64
;
25 static u64 partition_raw_offset
;
26 static u64 partition_data_offset
;
27 static u64 partition_data_size
;
28 static u8 h3
[0x18000];
30 static u8 disc_key
[16];
32 static void print_bytes(u8
*x
, u32 n
)
36 for (i
= 0; i
< n
; i
++)
37 fprintf(stderr
, "%02x", x
[i
]);
40 static void aes_cbc_dec(u8
*key
, u8
*iv
, u8
*in
, u32 len
, u8
*out
)
44 AES_set_decrypt_key(key
, 128, &aes_key
);
45 AES_cbc_encrypt(in
, out
, len
, &aes_key
, iv
, AES_DECRYPT
);
48 static void decrypt_title_key(u8
*title_key
, u8
*title_id
)
54 fp
= fopen("common-key", "rb");
55 fread(common_key
, 1, 16, fp
);
58 memset(iv
, 0, sizeof iv
);
59 memcpy(iv
, title_id
, 8);
61 aes_cbc_dec(common_key
, iv
, title_key
, 16, disc_key
);
64 static u32
be32(u8
*p
)
66 return (p
[0] << 24) | (p
[1] << 16) | (p
[2] << 8) | p
[3];
69 static u64
be64(u8
*p
)
71 return ((u64
)be32(p
) << 32) | be32(p
+ 4);
74 static u64
be34(u8
*p
)
76 return 4 * (u64
)be32(p
);
79 static void seek(u64 offset
)
81 fseeko(disc_fp
, offset
, SEEK_SET
);
84 static void disc_read(u64 offset
, u8
*data
, u32 len
)
87 fread(data
, 1, len
, disc_fp
);
90 static void partition_raw_read(u64 offset
, u8
*data
, u32 len
)
92 disc_read(partition_raw_offset
+ offset
, data
, len
);
95 static void partition_read_block(u64 blockno
, u8
*block
)
101 offset
= partition_data_offset
+ 0x8000 * blockno
;
102 partition_raw_read(offset
, raw
, 0x8000);
104 // XXX: check H0, H1, H2 here
106 memcpy(iv
, raw
+ 0x3d0, 16);
107 aes_cbc_dec(disc_key
, iv
, raw
+ 0x400, 0x7c00, block
);
110 static void partition_read(u64 offset
, u8
*data
, u32 len
)
117 offset_in_block
= offset
% 0x7c00;
118 len_in_block
= 0x7c00 - offset_in_block
;
119 if (len_in_block
> len
)
121 partition_read_block(offset
/ 0x7c00, block
);
122 memcpy(data
, block
+ offset_in_block
, len_in_block
);
123 data
+= len_in_block
;
124 offset
+= len_in_block
;
129 static void spinner(u64 x
, u64 max
)
132 static time_t start_time
;
133 static u32 expected_total
;
139 start_time
= time(0);
140 expected_total
= 300;
144 fprintf(stderr
, "Done. \n");
148 d
= time(0) - start_time
;
151 expected_total
= (15 * expected_total
+ d
* max
/ x
) / 16;
153 if (expected_total
> d
)
154 d
= expected_total
- d
;
161 percent
= 100.0 * x
/ max
;
163 fprintf(stderr
, "%5.2f%% (%c) ETA: %d:%02d:%02d \r",
164 percent
, "/|\\-"[(spin
++ / SPINNER_SPEED
) % 4], h
, m
, s
);
168 static void do_data(u64 size
)
176 size
= (size
/ 0x8000) * 0x7c00;
178 fp
= fopen("###dat###", "wb");
180 fprintf(stderr
, "\nDumping partition contents...\n");
182 remaining_size
= size
;
183 while (remaining_size
) {
184 spinner(offset
, size
);
187 if (block_size
> remaining_size
)
188 block_size
= remaining_size
;
190 partition_read(offset
, data
, block_size
);
191 fwrite(data
, 1, block_size
, fp
);
193 offset
+= block_size
;
194 remaining_size
-= block_size
;
201 static void do_yaz0_file(const char *name
, u64 offset
, u32 compressed_size
,
202 u32 uncompressed_size
)
214 snprintf(name_unc
, sizeof name_unc
, "%s###yaz###", name
);
215 fp
= fopen(name_unc
, "wb");
216 data
= malloc(compressed_size
);
217 partition_read(offset
, data
, compressed_size
);
221 for (out
= 0; out
< uncompressed_size
; ) {
227 if ((bits
& 0x80) != 0) {
228 hist
[out
++ % 0x1000] = *in
++;
229 if (out
% 0x1000 == 0)
230 fwrite(hist
, 1, 0x1000, fp
);
234 d
|= (n
<< 8) & 0xf00;
240 for (i
= 0; i
< n
; i
++) {
241 hist
[out
% 0x1000] = hist
[(out
- d
- 1) % 0x1000];
243 if (out
% 0x1000 == 0)
244 fwrite(hist
, 1, 0x1000, fp
);
252 if (out
% 0x1000 != 0)
253 fwrite(hist
, 1, out
% 0x1000, fp
);
259 static void copy_file(const char *name
, u64 offset
, u64 size
)
265 if (decompress_yaz0
&& size
>= 8) {
266 partition_read(offset
, data
, 8);
267 if (memcmp(data
, "Yaz0", 4) == 0) {
268 fprintf(stderr
, " [Yaz0]");
270 do_yaz0_file(name
, offset
, size
, be32(data
+ 4));
276 fp
= fopen(name
, "wb");
279 block_size
= sizeof data
;
280 if (block_size
> size
)
283 partition_read(offset
, data
, block_size
);
284 fwrite(data
, 1, block_size
, fp
);
286 offset
+= block_size
;
293 static u32
do_fst(u8
*fst
, const char *names
, u32 i
, char *indent
, int is_last
)
301 name
= names
+ (be32(fst
+ 12*i
) & 0x00ffffff);
302 size
= be32(fst
+ 12*i
+ 8);
305 fprintf(stderr
, "/\n");
307 for (j
= 1; j
< size
; )
308 j
= do_fst(fst
, names
, j
, indent
, (j
== size
- 1));
314 parent
= be32(fst
+ 12*i
+ 4);
315 is_last
= (be32(fst
+ 12*parent
+ 8) == size
);
318 fprintf(stderr
, "%s%c-- %s", indent
, "|+"[is_last
], name
);
324 fprintf(stderr
, "\n");
329 strcat(indent
, "| ");
331 for (j
= i
+ 1; j
< size
; )
332 j
= do_fst(fst
, names
, j
, indent
, (j
== size
- 1));
334 indent
[strlen(indent
) - 4] = 0;
340 offset
= be34(fst
+ 12*i
+ 4);
341 copy_file(name
, offset
, size
);
343 fprintf(stderr
, "\n");
349 static void do_files(void)
351 u8 b
[0x480]; // XXX: determine actual header size
359 partition_read(0, b
, sizeof b
);
361 fprintf(stderr
, "Title id: %c%c%c%c\n", b
[0], b
[1], b
[2], b
[3]);
362 fprintf(stderr
, "Group id: %c%c\n", b
[4], b
[5]);
363 fprintf(stderr
, "Name: %s\n", b
+ 0x20);
364 fprintf(stderr
, "\n");
366 dol_offset
= be34(b
+ 0x0420);
367 fst_offset
= be34(b
+ 0x0424);
368 fst_size
= be34(b
+ 0x0428);
370 fprintf(stderr
, "\tDOL @ %09llx\n", dol_offset
);
371 fprintf(stderr
, "\tFST @ %09llx (size %08x)\n", fst_offset
, fst_size
);
373 copy_file("###apl###", 0x2440, dol_offset
- 0x2440);
374 // XXX: wrong way to get this size, there is a header
375 copy_file("###dol###", dol_offset
, fst_offset
- dol_offset
);
376 // XXX: similar, perhaps
378 fst
= malloc(fst_size
);
379 partition_read(fst_offset
, fst
, fst_size
);
380 n_files
= be32(fst
+ 8);
382 fprintf(stderr
, "%d entries\n", n_files
);
386 do_fst(fst
, (char *)fst
+ 12*n_files
, 0, indent
, 0);
391 static void do_partition(void)
400 char dirname
[] = "title-0000000000000000";
402 partition_raw_read(0, b
, 0x02c0);
404 decrypt_title_key(b
+ 0x01bf, b
+ 0x01dc);
406 title_id
= be64(b
+ 0x01dc);
408 fprintf(stderr
, "\ttitle id = %016llx\n", title_id
);
410 // XXX: we should check the cert chain here, and read the tmd
412 tmd_offset
= be32(b
+ 0x02a4);
413 tmd_size
= be34(b
+ 0x02a8);
414 cert_size
= be32(b
+ 0x02ac);
415 cert_offset
= be34(b
+ 0x02b0);
416 h3_offset
= be34(b
+ 0x02b4);
417 partition_data_offset
= be34(b
+ 0x02b8);
418 partition_data_size
= be34(b
+ 0x02bc);
420 fprintf(stderr
, "\ttmd offset = %08x\n", tmd_offset
);
421 fprintf(stderr
, "\ttmd size = %09llx\n", tmd_size
);
422 fprintf(stderr
, "\tcert size = %08x\n", cert_size
);
423 fprintf(stderr
, "\tcert offset = %09llx\n", cert_offset
);
424 fprintf(stderr
, "\tdata offset = %09llx\n", partition_data_offset
);
425 fprintf(stderr
, "\tdata size = %09llx\n", partition_data_size
);
427 partition_raw_read(h3_offset
, h3
, 0x18000);
429 // XXX: check h3 against h4 here
431 snprintf(dirname
, sizeof dirname
, "%016llx", title_id
);
433 mkdir(dirname
, 0777);
436 if (dump_partition_data
)
437 do_data(partition_data_size
);
444 static void do_disc(void)
447 u64 partition_offset
[32]; // XXX: don't know the real maximum
451 disc_read(0, b
, sizeof b
);
453 fprintf(stderr
, "Title id: %c%c%c%c\n", b
[0], b
[1], b
[2], b
[3]);
454 fprintf(stderr
, "Group id: %c%c\n", b
[4], b
[5]);
455 fprintf(stderr
, "Name: %s\n", b
+ 0x20);
456 fprintf(stderr
, "\n");
458 disc_read(0x40000, b
, sizeof b
);
459 n_partitions
= be32(b
);
461 disc_read(be34(b
+ 4), b
, sizeof b
);
462 for (i
= 0; i
< n_partitions
; i
++)
463 partition_offset
[i
] = be34(b
+ 8 * i
);
465 fprintf(stderr
, "%d partitions:\n", n_partitions
);
466 for (i
= 0; i
< n_partitions
; i
++)
467 fprintf(stderr
, "\tpartition #%d @ %09llx\n", i
,
468 partition_offset
[i
]);
470 for (i
= 0; i
< n_partitions
; i
++) {
471 fprintf(stderr
, "\nDoing partition %d...\n", i
);
472 fprintf(stderr
, "--------------------------------\n");
474 partition_raw_offset
= partition_offset
[i
];
477 //break; // XXX SII: for testing
481 int main(int argc
, char **argv
)
484 fprintf(stderr
, "Usage: %s <disc file>\n", argv
[0]);
488 disc_fp
= fopen(argv
[1], "rb");