mid-kid
5 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