.
[svpe-tools.git] / negentig.c
blob083d5c7afdfaa918707c49a34f4a7eff8e197046
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>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <time.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.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;
23 static FILE *disc_fp;
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)
34 u32 i;
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)
42 AES_KEY aes_key;
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)
50 u8 common_key[16];
51 u8 iv[16];
52 FILE *fp;
54 fp = fopen("common-key", "rb");
55 fread(common_key, 1, 16, fp);
56 fclose(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)
86 seek(offset);
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)
97 u8 raw[0x8000];
98 u8 iv[16];
99 u64 offset;
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)
112 u8 block[0x8000];
113 u32 offset_in_block;
114 u32 len_in_block;
116 while(len) {
117 offset_in_block = offset % 0x7c00;
118 len_in_block = 0x7c00 - offset_in_block;
119 if (len_in_block > len)
120 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;
125 len -= len_in_block;
129 static void spinner(u64 x, u64 max)
131 static u32 spin;
132 static time_t start_time;
133 static u32 expected_total;
134 u32 d;
135 double percent;
136 u32 h, m, s;
138 if (x == 0) {
139 start_time = time(0);
140 expected_total = 300;
143 if (x == max) {
144 fprintf(stderr, "Done. \n");
145 return;
148 d = time(0) - start_time;
150 if (d != 0)
151 expected_total = (15 * expected_total + d * max / x) / 16;
153 if (expected_total > d)
154 d = expected_total - d;
155 else
156 d = 0;
158 h = d / 3600;
159 m = (d / 60) % 60;
160 s = d % 60;
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);
165 fflush(stderr);
168 static void do_data(u64 size)
170 u8 data[0x7c00];
171 u64 offset;
172 u64 remaining_size;
173 u32 block_size;
174 FILE *fp;
176 size = (size / 0x8000) * 0x7c00;
178 fp = fopen("###dat###", "wb");
180 fprintf(stderr, "\nDumping partition contents...\n");
181 offset = 0;
182 remaining_size = size;
183 while (remaining_size) {
184 spinner(offset, size);
186 block_size = 0x7c00;
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;
196 spinner(0, 0);
198 fclose(fp);
201 static void do_yaz0_file(const char *name, u64 offset, u32 compressed_size,
202 u32 uncompressed_size)
204 u8 hist[0x1000];
205 u32 out;
206 char name_unc[256];
207 u8 *data;
208 u8 *in;
209 u8 bits;
210 u32 nbits;
211 u32 n, d, i;
212 FILE *fp;
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);
219 nbits = 0;
220 in = data + 0x10;
221 for (out = 0; out < uncompressed_size; ) {
222 if (nbits == 0) {
223 bits = *in++;
224 nbits = 8;
227 if ((bits & 0x80) != 0) {
228 hist[out++ % 0x1000] = *in++;
229 if (out % 0x1000 == 0)
230 fwrite(hist, 1, 0x1000, fp);
231 } else {
232 n = *in++;
233 d = *in++;
234 d |= (n << 8) & 0xf00;
235 n >>= 4;
236 if (n == 0)
237 n = 0x10 + *in++;
238 n += 2;
240 for (i = 0; i < n; i++) {
241 hist[out % 0x1000] = hist[(out - d - 1) % 0x1000];
242 out++;
243 if (out % 0x1000 == 0)
244 fwrite(hist, 1, 0x1000, fp);
248 nbits--;
249 bits <<= 1;
252 if (out % 0x1000 != 0)
253 fwrite(hist, 1, out % 0x1000, fp);
255 free(data);
256 fclose(fp);
259 static void copy_file(const char *name, u64 offset, u64 size)
261 u8 data[0x80000];
262 FILE *fp;
263 u32 block_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));
272 return;
276 fp = fopen(name, "wb");
278 while (size) {
279 block_size = sizeof data;
280 if (block_size > size)
281 block_size = size;
283 partition_read(offset, data, block_size);
284 fwrite(data, 1, block_size, fp);
286 offset += block_size;
287 size -= block_size;
290 fclose(fp);
293 static u32 do_fst(u8 *fst, const char *names, u32 i, char *indent, int is_last)
295 u64 offset;
296 u32 size;
297 const char *name;
298 u32 parent;
299 u32 j;
301 name = names + (be32(fst + 12*i) & 0x00ffffff);
302 size = be32(fst + 12*i + 8);
304 if (i == 0) {
305 fprintf(stderr, "/\n");
307 for (j = 1; j < size; )
308 j = do_fst(fst, names, j, indent, (j == size - 1));
310 return size;
313 if (fst[12*i]) {
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);
320 if (fst[12*i]) {
321 mkdir(name, 0777);
322 chdir(name);
324 fprintf(stderr, "\n");
326 if (is_last)
327 strcat(indent, " ");
328 else
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;
336 chdir("..");
338 return size;
339 } else {
340 offset = be34(fst + 12*i + 4);
341 copy_file(name, offset, size);
343 fprintf(stderr, "\n");
345 return i + 1;
349 static void do_files(void)
351 u8 b[0x480]; // XXX: determine actual header size
352 u64 dol_offset;
353 u64 fst_offset;
354 u32 fst_size;
355 u8 *fst;
356 char indent[999];
357 u32 n_files;
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);
384 indent[0] = 0;
385 if (n_files > 1)
386 do_fst(fst, (char *)fst + 12*n_files, 0, indent, 0);
388 free(fst);
391 static void do_partition(void)
393 u8 b[0x02c0];
394 u64 title_id;
395 u32 tmd_offset;
396 u64 tmd_size;
397 u32 cert_size;
398 u64 cert_offset;
399 u64 h3_offset;
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);
434 chdir(dirname);
436 if (dump_partition_data)
437 do_data(partition_data_size);
439 do_files();
441 chdir("..");
444 static void do_disc(void)
446 u8 b[0x100];
447 u64 partition_offset[32]; // XXX: don't know the real maximum
448 u32 n_partitions;
449 u32 i;
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];
475 do_partition();
477 //break; // XXX SII: for testing
481 int main(int argc, char **argv)
483 if (argc != 2) {
484 fprintf(stderr, "Usage: %s <disc file>\n", argv[0]);
485 return 1;
488 disc_fp = fopen(argv[1], "rb");
490 do_disc();
492 fclose(disc_fp);
494 return 0;