All done. Well there's some loose ends, heh.
[svpe-tools.git] / negentig.c
blob6bf6ad3e2f41bc9b3f3b238930a7538ad4e20f12
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 //fprintf(stderr, "ky: "); print_bytes(disc_key, 16); fprintf(stderr, "\n");
104 //fprintf(stderr, "iv: "); print_bytes(iv, 16); fprintf(stderr, "\n");
105 aes_cbc_dec(disc_key, iv, raw + 0x400, 0x7c00, block);
108 static void partition_read(u64 offset, u8 *data, u32 len)
110 u8 block[0x8000];
111 u32 offset_in_block;
112 u32 len_in_block;
114 while(len) {
115 offset_in_block = offset % 0x7c00;
116 len_in_block = 0x7c00 - offset_in_block;
117 if (len_in_block > len)
118 len_in_block = len;
119 partition_read_block(offset / 0x7c00, block);
120 memcpy(data, block + offset_in_block, len_in_block);
121 data += len_in_block;
122 offset += len_in_block;
123 len -= len_in_block;
127 static void spinner(u64 x, u64 max)
129 static u32 spin;
130 static time_t start_time;
131 static u32 expected_total;
132 u32 d;
133 double percent;
134 u32 h, m, s;
136 if (x == 0) {
137 start_time = time(0);
138 expected_total = 300;
141 if (x == max) {
142 fprintf(stderr, "Done. \n");
143 return;
146 d = time(0) - start_time;
148 if (d != 0)
149 expected_total = (15 * expected_total + d * max / x) / 16;
151 if (expected_total > d)
152 d = expected_total - d;
153 else
154 d = 0;
156 h = d / 3600;
157 m = (d / 60) % 60;
158 s = d % 60;
159 percent = 100.0 * x / max;
161 fprintf(stderr, "%5.2f%% (%c) ETA: %d:%02d:%02d \r",
162 percent, "/|\\-"[(spin++ / SPINNER_SPEED) % 4], h, m, s);
163 fflush(stderr);
166 static void do_data(u64 size)
168 u8 data[0x7c00];
169 u64 offset;
170 u64 remaining_size;
171 u32 block_size;
172 FILE *fp;
174 size = (size / 0x8000) * 0x7c00;
176 fp = fopen("###dat###", "wb");
178 fprintf(stderr, "\nDumping partition contents...\n");
179 offset = 0;
180 remaining_size = size;
181 while (remaining_size) {
182 spinner(offset, size);
184 block_size = 0x7c00;
185 if (block_size > remaining_size)
186 block_size = remaining_size;
188 partition_read(offset, data, block_size);
189 fwrite(data, 1, block_size, fp);
191 offset += block_size;
192 remaining_size -= block_size;
194 spinner(0, 0);
196 fclose(fp);
199 static void copy_file(const char *name, u64 offset, u64 size)
201 u8 data[0x80000];
202 FILE *fp;
203 u32 block_size;
205 fp = fopen(name, "wb");
207 while (size) {
208 block_size = sizeof data;
209 if (block_size > size)
210 block_size = size;
212 partition_read(offset, data, block_size);
213 fwrite(data, 1, block_size, fp);
215 offset += block_size;
216 size -= block_size;
219 fclose(fp);
222 static u32 do_fst(u8 *fst, const char *names, u32 i, char *indent, int is_last)
224 u64 offset;
225 u32 size;
226 const char *name;
227 u32 parent;
228 u32 j;
230 name = names + (be32(fst + 12*i) & 0x00ffffff);
231 size = be32(fst + 12*i + 8);
233 if (i == 0) {
234 fprintf(stderr, "/\n");
236 for (j = 1; j < size; )
237 j = do_fst(fst, names, j, indent, (j == size - 1));
239 return size;
242 if (fst[12*i]) {
243 parent = be32(fst + 12*i + 4);
244 is_last = (be32(fst + 12*parent + 8) == size);
247 fprintf(stderr, "%s%c-- %s", indent, "|+"[is_last], name);
249 if (fst[12*i]) {
250 mkdir(name, 0777);
251 chdir(name);
253 fprintf(stderr, "\n");
255 if (is_last)
256 strcat(indent, " ");
257 else
258 strcat(indent, "| ");
260 for (j = i + 1; j < size; )
261 j = do_fst(fst, names, j, indent, (j == size - 1));
263 indent[strlen(indent) - 4] = 0;
265 chdir("..");
267 return size;
268 } else {
269 offset = be34(fst + 12*i + 4);
271 fprintf(stderr, " @ %09llx, size %08x\n", offset, size);
273 copy_file(name, offset, size);
275 return i + 1;
279 static void do_files(void)
281 u8 b[0x480]; // XXX: determine actual header size
282 u64 dol_offset;
283 u64 fst_offset;
284 u32 fst_size;
285 u8 *fst;
286 char indent[999];
287 u32 n_files;
289 partition_read(0, b, sizeof b);
291 fprintf(stderr, "Title id: %c%c%c%c\n", b[0], b[1], b[2], b[3]);
292 fprintf(stderr, "Group id: %c%c\n", b[4], b[5]);
293 fprintf(stderr, "Name: %s\n", b + 0x20);
294 fprintf(stderr, "\n");
296 dol_offset = be34(b + 0x0420);
297 fst_offset = be34(b + 0x0424);
298 fst_size = be34(b + 0x0428);
300 fprintf(stderr, "\tDOL @ %09llx\n", dol_offset);
301 fprintf(stderr, "\tFST @ %09llx (size %08x)\n", fst_offset, fst_size);
303 copy_file("###apl###", 0x2440, dol_offset - 0x2440); // XXX: wrong way to get this size, there is a header
304 copy_file("###dol###", dol_offset, fst_offset - dol_offset); // 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;