wiidisc.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. // Copyright 2009 Kwiirk based on negentig.c:
  2. // Copyright 2007,2008 Segher Boessenkool <segher@kernel.crashing.org>
  3. // Licensed under the terms of the GNU GPL, version 2
  4. // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
  5. #include "wiidisc.h"
  6. void aes_set_key(u8 *key);
  7. void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len);
  8. static void _decrypt_title_key(u8 *tik, u8 *title_key)
  9. {
  10. u8 common_key[16]={
  11. 0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45,
  12. 0x73, 0x81, 0xaa, 0xf7
  13. };
  14. u8 iv[16];
  15. wbfs_memset(iv, 0, sizeof iv);
  16. wbfs_memcpy(iv, tik + 0x01dc, 8);
  17. aes_set_key(common_key);
  18. //_aes_cbc_dec(common_key, iv, tik + 0x01bf, 16, title_key);
  19. aes_decrypt(iv, tik + 0x01bf,title_key,16);
  20. }
  21. static u32 _be32(const u8 *p)
  22. {
  23. return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
  24. }
  25. static void disc_read(wiidisc_t *d,u32 offset, u8 *data, u32 len)
  26. {
  27. if(data){
  28. int ret=0;
  29. if(len==0)
  30. return ;
  31. ret = d->read(d->fp,offset,len,data);
  32. if(ret)
  33. wbfs_fatal("error reading disc (disc_read)");
  34. }
  35. if(d->sector_usage_table)
  36. {
  37. u32 blockno = offset>>13;
  38. do
  39. {
  40. d->sector_usage_table[blockno]=1;
  41. blockno+=1;
  42. if(len>0x8000)
  43. len-=0x8000;
  44. }while(len>0x8000);
  45. }
  46. }
  47. static void partition_raw_read(wiidisc_t *d,u32 offset, u8 *data, u32 len)
  48. {
  49. disc_read(d, d->partition_raw_offset + offset, data, len);
  50. }
  51. static void partition_read_block(wiidisc_t *d,u32 blockno, u8 *block)
  52. {
  53. u8*raw = d->tmp_buffer;
  54. u8 iv[16];
  55. u32 offset;
  56. if(d->sector_usage_table)
  57. d->sector_usage_table[d->partition_block+blockno]=1;
  58. offset = d->partition_data_offset + ((0x8000>>2) * blockno);
  59. partition_raw_read(d,offset, raw, 0x8000);
  60. // decrypt data
  61. memcpy(iv, raw + 0x3d0, 16);
  62. aes_set_key(d->disc_key);
  63. aes_decrypt(iv, raw + 0x400,block,0x7c00);
  64. }
  65. static void partition_read(wiidisc_t *d,u32 offset, u8 *data, u32 len,int fake)
  66. {
  67. u8 *block = d->tmp_buffer2;
  68. u32 offset_in_block;
  69. u32 len_in_block;
  70. if(fake && d->sector_usage_table==0)
  71. return;
  72. while(len) {
  73. offset_in_block = offset % (0x7c00>>2);
  74. len_in_block = 0x7c00 - (offset_in_block<<2);
  75. if (len_in_block > len)
  76. len_in_block = len;
  77. if(!fake){
  78. partition_read_block(d,offset / (0x7c00>>2), block);
  79. wbfs_memcpy(data, block + (offset_in_block<<2), len_in_block);
  80. }else
  81. d->sector_usage_table[d->partition_block+(offset/(0x7c00>>2))]=1;
  82. data += len_in_block;
  83. offset += len_in_block>>2;
  84. len -= len_in_block;
  85. }
  86. }
  87. static u32 do_fst(wiidisc_t *d,u8 *fst, const char *names, u32 i)
  88. {
  89. u32 offset;
  90. u32 size;
  91. const char *name;
  92. u32 j;
  93. name = names + (_be32(fst + 12*i) & 0x00ffffff);
  94. size = _be32(fst + 12*i + 8);
  95. if (i == 0) {
  96. for (j = 1; j < size && !d->extracted_buffer; ){
  97. j = do_fst(d,fst, names, j);
  98. }
  99. return size;
  100. }
  101. //printf("name %s\n",name);
  102. if (fst[12*i]) {
  103. for (j = i + 1; j < size && !d->extracted_buffer; )
  104. j = do_fst(d,fst, names, j);
  105. return size;
  106. } else {
  107. offset = _be32(fst + 12*i + 4);
  108. if(d->extract_pathname && strcmp(name, d->extract_pathname)==0)
  109. {
  110. d->extracted_buffer = wbfs_ioalloc(size);
  111. partition_read(d,offset, d->extracted_buffer, size,0);
  112. }else
  113. partition_read(d,offset, 0, size,1);
  114. return i + 1;
  115. }
  116. }
  117. static void do_files(wiidisc_t*d)
  118. {
  119. u8 *b = wbfs_ioalloc(0x480); // XXX: determine actual header size
  120. u32 dol_offset;
  121. u32 fst_offset;
  122. u32 fst_size;
  123. u32 apl_offset;
  124. u32 apl_size;
  125. u8 *apl_header = wbfs_ioalloc(0x20);
  126. u8 *fst;
  127. u32 n_files;
  128. partition_read(d,0, b, 0x480,0);
  129. dol_offset = _be32(b + 0x0420);
  130. fst_offset = _be32(b + 0x0424);
  131. fst_size = _be32(b + 0x0428)<<2;
  132. apl_offset = 0x2440>>2;
  133. partition_read(d,apl_offset, apl_header, 0x20,0);
  134. apl_size = 0x20 + _be32(apl_header + 0x14) + _be32(apl_header + 0x18);
  135. // fake read dol and partition
  136. partition_read(d,apl_offset, 0, apl_size,1);
  137. partition_read(d,dol_offset, 0, (fst_offset - dol_offset)<<2,1);
  138. fst = wbfs_ioalloc(fst_size);
  139. if (fst == 0)
  140. wbfs_fatal("malloc fst");
  141. partition_read(d,fst_offset, fst, fst_size,0);
  142. n_files = _be32(fst + 8);
  143. if (n_files > 1)
  144. do_fst(d,fst, (char *)fst + 12*n_files, 0);
  145. wbfs_iofree(b);
  146. wbfs_iofree(apl_header);
  147. wbfs_iofree(fst);
  148. }
  149. static void do_partition(wiidisc_t*d)
  150. {
  151. u8 *tik = wbfs_ioalloc(0x2a4);
  152. u8 *b = wbfs_ioalloc(0x1c);
  153. u64 tmd_offset;
  154. u32 tmd_size;
  155. u8 *tmd;
  156. u64 cert_offset;
  157. u32 cert_size;
  158. u8 *cert;
  159. u64 h3_offset;
  160. // read ticket, and read some offsets and sizes
  161. partition_raw_read(d,0, tik, 0x2a4);
  162. partition_raw_read(d,0x2a4>>2, b, 0x1c);
  163. tmd_size = _be32(b);
  164. tmd_offset = _be32(b + 4);
  165. cert_size = _be32(b + 8);
  166. cert_offset = _be32(b + 0x0c);
  167. h3_offset = _be32(b + 0x10);
  168. d->partition_data_offset = _be32(b + 0x14);
  169. d->partition_block = (d->partition_raw_offset+d->partition_data_offset)>>13;
  170. tmd = wbfs_ioalloc(tmd_size);
  171. if (tmd == 0)
  172. wbfs_fatal("malloc tmd");
  173. partition_raw_read(d,tmd_offset, tmd, tmd_size);
  174. cert = wbfs_ioalloc(cert_size);
  175. if (cert == 0)
  176. wbfs_fatal("malloc cert");
  177. partition_raw_read(d,cert_offset, cert, cert_size);
  178. _decrypt_title_key(tik, d->disc_key);
  179. partition_raw_read(d,h3_offset, 0, 0x18000);
  180. wbfs_iofree(b);
  181. wbfs_iofree(tik);
  182. wbfs_iofree(cert);
  183. wbfs_iofree(tmd);
  184. do_files(d);
  185. }
  186. static int test_parition_skip(u32 partition_type,partition_selector_t part_sel)
  187. {
  188. switch(part_sel)
  189. {
  190. case ALL_PARTITIONS:
  191. return 0;
  192. case REMOVE_UPDATE_PARTITION:
  193. return (partition_type==1);
  194. case ONLY_GAME_PARTITION:
  195. return (partition_type!=0);
  196. default:
  197. return (partition_type!=part_sel);
  198. }
  199. }
  200. static int do_disc(wiidisc_t*d)
  201. {
  202. u8 *b = wbfs_ioalloc(0x100);
  203. u64 partition_offset[32]; // XXX: don't know the real maximum
  204. u64 partition_type[32]; // XXX: don't know the real maximum
  205. u32 n_partitions;
  206. u32 magic;
  207. u32 i;
  208. disc_read(d,0, b, 0x100);
  209. magic=_be32(b+24);
  210. if(magic!=0x5D1C9EA3){
  211. wbfs_iofree(b);
  212. wbfs_error("not a wii disc");
  213. return 0;
  214. }
  215. disc_read(d,0x40000>>2, b, 0x100);
  216. n_partitions = _be32(b);
  217. disc_read(d,_be32(b + 4), b, 0x100);
  218. for (i = 0; i < n_partitions; i++){
  219. partition_offset[i] = _be32(b + 8 * i);
  220. partition_type[i] = _be32(b + 8 * i+4);
  221. }
  222. for (i = 0; i < n_partitions; i++) {
  223. d->partition_raw_offset = partition_offset[i];
  224. if(!test_parition_skip(partition_type[i],d->part_sel))
  225. do_partition(d);
  226. }
  227. wbfs_iofree(b);
  228. return 1;
  229. }
  230. wiidisc_t *wd_open_disc(read_wiidisc_callback_t read,void*fp)
  231. {
  232. wiidisc_t *d = wbfs_malloc(sizeof(wiidisc_t));
  233. if(!d)
  234. return 0;
  235. wbfs_memset(d,0,sizeof(wiidisc_t));
  236. d->read = read;
  237. d->fp = fp;
  238. d->part_sel = ALL_PARTITIONS;
  239. d->tmp_buffer = wbfs_ioalloc(0x8000);
  240. d->tmp_buffer2 = wbfs_malloc(0x8000);
  241. return d;
  242. }
  243. void wd_close_disc(wiidisc_t *d)
  244. {
  245. wbfs_iofree(d->tmp_buffer);
  246. wbfs_free(d->tmp_buffer2);
  247. wbfs_free(d);
  248. }
  249. // returns a buffer allocated with wbfs_ioalloc() or NULL if not found of alloc error
  250. // XXX pathname not implemented. files are extracted by their name.
  251. // first file found with that name is returned.
  252. u8 * wd_extract_file(wiidisc_t *d, partition_selector_t partition_type, char *pathname)
  253. {
  254. u8 *retval = 0;
  255. d->extract_pathname = pathname;
  256. d->extracted_buffer = 0;
  257. d->part_sel = partition_type;
  258. int result = do_disc(d);
  259. d->extract_pathname = 0;
  260. d->part_sel = ALL_PARTITIONS;
  261. retval = result == 0 ? 0 : d->extracted_buffer;
  262. d->extracted_buffer = 0;
  263. return retval;
  264. }
  265. int wd_build_disc_usage(wiidisc_t *d, partition_selector_t selector, u8* usage_table)
  266. {
  267. d->sector_usage_table = usage_table;
  268. wbfs_memset(usage_table,0,143432*2);
  269. d->part_sel = selector;
  270. int result = do_disc(d);
  271. d->part_sel = ALL_PARTITIONS;
  272. d->sector_usage_table = 0;
  273. return result;
  274. }
  275. void wd_fix_partition_table(wiidisc_t *d, partition_selector_t selector, u8* partition_table)
  276. {
  277. u8 *b = partition_table;
  278. u32 partition_offset;
  279. u32 partition_type;
  280. u32 n_partitions,i,j;
  281. u32 *b32;
  282. if(selector == ALL_PARTITIONS)
  283. return;
  284. n_partitions = _be32(b);
  285. if(_be32(b + 4)-(0x40000>>2) >0x50)
  286. wbfs_fatal("cannot modify this partition table. Please report the bug.");
  287. b += (_be32(b + 4)-(0x40000>>2))*4;
  288. j=0;
  289. for (i = 0; i < n_partitions; i++){
  290. partition_offset = _be32(b + 8 * i);
  291. partition_type = _be32(b + 8 * i+4);
  292. if(!test_parition_skip(partition_type,selector))
  293. {
  294. b32 = (u32*)(b + 8 * j);
  295. b32[0] = wbfs_htonl(partition_offset);
  296. b32[1] = wbfs_htonl(partition_type);
  297. j++;
  298. }
  299. }
  300. b32 = (u32*)(partition_table);
  301. *b32 = wbfs_htonl(j);
  302. }