Nintendo DS overlay decompressor
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.

150 lines
3.7 KiB

4 years ago
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// 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] [--] <in> <out>\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;
}