From f952a0ab5ba42a4dc0bd0a4bf4fc9694e0e46de5 Mon Sep 17 00:00:00 2001 From: mid-kid Date: Wed, 15 Jul 2020 23:51:26 +0200 Subject: [PATCH] Initial commit --- Makefile | 47 ++++++++++++++++ source/main.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 Makefile create mode 100644 source/main.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6077323 --- /dev/null +++ b/Makefile @@ -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)) diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..e57d1c8 --- /dev/null +++ b/source/main.c @@ -0,0 +1,149 @@ +#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; +}