Super Mario 64s source code (from a leak on 4chan so be careful)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

556 lines
14 KiB

5 years ago
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include "libmio0.h"
  5. #include "utils.h"
  6. // defines
  7. #define MIO0_VERSION "0.1"
  8. #define GET_BIT(buf, bit) ((buf)[(bit) / 8] & (1 << (7 - ((bit) % 8))))
  9. // types
  10. typedef struct
  11. {
  12. int *indexes;
  13. int allocated;
  14. int count;
  15. int start;
  16. } lookback;
  17. // functions
  18. #define LOOKBACK_COUNT 256
  19. #define LOOKBACK_INIT_SIZE 128
  20. static lookback *lookback_init(void)
  21. {
  22. lookback *lb = malloc(LOOKBACK_COUNT * sizeof(*lb));
  23. for (int i = 0; i < LOOKBACK_COUNT; i++) {
  24. lb[i].allocated = LOOKBACK_INIT_SIZE;
  25. lb[i].indexes = malloc(lb[i].allocated * sizeof(*lb[i].indexes));
  26. lb[i].count = 0;
  27. lb[i].start = 0;
  28. }
  29. return lb;
  30. }
  31. static void lookback_free(lookback *lb)
  32. {
  33. for (int i = 0; i < LOOKBACK_COUNT; i++) {
  34. free(lb[i].indexes);
  35. }
  36. free(lb);
  37. }
  38. static inline void lookback_push(lookback *lkbk, unsigned char val, int index)
  39. {
  40. lookback *lb = &lkbk[val];
  41. if (lb->count == lb->allocated) {
  42. lb->allocated *= 4;
  43. lb->indexes = realloc(lb->indexes, lb->allocated * sizeof(*lb->indexes));
  44. }
  45. lb->indexes[lb->count++] = index;
  46. }
  47. static void PUT_BIT(unsigned char *buf, int bit, int val)
  48. {
  49. unsigned char mask = 1 << (7 - (bit % 8));
  50. unsigned int offset = bit / 8;
  51. buf[offset] = (buf[offset] & ~(mask)) | (val ? mask : 0);
  52. }
  53. // used to find longest matching stream in buffer
  54. // buf: buffer
  55. // start_offset: offset in buf to look back from
  56. // max_search: max number of bytes to find
  57. // found_offset: returned offset found (0 if none found)
  58. // returns max length of matching stream (0 if none found)
  59. static int find_longest(const unsigned char *buf, int start_offset, int max_search, int *found_offset, lookback *lkbk)
  60. {
  61. int best_length = 0;
  62. int best_offset = 0;
  63. int cur_length;
  64. int search_len;
  65. int farthest, off, i;
  66. int lb_idx;
  67. const unsigned char first = buf[start_offset];
  68. lookback *lb = &lkbk[first];
  69. // buf
  70. // | off start max
  71. // V |+i-> |+i-> |
  72. // |--------------raw-data-----------------|
  73. // |+i-> | |+i->
  74. // +cur_length
  75. // check at most the past 4096 values
  76. farthest = MAX(start_offset - 4096, 0);
  77. // find starting index
  78. for (lb_idx = lb->start; lb_idx < lb->count && lb->indexes[lb_idx] < farthest; lb_idx++) {}
  79. lb->start = lb_idx;
  80. for ( ; lb_idx < lb->count && lb->indexes[lb_idx] < start_offset; lb_idx++) {
  81. off = lb->indexes[lb_idx];
  82. // check at most requested max or up until start
  83. search_len = MIN(max_search, start_offset - off);
  84. for (i = 0; i < search_len; i++) {
  85. if (buf[start_offset + i] != buf[off + i]) {
  86. break;
  87. }
  88. }
  89. cur_length = i;
  90. // if matched up until start, continue matching in already matched parts
  91. if (cur_length == search_len) {
  92. // check at most requested max less current length
  93. search_len = max_search - cur_length;
  94. for (i = 0; i < search_len; i++) {
  95. if (buf[start_offset + cur_length + i] != buf[off + i]) {
  96. break;
  97. }
  98. }
  99. cur_length += i;
  100. }
  101. if (cur_length > best_length) {
  102. best_offset = start_offset - off;
  103. best_length = cur_length;
  104. }
  105. }
  106. // return best reverse offset and length (may be 0)
  107. *found_offset = best_offset;
  108. return best_length;
  109. }
  110. // decode MIO0 header
  111. // returns 1 if valid header, 0 otherwise
  112. int mio0_decode_header(const unsigned char *buf, mio0_header_t *head)
  113. {
  114. if (!memcmp(buf, "MIO0", 4)) {
  115. head->dest_size = read_u32_be(&buf[4]);
  116. head->comp_offset = read_u32_be(&buf[8]);
  117. head->uncomp_offset = read_u32_be(&buf[12]);
  118. return 1;
  119. }
  120. return 0;
  121. }
  122. void mio0_encode_header(unsigned char *buf, const mio0_header_t *head)
  123. {
  124. memcpy(buf, "MIO0", 4);
  125. write_u32_be(&buf[4], head->dest_size);
  126. write_u32_be(&buf[8], head->comp_offset);
  127. write_u32_be(&buf[12], head->uncomp_offset);
  128. }
  129. int mio0_decode(const unsigned char *in, unsigned char *out, unsigned int *end)
  130. {
  131. mio0_header_t head;
  132. unsigned int bytes_written = 0;
  133. int bit_idx = 0;
  134. int comp_idx = 0;
  135. int uncomp_idx = 0;
  136. int valid;
  137. // extract header
  138. valid = mio0_decode_header(in, &head);
  139. // verify MIO0 header
  140. if (!valid) {
  141. return -2;
  142. }
  143. // decode data
  144. while (bytes_written < head.dest_size) {
  145. if (GET_BIT(&in[MIO0_HEADER_LENGTH], bit_idx)) {
  146. // 1 - pull uncompressed data
  147. out[bytes_written] = in[head.uncomp_offset + uncomp_idx];
  148. bytes_written++;
  149. uncomp_idx++;
  150. } else {
  151. // 0 - read compressed data
  152. int idx;
  153. int length;
  154. int i;
  155. const unsigned char *vals = &in[head.comp_offset + comp_idx];
  156. comp_idx += 2;
  157. length = ((vals[0] & 0xF0) >> 4) + 3;
  158. idx = ((vals[0] & 0x0F) << 8) + vals[1] + 1;
  159. for (i = 0; i < length; i++) {
  160. out[bytes_written] = out[bytes_written - idx];
  161. bytes_written++;
  162. }
  163. }
  164. bit_idx++;
  165. }
  166. if (end) {
  167. *end = head.uncomp_offset + uncomp_idx;
  168. }
  169. return bytes_written;
  170. }
  171. int mio0_encode(const unsigned char *in, unsigned int length, unsigned char *out)
  172. {
  173. unsigned char *bit_buf;
  174. unsigned char *comp_buf;
  175. unsigned char *uncomp_buf;
  176. unsigned int bit_length;
  177. unsigned int comp_offset;
  178. unsigned int uncomp_offset;
  179. unsigned int bytes_proc = 0;
  180. int bytes_written;
  181. int bit_idx = 0;
  182. int comp_idx = 0;
  183. int uncomp_idx = 0;
  184. lookback *lookbacks;
  185. // initialize lookback buffer
  186. lookbacks = lookback_init();
  187. // allocate some temporary buffers worst case size
  188. bit_buf = malloc((length + 7) / 8); // 1-bit/byte
  189. comp_buf = malloc(length); // 16-bits/2bytes
  190. uncomp_buf = malloc(length); // all uncompressed
  191. memset(bit_buf, 0, (length + 7) / 8);
  192. // encode data
  193. // special case for first byte
  194. lookback_push(lookbacks, in[0], 0);
  195. uncomp_buf[uncomp_idx] = in[0];
  196. uncomp_idx += 1;
  197. bytes_proc += 1;
  198. PUT_BIT(bit_buf, bit_idx++, 1);
  199. while (bytes_proc < length) {
  200. int offset;
  201. int max_length = MIN(length - bytes_proc, 18);
  202. int longest_match = find_longest(in, bytes_proc, max_length, &offset, lookbacks);
  203. // push current byte before checking next longer match
  204. lookback_push(lookbacks, in[bytes_proc], bytes_proc);
  205. if (longest_match > 2) {
  206. int lookahead_offset;
  207. // lookahead to next byte to see if longer match
  208. int lookahead_length = MIN(length - bytes_proc - 1, 18);
  209. int lookahead_match = find_longest(in, bytes_proc + 1, lookahead_length, &lookahead_offset, lookbacks);
  210. // better match found, use uncompressed + lookahead compressed
  211. if ((longest_match + 1) < lookahead_match) {
  212. // uncompressed byte
  213. uncomp_buf[uncomp_idx] = in[bytes_proc];
  214. uncomp_idx++;
  215. PUT_BIT(bit_buf, bit_idx, 1);
  216. bytes_proc++;
  217. longest_match = lookahead_match;
  218. offset = lookahead_offset;
  219. bit_idx++;
  220. lookback_push(lookbacks, in[bytes_proc], bytes_proc);
  221. }
  222. // first byte already pushed above
  223. for (int i = 1; i < longest_match; i++) {
  224. lookback_push(lookbacks, in[bytes_proc + i], bytes_proc + i);
  225. }
  226. // compressed block
  227. comp_buf[comp_idx] = (((longest_match - 3) & 0x0F) << 4) |
  228. (((offset - 1) >> 8) & 0x0F);
  229. comp_buf[comp_idx + 1] = (offset - 1) & 0xFF;
  230. comp_idx += 2;
  231. PUT_BIT(bit_buf, bit_idx, 0);
  232. bytes_proc += longest_match;
  233. } else {
  234. // uncompressed byte
  235. uncomp_buf[uncomp_idx] = in[bytes_proc];
  236. uncomp_idx++;
  237. PUT_BIT(bit_buf, bit_idx, 1);
  238. bytes_proc++;
  239. }
  240. bit_idx++;
  241. }
  242. // compute final sizes and offsets
  243. // +7 so int division accounts for all bits
  244. bit_length = ((bit_idx + 7) / 8);
  245. // compressed data after control bits and aligned to 4-byte boundary
  246. comp_offset = ALIGN(MIO0_HEADER_LENGTH + bit_length, 4);
  247. uncomp_offset = comp_offset + comp_idx;
  248. bytes_written = uncomp_offset + uncomp_idx;
  249. // output header
  250. memcpy(out, "MIO0", 4);
  251. write_u32_be(&out[4], length);
  252. write_u32_be(&out[8], comp_offset);
  253. write_u32_be(&out[12], uncomp_offset);
  254. // output data
  255. memcpy(&out[MIO0_HEADER_LENGTH], bit_buf, bit_length);
  256. memcpy(&out[comp_offset], comp_buf, comp_idx);
  257. memcpy(&out[uncomp_offset], uncomp_buf, uncomp_idx);
  258. // free allocated buffers
  259. free(bit_buf);
  260. free(comp_buf);
  261. free(uncomp_buf);
  262. lookback_free(lookbacks);
  263. return bytes_written;
  264. }
  265. int mio0_decode_file(const char *in_file, unsigned long offset, const char *out_file)
  266. {
  267. mio0_header_t head;
  268. FILE *in;
  269. FILE *out;
  270. unsigned char *in_buf = NULL;
  271. unsigned char *out_buf = NULL;
  272. long file_size;
  273. int ret_val = 0;
  274. size_t bytes_read;
  275. int bytes_decoded;
  276. int bytes_written;
  277. int valid;
  278. in = fopen(in_file, "rb");
  279. if (in == NULL) {
  280. return 1;
  281. }
  282. // allocate buffer to read from offset to end of file
  283. fseek(in, 0, SEEK_END);
  284. file_size = ftell(in);
  285. in_buf = malloc(file_size - offset);
  286. fseek(in, offset, SEEK_SET);
  287. // read bytes
  288. bytes_read = fread(in_buf, 1, file_size - offset, in);
  289. if (bytes_read != file_size - offset) {
  290. ret_val = 2;
  291. goto free_all;
  292. }
  293. // verify header
  294. valid = mio0_decode_header(in_buf, &head);
  295. if (!valid) {
  296. ret_val = 3;
  297. goto free_all;
  298. }
  299. out_buf = malloc(head.dest_size);
  300. // decompress MIO0 encoded data
  301. bytes_decoded = mio0_decode(in_buf, out_buf, NULL);
  302. if (bytes_decoded < 0) {
  303. ret_val = 3;
  304. goto free_all;
  305. }
  306. // open output file
  307. out = fopen(out_file, "wb");
  308. if (out == NULL) {
  309. ret_val = 4;
  310. goto free_all;
  311. }
  312. // write data to file
  313. bytes_written = fwrite(out_buf, 1, bytes_decoded, out);
  314. if (bytes_written != bytes_decoded) {
  315. ret_val = 5;
  316. }
  317. // clean up
  318. fclose(out);
  319. free_all:
  320. if (out_buf) {
  321. free(out_buf);
  322. }
  323. if (in_buf) {
  324. free(in_buf);
  325. }
  326. fclose(in);
  327. return ret_val;
  328. }
  329. int mio0_encode_file(const char *in_file, const char *out_file)
  330. {
  331. FILE *in;
  332. FILE *out;
  333. unsigned char *in_buf = NULL;
  334. unsigned char *out_buf = NULL;
  335. size_t file_size;
  336. size_t bytes_read;
  337. int bytes_encoded;
  338. int bytes_written;
  339. int ret_val = 0;
  340. in = fopen(in_file, "rb");
  341. if (in == NULL) {
  342. return 1;
  343. }
  344. // allocate buffer to read entire contents of files
  345. fseek(in, 0, SEEK_END);
  346. file_size = ftell(in);
  347. fseek(in, 0, SEEK_SET);
  348. in_buf = malloc(file_size);
  349. // read bytes
  350. bytes_read = fread(in_buf, 1, file_size, in);
  351. if (bytes_read != file_size) {
  352. ret_val = 2;
  353. goto free_all;
  354. }
  355. // allocate worst case length
  356. out_buf = malloc(MIO0_HEADER_LENGTH + ((file_size+7)/8) + file_size);
  357. // compress data in MIO0 format
  358. bytes_encoded = mio0_encode(in_buf, file_size, out_buf);
  359. // open output file
  360. out = fopen(out_file, "wb");
  361. if (out == NULL) {
  362. ret_val = 4;
  363. goto free_all;
  364. }
  365. // write data to file
  366. bytes_written = fwrite(out_buf, 1, bytes_encoded, out);
  367. if (bytes_written != bytes_encoded) {
  368. ret_val = 5;
  369. }
  370. // clean up
  371. fclose(out);
  372. free_all:
  373. if (out_buf) {
  374. free(out_buf);
  375. }
  376. if (in_buf) {
  377. free(in_buf);
  378. }
  379. fclose(in);
  380. return ret_val;
  381. }
  382. // mio0 standalone executable
  383. #ifdef MIO0_STANDALONE
  384. typedef struct
  385. {
  386. char *in_filename;
  387. char *out_filename;
  388. unsigned int offset;
  389. int compress;
  390. } arg_config;
  391. static arg_config default_config =
  392. {
  393. NULL,
  394. NULL,
  395. 0,
  396. 1
  397. };
  398. static void print_usage(void)
  399. {
  400. ERROR("Usage: mio0 [-c / -d] [-o OFFSET] FILE [OUTPUT]\n"
  401. "\n"
  402. "mio0 v" MIO0_VERSION ": MIO0 compression and decompression tool\n"
  403. "\n"
  404. "Optional arguments:\n"
  405. " -c compress raw data into MIO0 (default: compress)\n"
  406. " -d decompress MIO0 into raw data\n"
  407. " -o OFFSET starting offset in FILE (default: 0)\n"
  408. "\n"
  409. "File arguments:\n"
  410. " FILE input file\n"
  411. " [OUTPUT] output file (default: FILE.out)\n");
  412. exit(1);
  413. }
  414. // parse command line arguments
  415. static void parse_arguments(int argc, char *argv[], arg_config *config)
  416. {
  417. int i;
  418. int file_count = 0;
  419. if (argc < 2) {
  420. print_usage();
  421. exit(1);
  422. }
  423. for (i = 1; i < argc; i++) {
  424. if (argv[i][0] == '-') {
  425. switch (argv[i][1]) {
  426. case 'c':
  427. config->compress = 1;
  428. break;
  429. case 'd':
  430. config->compress = 0;
  431. break;
  432. case 'o':
  433. if (++i >= argc) {
  434. print_usage();
  435. }
  436. config->offset = strtoul(argv[i], NULL, 0);
  437. break;
  438. default:
  439. print_usage();
  440. break;
  441. }
  442. } else {
  443. switch (file_count) {
  444. case 0:
  445. config->in_filename = argv[i];
  446. break;
  447. case 1:
  448. config->out_filename = argv[i];
  449. break;
  450. default: // too many
  451. print_usage();
  452. break;
  453. }
  454. file_count++;
  455. }
  456. }
  457. if (file_count < 1) {
  458. print_usage();
  459. }
  460. }
  461. int main(int argc, char *argv[])
  462. {
  463. char out_filename[FILENAME_MAX];
  464. arg_config config;
  465. int ret_val;
  466. // get configuration from arguments
  467. config = default_config;
  468. parse_arguments(argc, argv, &config);
  469. if (config.out_filename == NULL) {
  470. config.out_filename = out_filename;
  471. sprintf(config.out_filename, "%s.out", config.in_filename);
  472. }
  473. // operation
  474. if (config.compress) {
  475. ret_val = mio0_encode_file(config.in_filename, config.out_filename);
  476. } else {
  477. ret_val = mio0_decode_file(config.in_filename, config.offset, config.out_filename);
  478. }
  479. switch (ret_val) {
  480. case 1:
  481. ERROR("Error opening input file \"%s\"\n", config.in_filename);
  482. break;
  483. case 2:
  484. ERROR("Error reading from input file \"%s\"\n", config.in_filename);
  485. break;
  486. case 3:
  487. ERROR("Error decoding MIO0 data. Wrong offset (0x%X)?\n", config.offset);
  488. break;
  489. case 4:
  490. ERROR("Error opening output file \"%s\"\n", config.out_filename);
  491. break;
  492. case 5:
  493. ERROR("Error writing bytes to output file \"%s\"\n", config.out_filename);
  494. break;
  495. }
  496. return ret_val;
  497. }
  498. #endif // MIO0_STANDALONE