mid-kid
4 years ago
commit
f952a0ab5b
2 changed files with 196 additions and 0 deletions
@ -0,0 +1,47 @@ |
|||||
|
name := ovldec |
||||
|
|
||||
|
dir_source := source |
||||
|
dir_build := build |
||||
|
|
||||
|
CFLAGS := -O0 -g -Wall -Wextra -std=c17 $(CFLAGS) |
||||
|
|
||||
|
CFLAGS += #$(shell pkg-config --cflags ...) |
||||
|
LDLIBS += #$(shell pkg-config --libs ...) |
||||
|
|
||||
|
SANIT := -fsanitize=address -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize=leak -fsanitize=undefined |
||||
|
OPTIM := -Os -fdata-sections -ffunction-sections -flto -fuse-linker-plugin -fipa-pta -Wl,--gc-sections -Wl,--print-gc-sections -fgraphite-identity -floop-nest-optimize |
||||
|
|
||||
|
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) |
||||
|
objects := $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, $(call rwildcard, $(dir_source)/, *.c)) |
||||
|
|
||||
|
.SECONDEXPANSION: |
||||
|
|
||||
|
.PHONY: all |
||||
|
all: $(name) |
||||
|
|
||||
|
.PHONY: clean |
||||
|
clean: |
||||
|
rm -rf $(dir_build) $(name) |
||||
|
|
||||
|
.PHONY: sanit |
||||
|
sanit: CFLAGS += $(SANIT) |
||||
|
sanit: LDFLAGS += $(SANIT) |
||||
|
sanit: $(name) |
||||
|
|
||||
|
.PHONY: optim |
||||
|
optim: CFLAGS += $(OPTIM) |
||||
|
optim: LDFLAGS += $(OPTIM) |
||||
|
optim: $(name) |
||||
|
strip --strip-all --strip-unneeded $(name) |
||||
|
|
||||
|
$(name): $(objects) |
||||
|
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ |
||||
|
|
||||
|
$(dir_build)/%.o: $(dir_source)/%.c | $$(dir $$@) |
||||
|
$(COMPILE.c) -MMD -MF $(@:.o=.d) $(OUTPUT_OPTION) $< |
||||
|
|
||||
|
.PRECIOUS: %/ |
||||
|
%/: |
||||
|
mkdir -p $@ |
||||
|
|
||||
|
-include $(patsubst %.o, %.d, $(objects)) |
@ -0,0 +1,149 @@ |
|||||
|
#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; |
||||
|
} |
Loading…
Reference in new issue