Clean up comments, debugging code.
[svpe-tools.git] / negentig.c
blob5fce1409ec8e709bae202880408b406adee7d945
1 #include <openssl/aes.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <time.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdio.h>
10 static int dump_partition_data = 0;
12 #define SPINNER_SPEED 64
14 typedef unsigned char u8;
15 typedef unsigned short u16;
16 typedef unsigned int u32;
17 typedef unsigned long long u64;
19 FILE *disc_fp;
21 u64 partition_raw_offset;
22 u64 partition_data_offset;
23 u64 partition_data_size;
24 u8 h3[0x18000];
26 u8 disc_key[16];
28 static void print_bytes(u8 *x, u32 n)
30 u32 i;
32 for (i = 0; i < n; i++)
33 fprintf(stderr, "%02x", x[i]);
36 static void aes_cbc_dec(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out)
38 AES_KEY aes_key;
40 AES_set_decrypt_key(key, 128, &aes_key);
41 AES_cbc_encrypt(in, out, len, &aes_key, iv, AES_DECRYPT);
44 static void decrypt_title_key(u8 *title_key, u8 *title_id)
46 u8 common_key[16];
47 u8 iv[16];
48 FILE *fp;
50 fp = fopen("common-key", "rb");
51 fread(common_key, 1, 16, fp);
52 fclose(fp);
54 memset(iv, 0, sizeof iv);
55 memcpy(iv, title_id, 8);
57 aes_cbc_dec(common_key, iv, title_key, 16, disc_key);
60 static u32 be32(u8 *p)
62 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
65 static u64 be64(u8 *p)
67 return ((u64)be32(p) << 32) | be32(p + 4);
70 static u64 be34(u8 *p)
72 return 4 * (u64)be32(p);
75 static void seek(u64 offset)
77 fseeko(disc_fp, offset, SEEK_SET);
80 static void disc_read(u64 offset, u8 *data, u32 len)
82 seek(offset);
83 fread(data, 1, len, disc_fp);
86 static void partition_raw_read(u64 offset, u8 *data, u32 len)
88 disc_read(partition_raw_offset + offset, data, len);
91 static void partition_read_block(u64 blockno, u8 *block)
93 u8 raw[0x8000];
94 u8 iv[16];
95 u64 offset;
97 offset = partition_data_offset + 0x8000 * blockno;
98 partition_raw_read(offset, raw, 0x8000);
100 // XXX: check H0, H1, H2 here
102 memcpy(iv, raw + 0x3d0, 16);
103 aes_cbc_dec(disc_key, iv, raw + 0x400, 0x7c00, block);
106 static void partition_read(u64 offset, u8 *data, u32 len)
108 u8 block[0x8000];
109 u32 offset_in_block;
110 u32 len_in_block;
112 while(len) {
113 offset_in_block = offset % 0x7c00;
114 len_in_block = 0x7c00 - offset_in_block;
115 if (len_in_block > len)
116 len_in_block = len;
117 partition_read_block(offset / 0x7c00, block);
118 memcpy(data, block + offset_in_block, len_in_block);
119 data += len_in_block;
120 offset += len_in_block;
121 len -= len_in_block;
125 static void spinner(u64 x, u64 max)
127 static u32 spin;
128 static time_t start_time;
129 static u32 expected_total;
130 u32 d;
131 double percent;
132 u32 h, m, s;
134 if (x == 0) {
135 start_time = time(0);
136 expected_total = 300;
139 if (x == max) {
140 fprintf(stderr, "Done. \n");
141 return;
144 d = time(0) - start_time;
146 if (d != 0)
147 expected_total = (15 * expected_total + d * max / x) / 16;
149 if (expected_total > d)
150 d = expected_total - d;
151 else
152 d = 0;
154 h = d / 3600;
155 m = (d / 60) % 60;
156 s = d % 60;
157 percent = 100.0 * x / max;
159 fprintf(stderr, "%5.2f%% (%c) ETA: %d:%02d:%02d \r",
160 percent, "/|\\-"[(spin++ / SPINNER_SPEED) % 4], h, m, s);
161 fflush(stderr);
164 static void do_data(u64 size)
166 u8 data[0x7c00];
167 u64 offset;
168 u64 remaining_size;
169 u32 block_size;
170 FILE *fp;
172 size = (size / 0x8000) * 0x7c00;
174 fp = fopen("###dat###", "wb");
176 fprintf(stderr, "\nDumping partition contents...\n");
177 offset = 0;
178 remaining_size = size;
179 while (remaining_size) {
180 spinner(offset, size);
182 block_size = 0x7c00;
183 if (block_size > remaining_size)
184 block_size = remaining_size;
186 partition_read(offset, data, block_size);
187 fwrite(data, 1, block_size, fp);
189 offset += block_size;
190 remaining_size -= block_size;
192 spinner(0, 0);
194 fclose(fp);
197 static void copy_file(const char *name, u64 offset, u64 size)
199 u8 data[0x80000];
200 FILE *fp;
201 u32 block_size;
203 fp = fopen(name, "wb");
205 while (size) {
206 block_size = sizeof data;
207 if (block_size > size)
208 block_size = size;
210 partition_read(offset, data, block_size);
211 fwrite(data, 1, block_size, fp);
213 offset += block_size;
214 size -= block_size;
217 fclose(fp);
220 static u32 do_fst(u8 *fst, const char *names, u32 i, char *indent, int is_last)
222 u64 offset;
223 u32 size;
224 const char *name;
225 u32 parent;
226 u32 j;
228 name = names + (be32(fst + 12*i) & 0x00ffffff);
229 size = be32(fst + 12*i + 8);
231 if (i == 0) {
232 fprintf(stderr, "/\n");
234 for (j = 1; j < size; )
235 j = do_fst(fst, names, j, indent, (j == size - 1));
237 return size;
240 if (fst[12*i]) {
241 parent = be32(fst + 12*i + 4);
242 is_last = (be32(fst + 12*parent + 8) == size);
245 fprintf(stderr, "%s%c-- %s", indent, "|+"[is_last], name);
247 if (fst[12*i]) {
248 mkdir(name, 0777);
249 chdir(name);
251 fprintf(stderr, "\n");
253 if (is_last)
254 strcat(indent, " ");
255 else
256 strcat(indent, "| ");
258 for (j = i + 1; j < size; )
259 j = do_fst(fst, names, j, indent, (j == size - 1));
261 indent[strlen(indent) - 4] = 0;
263 chdir("..");
265 return size;
266 } else {
267 offset = be34(fst + 12*i + 4);
269 fprintf(stderr, " @ %09llx, size %08x\n", offset, size);
271 copy_file(name, offset, size);
273 return i + 1;
277 static void do_files(void)
279 u8 b[0x480]; // XXX: determine actual header size
280 u64 dol_offset;
281 u64 fst_offset;
282 u32 fst_size;
283 u8 *fst;
284 char indent[999];
285 u32 n_files;
287 partition_read(0, b, sizeof b);
289 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
290 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
291 fprintf(stderr, "Name: %s\n", b + 0x20);
292 fprintf(stderr, "\n");
294 dol_offset = be34(b + 0x0420);
295 fst_offset = be34(b + 0x0424);
296 fst_size = be34(b + 0x0428);
298 fprintf(stderr, "\tDOL @ %09llx\n", dol_offset);
299 fprintf(stderr, "\tFST @ %09llx (size %08x)\n", fst_offset, fst_size);
301 copy_file("###apl###", 0x2440, dol_offset - 0x2440);
302 // XXX: wrong way to get this size, there is a header
303 copy_file("###dol###", dol_offset, fst_offset - dol_offset);
304 // XXX: similar, perhaps
306 fst = malloc(fst_size);
307 partition_read(fst_offset, fst, fst_size);
308 n_files = be32(fst + 8);
310 fprintf(stderr, "%d entries\n", n_files);
312 indent[0] = 0;
313 if (n_files > 1)
314 do_fst(fst, (char *)fst + 12*n_files, 0, indent, 0);
316 free(fst);
319 static void do_partition(void)
321 u8 b[0x02c0];
322 u64 title_id;
323 u32 tmd_offset;
324 u64 tmd_size;
325 u32 cert_size;
326 u64 cert_offset;
327 u64 h3_offset;
328 char dirname[] = "title-0000000000000000";
330 partition_raw_read(0, b, 0x02c0);
332 decrypt_title_key(b + 0x01bf, b + 0x01dc);
334 title_id = be64(b + 0x01dc);
336 fprintf(stderr, "\ttitle id = %016llx\n", title_id);
338 // XXX: we should check the cert chain here, and read the tmd
340 tmd_offset = be32(b + 0x02a4);
341 tmd_size = be34(b + 0x02a8);
342 cert_size = be32(b + 0x02ac);
343 cert_offset = be34(b + 0x02b0);
344 h3_offset = be34(b + 0x02b4);
345 partition_data_offset = be34(b + 0x02b8);
346 partition_data_size = be34(b + 0x02bc);
348 fprintf(stderr, "\ttmd offset = %08x\n", tmd_offset);
349 fprintf(stderr, "\ttmd size = %09llx\n", tmd_size);
350 fprintf(stderr, "\tcert size = %08x\n", cert_size);
351 fprintf(stderr, "\tcert offset = %09llx\n", cert_offset);
352 fprintf(stderr, "\tdata offset = %09llx\n", partition_data_offset);
353 fprintf(stderr, "\tdata size = %09llx\n", partition_data_size);
355 partition_raw_read(h3_offset, h3, 0x18000);
357 // XXX: check h3 against h4 here
359 snprintf(dirname, sizeof dirname, "%016llx", title_id);
361 mkdir(dirname, 0777);
362 chdir(dirname);
364 if (dump_partition_data)
365 do_data(partition_data_size);
367 do_files();
369 chdir("..");
372 static void do_disc(void)
374 u8 b[0x100];
375 u64 partition_offset[32]; // XXX: don't know the real maximum
376 u32 n_partitions;
377 u32 i;
379 disc_read(0, b, sizeof b);
381 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
382 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
383 fprintf(stderr, "Name: %s\n", b + 0x20);
384 fprintf(stderr, "\n");
386 disc_read(0x40000, b, sizeof b);
387 n_partitions = be32(b);
389 disc_read(be34(b + 4), b, sizeof b);
390 for (i = 0; i < n_partitions; i++)
391 partition_offset[i] = be34(b + 8 * i);
393 fprintf(stderr, "%d partitions:\n", n_partitions);
394 for (i = 0; i < n_partitions; i++)
395 fprintf(stderr, "\tpartition #%d @ %09llx\n", i,
396 partition_offset[i]);
398 for (i = 0; i < n_partitions; i++) {
399 fprintf(stderr, "\nDoing partition %d...\n", i);
400 fprintf(stderr, "--------------------------------\n");
402 partition_raw_offset = partition_offset[i];
403 do_partition();
405 //break; // XXX SII: for testing
409 int main(int argc, char **argv)
411 if (argc != 2) {
412 fprintf(stderr, "Usage: %s <disc file>\n", argv[0]);
413 return 1;
416 disc_fp = fopen(argv[1], "rb");
418 do_disc();
420 fclose(disc_fp);
422 return 0;