#include #include #include #include // This is a LZ77-esque compression algorithm. // However, unlike LZ77, it decompresses itself in-place. // This is done by storing and reading the compressed data _backwards_, // and writing the decompressed data far beyond the end of the compressed blob. // Of course, that's not a *requirement* of any sort. void nitro_decompress(void *header) { if (!header) return; unsigned dest_offset = *((uint32_t *)header - 1); unsigned data_offset = *((uint32_t *)header - 2) >> 24; unsigned end_offset = *((uint32_t *)header - 2) & 0xFFFFFF; // Compressed size: end_offset // Header + padding size: data_offset // Decompressed size: dest_offset + end_offset + 1 uint8_t *dest = (uint8_t *)header + dest_offset; uint8_t *data = (uint8_t *)header - data_offset; uint8_t *end = (uint8_t *)header - end_offset; while (data > end) { uint8_t flags = *--data; for (int i = 8; i > 0; i--) { if (flags & 0x80) { unsigned length = ((*(data - 1) & 0xF0) >> 4) + 3; unsigned offset = (((*(data - 1) & 0x0F) << 8) | *(data - 2)) + 2; data -= 2; for (unsigned x = length; x > 0; x--) { char d = *(dest + offset); *--dest = d; } } else { *--dest = *--data; } flags <<= 1; if (data <= end) break; } } return; } void usage(const char *prgname) { fprintf(stderr, "Usage: %s [-o offset] [--] \n", prgname); exit(1); } int main(int argc, char *argv[]) { FILE *f; int argi = 1; int offset = 0; for (;;) { if (argc <= argi) usage(argv[0]); if (argv[argi][0] != '-') break; if (strcmp(argv[argi], "--") == 0) { argi++; break; } else if (strcmp(argv[argi], "-o") == 0) { if (argc <= argi + 1) { fprintf(stderr, "-o: Missing offset\n"); usage(argv[0]); } offset = strtol(argv[argi + 1], NULL, 0); argi += 2; } else { fprintf(stderr, "Unrecognized option: '%s'\n", argv[argi]); usage(argv[0]); } } if (argc < argi + 2) { fprintf(stderr, "Missing parameters\n"); usage(argv[0]); } f = fopen(argv[argi + 0], "r"); if (!f) { perror("fopen"); return 1; } // Find the "header" in the file if (!offset) { fseek(f, 0, SEEK_END); } else if (offset < 0) { fseek(f, offset, SEEK_END); } else if (offset > 0) { fseek(f, offset, SEEK_SET); } // Figure out the length and decompressed length of the file uint32_t f_len; uint32_t f_declen; fseek(f, -(sizeof(f_declen) + sizeof(f_len)), SEEK_CUR); if (fread(&f_len, sizeof(f_len), 1, f) != 1) { perror("fread"); return 1; } if (fread(&f_declen, sizeof(f_declen), 1, f) != 1) { perror("fread"); return 1; } f_len = f_len & 0xFFFFFF; if (f_len % 4 != 0) f_len += 4 - (f_len % 4); f_declen += f_len; if (fseek(f, -(size_t)f_len, SEEK_CUR)) { perror("fseek"); return 1; } // Read the file void *file = malloc(f_declen); if (!file) { perror("malloc"); return 1; } if (fread(file, 1, f_len, f) != f_len) { perror("fread"); return 1; } fclose(f); nitro_decompress((char *)file + f_len); // Save the output f = fopen(argv[argi + 1], "w"); if (!f) { perror("fopen"); return 1; } fwrite(file, f_declen, 1, f); fclose(f); free(file); return 0; }