mirror of
https://github.com/HarbourMasters/Starship.git
synced 2025-01-23 13:35:11 +03:00
Makefile Changes (#40)
* Remove existing recomp * Compile ido-static-recomp instead of relying on precompiled version. Also introduce makefile changes to allow aarch64 devices to compile. * Get mio0 from the source, and build it in the init. * Workaround for sm64tools not ignoring it's build files. * Fix cflags * Fancy colours for build system * Remove sm64tools submodule and just take the minimum required files instead. * Remove ido-static-recomp submodule and just fetch latest from GH instead. * Add support for using a venv in python. * remove mio0-decompressor temp * Fix the mio0-decompress files being somehow missing. * Fix stray message about "fix_checksum" * Update logo to be a VAR, and have a failed build say FAILED * Add checksum fix back in.
This commit is contained in:
parent
3a38f87ea5
commit
065b566eb6
3
.gitignore
vendored
3
.gitignore
vendored
@ -13,3 +13,6 @@ ctx.c.m2c
|
||||
*.z64
|
||||
*.bin
|
||||
/build
|
||||
tools/mio0
|
||||
tools/ido-recomp
|
||||
.venv/
|
111
Makefile
111
Makefile
@ -22,6 +22,12 @@ CC_CHECK_COMP ?= gcc
|
||||
OBJDUMP_BUILD ?= 0
|
||||
# Number of threads to compress with
|
||||
N_THREADS ?= $(shell nproc)
|
||||
# Whether to colorize build messages
|
||||
COLOR ?= 1
|
||||
# Whether to hide commands or not
|
||||
VERBOSE ?= 0
|
||||
# Command for printing messages during the make.
|
||||
PRINT ?= printf
|
||||
|
||||
# Set prefix to mips binutils binaries (mips-linux-gnu-ld => 'mips-linux-gnu-') - Change at your own risk!
|
||||
# In nearly all cases, not having 'mips-linux-gnu-*' binaries on the PATH is indicative of missing dependencies
|
||||
@ -64,16 +70,46 @@ CPPFLAGS += -fno-dollars-in-identifiers -P
|
||||
LDFLAGS := --no-check-sections --accept-unknown-input-arch --emit-relocs
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNAME_M := $(shell uname -m)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
$(error Native Windows is currently unsupported for building this repository, use WSL instead c:)
|
||||
else ifeq ($(UNAME_S),Linux)
|
||||
DETECTED_OS := linux
|
||||
#Detect aarch64 devices (Like Raspberry Pi OS 64-bit)
|
||||
#If it's found, then change the compiler to a version that can compile in 32 bit mode.
|
||||
ifeq ($(UNAME_M),aarch64)
|
||||
CC_CHECK_COMP := arm-linux-gnueabihf-gcc
|
||||
endif
|
||||
else ifeq ($(UNAME_S),Darwin)
|
||||
DETECTED_OS := mac
|
||||
MAKE := gmake
|
||||
CPPFLAGS += -xc++
|
||||
endif
|
||||
|
||||
# Support python venv's if one is installed.
|
||||
PYTHON_VENV = .venv/bin/python3
|
||||
ifneq "$(wildcard $(PYTHON_VENV) )" ""
|
||||
PYTHON = $(PYTHON_VENV)
|
||||
endif
|
||||
|
||||
ifeq ($(VERBOSE),0)
|
||||
V := @
|
||||
endif
|
||||
|
||||
ifeq ($(COLOR),1)
|
||||
NO_COL := \033[0m
|
||||
RED := \033[0;31m
|
||||
GREEN := \033[0;32m
|
||||
BLUE := \033[0;34m
|
||||
YELLOW := \033[0;33m
|
||||
BLINK := \033[33;5m
|
||||
endif
|
||||
|
||||
# Common build print status function
|
||||
define print
|
||||
@$(PRINT) "$(GREEN)$(1) $(YELLOW)$(2)$(GREEN) -> $(BLUE)$(3)$(NO_COL)\n"
|
||||
endef
|
||||
|
||||
#### Tools ####
|
||||
ifneq ($(shell type $(MIPS_BINUTILS_PREFIX)ld >/dev/null 2>/dev/null; echo $$?), 0)
|
||||
$(error Unable to find $(MIPS_BINUTILS_PREFIX)ld. Please install or build MIPS binutils, commonly mips-linux-gnu. (or set MIPS_BINUTILS_PREFIX if your MIPS binutils install uses another prefix))
|
||||
@ -82,7 +118,7 @@ endif
|
||||
|
||||
### Compiler ###
|
||||
|
||||
IDO := $(TOOLS)/ido_recomp/$(DETECTED_OS)/5.3/cc
|
||||
IDO := $(TOOLS)/ido-recomp/$(DETECTED_OS)/cc
|
||||
AS := $(MIPS_BINUTILS_PREFIX)as
|
||||
LD := $(MIPS_BINUTILS_PREFIX)ld
|
||||
OBJCOPY := $(MIPS_BINUTILS_PREFIX)objcopy
|
||||
@ -94,7 +130,7 @@ CAT := cat
|
||||
|
||||
ASM_PROC_FLAGS := --input-enc=utf-8 --output-enc=euc-jp --convert-statics=global-with-filename
|
||||
|
||||
SPLAT ?= $(TOOLS)/splat/split.py
|
||||
SPLAT ?= $(PYTHON) $(TOOLS)/splat/split.py
|
||||
SPLAT_YAML ?= $(TARGET).$(VERSION).yaml
|
||||
|
||||
COMPTOOL := $(TOOLS)/comptool.py
|
||||
@ -119,7 +155,14 @@ ifneq ($(RUN_CC_CHECK),0)
|
||||
# The -MMD flags additionaly creates a .d file with the same name as the .o file.
|
||||
CHECK_WARNINGS := -Wno-unused-variable
|
||||
CC_CHECK := $(CC_CHECK_COMP)
|
||||
CC_CHECK_FLAGS := -MMD -MP -fno-builtin -fsyntax-only -funsigned-char -fdiagnostics-color -std=gnu89 -m32 -DNON_MATCHING -DAVOID_UB -DCC_CHECK=1
|
||||
CC_CHECK_FLAGS := -MMD -MP -fno-builtin -fsyntax-only -funsigned-char -fdiagnostics-color -std=gnu89 -DNON_MATCHING -DAVOID_UB -DCC_CHECK=1
|
||||
|
||||
# Ensure that gcc treats the code as 32-bit
|
||||
ifeq ($(UNAME_M),aarch64)
|
||||
CC_CHECK_FLAGS += -march=armv7-a+fp
|
||||
else
|
||||
CC_CHECK_FLAGS += -m32
|
||||
endif
|
||||
ifneq ($(WERROR), 0)
|
||||
CHECK_WARNINGS += -Werror
|
||||
endif
|
||||
@ -231,25 +274,30 @@ all: uncompressed
|
||||
|
||||
init:
|
||||
@$(MAKE) clean
|
||||
@$(MAKE) -s -C tools
|
||||
@$(MAKE) decompress
|
||||
@$(MAKE) extract -j $(N_THREADS)
|
||||
@$(MAKE) all -j $(N_THREADS)
|
||||
@$(MAKE) compressed
|
||||
|
||||
SF := ___ ___\n/ __|| _|\n\__ \| _|\n|___/|_|\n
|
||||
uncompressed: $(ROM)
|
||||
ifneq ($(COMPARE),0)
|
||||
@echo "Calculating Rom Header Checksum..."
|
||||
@echo "$(GREEN)Calculating Rom Header Checksum... $(YELLOW)$<$(NO_COL)"
|
||||
@$(PYTHON) $(COMPTOOL) -r $(ROM) .
|
||||
@md5sum $(ROM)
|
||||
@md5sum -c $(TARGET).$(VERSION).uncompressed.md5
|
||||
@md5sum --status -c $(TARGET).$(VERSION).uncompressed.md5 && \
|
||||
$(PRINT) "$(BLUE)$(TARGET).$(VERSION).uncompressed.z64$(NO_COL): $(GREEN)OK$(NO_COL)\n$(YELLOW) $(SF)" || \
|
||||
$(PRINT) "$(BLUE)$(TARGET).$(VERSION).uncompressed.z64 $(RED)FAILED$(NO_COL)\n"
|
||||
endif
|
||||
|
||||
compressed: $(ROMC)
|
||||
ifeq ($(COMPARE),1)
|
||||
@echo "Calculating Rom Header Checksum..."
|
||||
@echo "$(GREEN)Calculating Rom Header Checksum... $(YELLOW)$<$(NO_COL)"
|
||||
@$(PYTHON) $(COMPTOOL) -r $(ROMC) .
|
||||
@md5sum $(ROMC)
|
||||
@md5sum -c $(TARGET).$(VERSION).md5
|
||||
@md5sum --status -c $(TARGET).$(VERSION).md5 && \
|
||||
$(PRINT) "$(BLUE)$(TARGET).$(VERSION).z64$(NO_COL): $(GREEN)OK$(NO_COL)\n" || \
|
||||
$(PRINT) "$(BLUE)$(TARGET).$(VERSION).z64 $(RED)FAILED$(NO_COL)\n"
|
||||
|
||||
endif
|
||||
|
||||
#### Main Targets ###
|
||||
@ -273,7 +321,7 @@ clean:
|
||||
@git clean -fdx linker_scripts/*.ld
|
||||
|
||||
format:
|
||||
@$(TOOLS)/format.py -j $(N_THREADS)
|
||||
@$(PYTHON) $(TOOLS)/format.py -j $(N_THREADS)
|
||||
|
||||
checkformat:
|
||||
@$(TOOLS)/check_format.sh -j $(N_THREADS)
|
||||
@ -299,53 +347,58 @@ disasm:
|
||||
|
||||
# Final ROM
|
||||
$(ROMC): $(BASEROM_UNCOMPRESSED)
|
||||
@echo "Compressing ROM..."
|
||||
$(call print,Compressing ROM...,$<,$@)
|
||||
@$(PYTHON) $(COMPTOOL) -c $(ROM) $(ROMC)
|
||||
|
||||
# Uncompressed ROM
|
||||
$(ROM): $(ELF)
|
||||
@echo "ELF->ROM:"
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
$(call print,ELF->ROM:,$<,$@)
|
||||
$(V)$(OBJCOPY) -O binary $< $@
|
||||
|
||||
# Link
|
||||
$(ELF): $(LIBULTRA_O) $(O_FILES) $(LD_SCRIPT) $(BUILD_DIR)/linker_scripts/$(VERSION)/hardware_regs.ld $(BUILD_DIR)/linker_scripts/$(VERSION)/undefined_syms.ld $(BUILD_DIR)/linker_scripts/$(VERSION)/pif_syms.ld $(BUILD_DIR)/linker_scripts/$(VERSION)/auto/undefined_syms_auto.ld $(BUILD_DIR)/linker_scripts/$(VERSION)/auto/undefined_funcs_auto.ld
|
||||
@echo "Linking..."
|
||||
$(LD) $(LDFLAGS) -T $(LD_SCRIPT) \
|
||||
$(call print,Linking:,$<,$@)
|
||||
$(V)$(LD) $(LDFLAGS) -T $(LD_SCRIPT) \
|
||||
-T $(BUILD_DIR)/linker_scripts/$(VERSION)/hardware_regs.ld -T $(BUILD_DIR)/linker_scripts/$(VERSION)/undefined_syms.ld -T $(BUILD_DIR)/linker_scripts/$(VERSION)/pif_syms.ld \
|
||||
-T $(BUILD_DIR)/linker_scripts/$(VERSION)/auto/undefined_syms_auto.ld -T $(BUILD_DIR)/linker_scripts/$(VERSION)/auto/undefined_funcs_auto.ld \
|
||||
-Map $(LD_MAP) -o $@
|
||||
|
||||
# PreProcessor
|
||||
$(BUILD_DIR)/%.ld: %.ld
|
||||
$(CPP) $(CPPFLAGS) $(BUILD_DEFINES) $(IINC) $< > $@
|
||||
$(call print,PreProcessor:,$<,$@)
|
||||
$(V)$(CPP) $(CPPFLAGS) $(BUILD_DEFINES) $(IINC) $< > $@
|
||||
|
||||
# Binary
|
||||
$(BUILD_DIR)/%.o: %.bin
|
||||
$(OBJCOPY) -I binary -O elf32-big $< $@
|
||||
$(call print,Binary:,$<,$@)
|
||||
$(V)$(OBJCOPY) -I binary -O elf32-big $< $@
|
||||
|
||||
# Assembly
|
||||
$(BUILD_DIR)/%.o: %.s
|
||||
$(CPP) $(CPPFLAGS) $(BUILD_DEFINES) $(IINC) -I $(dir $*) $(COMMON_DEFINES) $(RELEASE_DEFINES) $(GBI_DEFINES) $(AS_DEFINES) $< | $(ICONV) $(ICONV_FLAGS) | $(AS) $(ASFLAGS) $(ENDIAN) $(IINC) -I $(dir $*) -o $@
|
||||
$(OBJDUMP_CMD)
|
||||
$(call print,Assembling:,$<,$@)
|
||||
$(V)$(CPP) $(CPPFLAGS) $(BUILD_DEFINES) $(IINC) -I $(dir $*) $(COMMON_DEFINES) $(RELEASE_DEFINES) $(GBI_DEFINES) $(AS_DEFINES) $< | $(ICONV) $(ICONV_FLAGS) | $(AS) $(ASFLAGS) $(ENDIAN) $(IINC) -I $(dir $*) -o $@
|
||||
$(V)$(OBJDUMP_CMD)
|
||||
|
||||
# C
|
||||
$(BUILD_DIR)/%.o: %.c
|
||||
$(CC_CHECK) $(CC_CHECK_FLAGS) $(IINC) -I $(dir $*) $(CHECK_WARNINGS) $(BUILD_DEFINES) $(COMMON_DEFINES) $(RELEASE_DEFINES) $(GBI_DEFINES) $(C_DEFINES) $(MIPS_BUILTIN_DEFS) -o $@ $<
|
||||
$(CC) -c $(CFLAGS) $(BUILD_DEFINES) $(IINC) $(WARNINGS) $(MIPS_VERSION) $(ENDIAN) $(COMMON_DEFINES) $(RELEASE_DEFINES) $(GBI_DEFINES) $(C_DEFINES) $(OPTFLAGS) -o $@ $<
|
||||
$(OBJDUMP_CMD)
|
||||
$(RM_MDEBUG)
|
||||
$(call print,Compiling:,$<,$@)
|
||||
@$(CC_CHECK) $(CC_CHECK_FLAGS) $(IINC) -I $(dir $*) $(CHECK_WARNINGS) $(BUILD_DEFINES) $(COMMON_DEFINES) $(RELEASE_DEFINES) $(GBI_DEFINES) $(C_DEFINES) $(MIPS_BUILTIN_DEFS) -o $@ $<
|
||||
$(V)$(CC) -c $(CFLAGS) $(BUILD_DEFINES) $(IINC) $(WARNINGS) $(MIPS_VERSION) $(ENDIAN) $(COMMON_DEFINES) $(RELEASE_DEFINES) $(GBI_DEFINES) $(C_DEFINES) $(OPTFLAGS) -o $@ $<
|
||||
$(V)$(OBJDUMP_CMD)
|
||||
$(V)$(RM_MDEBUG)
|
||||
|
||||
# Patch ll.o
|
||||
build/src/libultra/libc/ll.o: src/libultra/libc/ll.c
|
||||
$(CC_CHECK) $(CC_CHECK_FLAGS) $(IINC) -I $(dir $*) $(CHECK_WARNINGS) $(BUILD_DEFINES) $(COMMON_DEFINES) $(RELEASE_DEFINES) $(GBI_DEFINES) $(C_DEFINES) $(MIPS_BUILTIN_DEFS) -o $@ $<
|
||||
$(CC) -c $(CFLAGS) $(BUILD_DEFINES) $(IINC) $(WARNINGS) $(MIPS_VERSION) $(ENDIAN) $(COMMON_DEFINES) $(RELEASE_DEFINES) $(GBI_DEFINES) $(C_DEFINES) $(OPTFLAGS) -o $@ $<
|
||||
$(PYTHON) tools/set_o32abi_bit.py $@
|
||||
$(OBJDUMP_CMD)
|
||||
$(RM_MDEBUG)
|
||||
$(call print,Patching:,$<,$@)
|
||||
@$(CC_CHECK) $(CC_CHECK_FLAGS) $(IINC) -I $(dir $*) $(CHECK_WARNINGS) $(BUILD_DEFINES) $(COMMON_DEFINES) $(RELEASE_DEFINES) $(GBI_DEFINES) $(C_DEFINES) $(MIPS_BUILTIN_DEFS) -o $@ $<
|
||||
$(V)$(CC) -c $(CFLAGS) $(BUILD_DEFINES) $(IINC) $(WARNINGS) $(MIPS_VERSION) $(ENDIAN) $(COMMON_DEFINES) $(RELEASE_DEFINES) $(GBI_DEFINES) $(C_DEFINES) $(OPTFLAGS) -o $@ $<
|
||||
$(V)$(PYTHON) tools/set_o32abi_bit.py $@
|
||||
$(V)$(OBJDUMP_CMD)
|
||||
$(V)$(RM_MDEBUG)
|
||||
|
||||
-include $(DEP_FILES)
|
||||
|
||||
# Print target for debugging
|
||||
print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true
|
||||
|
||||
.PHONY: all uncompressed compressed clean init extract expected format checkformat decompress context disasm fix_checksum
|
||||
.PHONY: all uncompressed compressed clean init extract expected format checkformat decompress context disasm
|
||||
|
35
tools/Makefile
Normal file
35
tools/Makefile
Normal file
@ -0,0 +1,35 @@
|
||||
CC = gcc
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
$(error Native Windows is currently unsupported for building this repository, use WSL instead c:)
|
||||
else ifeq ($(UNAME_S),Linux)
|
||||
DETECTED_OS := linux
|
||||
else ifeq ($(UNAME_S),Darwin)
|
||||
DETECTED_OS := mac
|
||||
endif
|
||||
|
||||
RECOMP_DIR := ido-recomp/$(DETECTED_OS)
|
||||
|
||||
default: all
|
||||
|
||||
all: recomp mio0
|
||||
|
||||
clean:
|
||||
$(RM) -rf $(RECOMP_DIR)
|
||||
$(RM) mio0
|
||||
|
||||
distclean: clean
|
||||
|
||||
recomp:
|
||||
@echo "Fetching Recomp..."
|
||||
wget https://github.com/decompals/ido-static-recomp/releases/download/v1.0/ido-5.3-recomp-${DETECTED_OS}.tar.gz
|
||||
mkdir -p $(RECOMP_DIR)
|
||||
tar xf ido-5.3-recomp-${DETECTED_OS}.tar.gz -C $(RECOMP_DIR)
|
||||
$(RM) ido-5.3-recomp-${DETECTED_OS}.tar.gz
|
||||
|
||||
mio0:
|
||||
@echo "Building mio0..."
|
||||
$(CC) -Wall -Wextra -Wno-format-overflow -O2 -ffunction-sections -fdata-sections -DMIO0_STANDALONE -s -Wl,--gc-sections -o mio0 mio0-decompressor/libmio0.c
|
||||
|
||||
.PHONY: all clean distclean default
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tools/mio0
BIN
tools/mio0
Binary file not shown.
22
tools/mio0-decompressor/LICENSE
Normal file
22
tools/mio0-decompressor/LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Q
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
6
tools/mio0-decompressor/README.md
Normal file
6
tools/mio0-decompressor/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# mio-decompressor
|
||||
Original Source can be found here: https://github.com/queueRAM/sm64tools
|
||||
|
||||
## License
|
||||
|
||||
MIT License. Copyright 2015 queueRAM.
|
556
tools/mio0-decompressor/libmio0.c
Normal file
556
tools/mio0-decompressor/libmio0.c
Normal file
@ -0,0 +1,556 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "libmio0.h"
|
||||
#include "utils.h"
|
||||
|
||||
// defines
|
||||
|
||||
#define MIO0_VERSION "0.1"
|
||||
|
||||
#define GET_BIT(buf, bit) ((buf)[(bit) / 8] & (1 << (7 - ((bit) % 8))))
|
||||
|
||||
// types
|
||||
typedef struct
|
||||
{
|
||||
int *indexes;
|
||||
int allocated;
|
||||
int count;
|
||||
int start;
|
||||
} lookback;
|
||||
|
||||
// functions
|
||||
#define LOOKBACK_COUNT 256
|
||||
#define LOOKBACK_INIT_SIZE 128
|
||||
static lookback *lookback_init(void)
|
||||
{
|
||||
lookback *lb = malloc(LOOKBACK_COUNT * sizeof(*lb));
|
||||
for (int i = 0; i < LOOKBACK_COUNT; i++) {
|
||||
lb[i].allocated = LOOKBACK_INIT_SIZE;
|
||||
lb[i].indexes = malloc(lb[i].allocated * sizeof(*lb[i].indexes));
|
||||
lb[i].count = 0;
|
||||
lb[i].start = 0;
|
||||
}
|
||||
return lb;
|
||||
}
|
||||
|
||||
static void lookback_free(lookback *lb)
|
||||
{
|
||||
for (int i = 0; i < LOOKBACK_COUNT; i++) {
|
||||
free(lb[i].indexes);
|
||||
}
|
||||
free(lb);
|
||||
}
|
||||
|
||||
static inline void lookback_push(lookback *lkbk, unsigned char val, int index)
|
||||
{
|
||||
lookback *lb = &lkbk[val];
|
||||
if (lb->count == lb->allocated) {
|
||||
lb->allocated *= 4;
|
||||
lb->indexes = realloc(lb->indexes, lb->allocated * sizeof(*lb->indexes));
|
||||
}
|
||||
lb->indexes[lb->count++] = index;
|
||||
}
|
||||
|
||||
static void PUT_BIT(unsigned char *buf, int bit, int val)
|
||||
{
|
||||
unsigned char mask = 1 << (7 - (bit % 8));
|
||||
unsigned int offset = bit / 8;
|
||||
buf[offset] = (buf[offset] & ~(mask)) | (val ? mask : 0);
|
||||
}
|
||||
|
||||
// used to find longest matching stream in buffer
|
||||
// buf: buffer
|
||||
// start_offset: offset in buf to look back from
|
||||
// max_search: max number of bytes to find
|
||||
// found_offset: returned offset found (0 if none found)
|
||||
// returns max length of matching stream (0 if none found)
|
||||
static int find_longest(const unsigned char *buf, int start_offset, int max_search, int *found_offset, lookback *lkbk)
|
||||
{
|
||||
int best_length = 0;
|
||||
int best_offset = 0;
|
||||
int cur_length;
|
||||
int search_len;
|
||||
int farthest, off, i;
|
||||
int lb_idx;
|
||||
const unsigned char first = buf[start_offset];
|
||||
lookback *lb = &lkbk[first];
|
||||
|
||||
// buf
|
||||
// | off start max
|
||||
// V |+i-> |+i-> |
|
||||
// |--------------raw-data-----------------|
|
||||
// |+i-> | |+i->
|
||||
// +cur_length
|
||||
|
||||
// check at most the past 4096 values
|
||||
farthest = MAX(start_offset - 4096, 0);
|
||||
// find starting index
|
||||
for (lb_idx = lb->start; lb_idx < lb->count && lb->indexes[lb_idx] < farthest; lb_idx++) {}
|
||||
lb->start = lb_idx;
|
||||
for ( ; lb_idx < lb->count && lb->indexes[lb_idx] < start_offset; lb_idx++) {
|
||||
off = lb->indexes[lb_idx];
|
||||
// check at most requested max or up until start
|
||||
search_len = MIN(max_search, start_offset - off);
|
||||
for (i = 0; i < search_len; i++) {
|
||||
if (buf[start_offset + i] != buf[off + i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
cur_length = i;
|
||||
// if matched up until start, continue matching in already matched parts
|
||||
if (cur_length == search_len) {
|
||||
// check at most requested max less current length
|
||||
search_len = max_search - cur_length;
|
||||
for (i = 0; i < search_len; i++) {
|
||||
if (buf[start_offset + cur_length + i] != buf[off + i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
cur_length += i;
|
||||
}
|
||||
if (cur_length > best_length) {
|
||||
best_offset = start_offset - off;
|
||||
best_length = cur_length;
|
||||
}
|
||||
}
|
||||
|
||||
// return best reverse offset and length (may be 0)
|
||||
*found_offset = best_offset;
|
||||
return best_length;
|
||||
}
|
||||
|
||||
// decode MIO0 header
|
||||
// returns 1 if valid header, 0 otherwise
|
||||
int mio0_decode_header(const unsigned char *buf, mio0_header_t *head)
|
||||
{
|
||||
if (!memcmp(buf, "MIO0", 4)) {
|
||||
head->dest_size = read_u32_be(&buf[4]);
|
||||
head->comp_offset = read_u32_be(&buf[8]);
|
||||
head->uncomp_offset = read_u32_be(&buf[12]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mio0_encode_header(unsigned char *buf, const mio0_header_t *head)
|
||||
{
|
||||
memcpy(buf, "MIO0", 4);
|
||||
write_u32_be(&buf[4], head->dest_size);
|
||||
write_u32_be(&buf[8], head->comp_offset);
|
||||
write_u32_be(&buf[12], head->uncomp_offset);
|
||||
}
|
||||
|
||||
int mio0_decode(const unsigned char *in, unsigned char *out, unsigned int *end)
|
||||
{
|
||||
mio0_header_t head;
|
||||
unsigned int bytes_written = 0;
|
||||
int bit_idx = 0;
|
||||
int comp_idx = 0;
|
||||
int uncomp_idx = 0;
|
||||
int valid;
|
||||
|
||||
// extract header
|
||||
valid = mio0_decode_header(in, &head);
|
||||
// verify MIO0 header
|
||||
if (!valid) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
// decode data
|
||||
while (bytes_written < head.dest_size) {
|
||||
if (GET_BIT(&in[MIO0_HEADER_LENGTH], bit_idx)) {
|
||||
// 1 - pull uncompressed data
|
||||
out[bytes_written] = in[head.uncomp_offset + uncomp_idx];
|
||||
bytes_written++;
|
||||
uncomp_idx++;
|
||||
} else {
|
||||
// 0 - read compressed data
|
||||
int idx;
|
||||
int length;
|
||||
int i;
|
||||
const unsigned char *vals = &in[head.comp_offset + comp_idx];
|
||||
comp_idx += 2;
|
||||
length = ((vals[0] & 0xF0) >> 4) + 3;
|
||||
idx = ((vals[0] & 0x0F) << 8) + vals[1] + 1;
|
||||
for (i = 0; i < length; i++) {
|
||||
out[bytes_written] = out[bytes_written - idx];
|
||||
bytes_written++;
|
||||
}
|
||||
}
|
||||
bit_idx++;
|
||||
}
|
||||
|
||||
if (end) {
|
||||
*end = head.uncomp_offset + uncomp_idx;
|
||||
}
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int mio0_encode(const unsigned char *in, unsigned int length, unsigned char *out)
|
||||
{
|
||||
unsigned char *bit_buf;
|
||||
unsigned char *comp_buf;
|
||||
unsigned char *uncomp_buf;
|
||||
unsigned int bit_length;
|
||||
unsigned int comp_offset;
|
||||
unsigned int uncomp_offset;
|
||||
unsigned int bytes_proc = 0;
|
||||
int bytes_written;
|
||||
int bit_idx = 0;
|
||||
int comp_idx = 0;
|
||||
int uncomp_idx = 0;
|
||||
lookback *lookbacks;
|
||||
|
||||
// initialize lookback buffer
|
||||
lookbacks = lookback_init();
|
||||
|
||||
// allocate some temporary buffers worst case size
|
||||
bit_buf = malloc((length + 7) / 8); // 1-bit/byte
|
||||
comp_buf = malloc(length); // 16-bits/2bytes
|
||||
uncomp_buf = malloc(length); // all uncompressed
|
||||
memset(bit_buf, 0, (length + 7) / 8);
|
||||
|
||||
// encode data
|
||||
// special case for first byte
|
||||
lookback_push(lookbacks, in[0], 0);
|
||||
uncomp_buf[uncomp_idx] = in[0];
|
||||
uncomp_idx += 1;
|
||||
bytes_proc += 1;
|
||||
PUT_BIT(bit_buf, bit_idx++, 1);
|
||||
while (bytes_proc < length) {
|
||||
int offset;
|
||||
int max_length = MIN(length - bytes_proc, 18);
|
||||
int longest_match = find_longest(in, bytes_proc, max_length, &offset, lookbacks);
|
||||
// push current byte before checking next longer match
|
||||
lookback_push(lookbacks, in[bytes_proc], bytes_proc);
|
||||
if (longest_match > 2) {
|
||||
int lookahead_offset;
|
||||
// lookahead to next byte to see if longer match
|
||||
int lookahead_length = MIN(length - bytes_proc - 1, 18);
|
||||
int lookahead_match = find_longest(in, bytes_proc + 1, lookahead_length, &lookahead_offset, lookbacks);
|
||||
// better match found, use uncompressed + lookahead compressed
|
||||
if ((longest_match + 1) < lookahead_match) {
|
||||
// uncompressed byte
|
||||
uncomp_buf[uncomp_idx] = in[bytes_proc];
|
||||
uncomp_idx++;
|
||||
PUT_BIT(bit_buf, bit_idx, 1);
|
||||
bytes_proc++;
|
||||
longest_match = lookahead_match;
|
||||
offset = lookahead_offset;
|
||||
bit_idx++;
|
||||
lookback_push(lookbacks, in[bytes_proc], bytes_proc);
|
||||
}
|
||||
// first byte already pushed above
|
||||
for (int i = 1; i < longest_match; i++) {
|
||||
lookback_push(lookbacks, in[bytes_proc + i], bytes_proc + i);
|
||||
}
|
||||
// compressed block
|
||||
comp_buf[comp_idx] = (((longest_match - 3) & 0x0F) << 4) |
|
||||
(((offset - 1) >> 8) & 0x0F);
|
||||
comp_buf[comp_idx + 1] = (offset - 1) & 0xFF;
|
||||
comp_idx += 2;
|
||||
PUT_BIT(bit_buf, bit_idx, 0);
|
||||
bytes_proc += longest_match;
|
||||
} else {
|
||||
// uncompressed byte
|
||||
uncomp_buf[uncomp_idx] = in[bytes_proc];
|
||||
uncomp_idx++;
|
||||
PUT_BIT(bit_buf, bit_idx, 1);
|
||||
bytes_proc++;
|
||||
}
|
||||
bit_idx++;
|
||||
}
|
||||
|
||||
// compute final sizes and offsets
|
||||
// +7 so int division accounts for all bits
|
||||
bit_length = ((bit_idx + 7) / 8);
|
||||
// compressed data after control bits and aligned to 4-byte boundary
|
||||
comp_offset = ALIGN(MIO0_HEADER_LENGTH + bit_length, 4);
|
||||
uncomp_offset = comp_offset + comp_idx;
|
||||
bytes_written = uncomp_offset + uncomp_idx;
|
||||
|
||||
// output header
|
||||
memcpy(out, "MIO0", 4);
|
||||
write_u32_be(&out[4], length);
|
||||
write_u32_be(&out[8], comp_offset);
|
||||
write_u32_be(&out[12], uncomp_offset);
|
||||
// output data
|
||||
memcpy(&out[MIO0_HEADER_LENGTH], bit_buf, bit_length);
|
||||
memcpy(&out[comp_offset], comp_buf, comp_idx);
|
||||
memcpy(&out[uncomp_offset], uncomp_buf, uncomp_idx);
|
||||
|
||||
// free allocated buffers
|
||||
free(bit_buf);
|
||||
free(comp_buf);
|
||||
free(uncomp_buf);
|
||||
lookback_free(lookbacks);
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int mio0_decode_file(const char *in_file, unsigned long offset, const char *out_file)
|
||||
{
|
||||
mio0_header_t head;
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
unsigned char *in_buf = NULL;
|
||||
unsigned char *out_buf = NULL;
|
||||
long file_size;
|
||||
int ret_val = 0;
|
||||
size_t bytes_read;
|
||||
int bytes_decoded;
|
||||
int bytes_written;
|
||||
int valid;
|
||||
|
||||
in = fopen(in_file, "rb");
|
||||
if (in == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// allocate buffer to read from offset to end of file
|
||||
fseek(in, 0, SEEK_END);
|
||||
file_size = ftell(in);
|
||||
in_buf = malloc(file_size - offset);
|
||||
fseek(in, offset, SEEK_SET);
|
||||
|
||||
// read bytes
|
||||
bytes_read = fread(in_buf, 1, file_size - offset, in);
|
||||
if (bytes_read != file_size - offset) {
|
||||
ret_val = 2;
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
// verify header
|
||||
valid = mio0_decode_header(in_buf, &head);
|
||||
if (!valid) {
|
||||
ret_val = 3;
|
||||
goto free_all;
|
||||
}
|
||||
out_buf = malloc(head.dest_size);
|
||||
|
||||
// decompress MIO0 encoded data
|
||||
bytes_decoded = mio0_decode(in_buf, out_buf, NULL);
|
||||
if (bytes_decoded < 0) {
|
||||
ret_val = 3;
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
// open output file
|
||||
out = fopen(out_file, "wb");
|
||||
if (out == NULL) {
|
||||
ret_val = 4;
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
// write data to file
|
||||
bytes_written = fwrite(out_buf, 1, bytes_decoded, out);
|
||||
if (bytes_written != bytes_decoded) {
|
||||
ret_val = 5;
|
||||
}
|
||||
|
||||
// clean up
|
||||
fclose(out);
|
||||
free_all:
|
||||
if (out_buf) {
|
||||
free(out_buf);
|
||||
}
|
||||
if (in_buf) {
|
||||
free(in_buf);
|
||||
}
|
||||
fclose(in);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int mio0_encode_file(const char *in_file, const char *out_file)
|
||||
{
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
unsigned char *in_buf = NULL;
|
||||
unsigned char *out_buf = NULL;
|
||||
size_t file_size;
|
||||
size_t bytes_read;
|
||||
int bytes_encoded;
|
||||
int bytes_written;
|
||||
int ret_val = 0;
|
||||
|
||||
in = fopen(in_file, "rb");
|
||||
if (in == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// allocate buffer to read entire contents of files
|
||||
fseek(in, 0, SEEK_END);
|
||||
file_size = ftell(in);
|
||||
fseek(in, 0, SEEK_SET);
|
||||
in_buf = malloc(file_size);
|
||||
|
||||
// read bytes
|
||||
bytes_read = fread(in_buf, 1, file_size, in);
|
||||
if (bytes_read != file_size) {
|
||||
ret_val = 2;
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
// allocate worst case length
|
||||
out_buf = malloc(MIO0_HEADER_LENGTH + ((file_size+7)/8) + file_size);
|
||||
|
||||
// compress data in MIO0 format
|
||||
bytes_encoded = mio0_encode(in_buf, file_size, out_buf);
|
||||
|
||||
// open output file
|
||||
out = fopen(out_file, "wb");
|
||||
if (out == NULL) {
|
||||
ret_val = 4;
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
// write data to file
|
||||
bytes_written = fwrite(out_buf, 1, bytes_encoded, out);
|
||||
if (bytes_written != bytes_encoded) {
|
||||
ret_val = 5;
|
||||
}
|
||||
|
||||
// clean up
|
||||
fclose(out);
|
||||
free_all:
|
||||
if (out_buf) {
|
||||
free(out_buf);
|
||||
}
|
||||
if (in_buf) {
|
||||
free(in_buf);
|
||||
}
|
||||
fclose(in);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
// mio0 standalone executable
|
||||
#ifdef MIO0_STANDALONE
|
||||
typedef struct
|
||||
{
|
||||
char *in_filename;
|
||||
char *out_filename;
|
||||
unsigned int offset;
|
||||
int compress;
|
||||
} arg_config;
|
||||
|
||||
static arg_config default_config =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
1
|
||||
};
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
ERROR("Usage: mio0 [-c / -d] [-o OFFSET] FILE [OUTPUT]\n"
|
||||
"\n"
|
||||
"mio0 v" MIO0_VERSION ": MIO0 compression and decompression tool\n"
|
||||
"\n"
|
||||
"Optional arguments:\n"
|
||||
" -c compress raw data into MIO0 (default: compress)\n"
|
||||
" -d decompress MIO0 into raw data\n"
|
||||
" -o OFFSET starting offset in FILE (default: 0)\n"
|
||||
"\n"
|
||||
"File arguments:\n"
|
||||
" FILE input file\n"
|
||||
" [OUTPUT] output file (default: FILE.out)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// parse command line arguments
|
||||
static void parse_arguments(int argc, char *argv[], arg_config *config)
|
||||
{
|
||||
int i;
|
||||
int file_count = 0;
|
||||
if (argc < 2) {
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
switch (argv[i][1]) {
|
||||
case 'c':
|
||||
config->compress = 1;
|
||||
break;
|
||||
case 'd':
|
||||
config->compress = 0;
|
||||
break;
|
||||
case 'o':
|
||||
if (++i >= argc) {
|
||||
print_usage();
|
||||
}
|
||||
config->offset = strtoul(argv[i], NULL, 0);
|
||||
break;
|
||||
default:
|
||||
print_usage();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (file_count) {
|
||||
case 0:
|
||||
config->in_filename = argv[i];
|
||||
break;
|
||||
case 1:
|
||||
config->out_filename = argv[i];
|
||||
break;
|
||||
default: // too many
|
||||
print_usage();
|
||||
break;
|
||||
}
|
||||
file_count++;
|
||||
}
|
||||
}
|
||||
if (file_count < 1) {
|
||||
print_usage();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char out_filename[FILENAME_MAX];
|
||||
arg_config config;
|
||||
int ret_val;
|
||||
|
||||
// get configuration from arguments
|
||||
config = default_config;
|
||||
parse_arguments(argc, argv, &config);
|
||||
if (config.out_filename == NULL) {
|
||||
config.out_filename = out_filename;
|
||||
sprintf(config.out_filename, "%s.out", config.in_filename);
|
||||
}
|
||||
|
||||
// operation
|
||||
if (config.compress) {
|
||||
ret_val = mio0_encode_file(config.in_filename, config.out_filename);
|
||||
} else {
|
||||
ret_val = mio0_decode_file(config.in_filename, config.offset, config.out_filename);
|
||||
}
|
||||
|
||||
switch (ret_val) {
|
||||
case 1:
|
||||
ERROR("Error opening input file \"%s\"\n", config.in_filename);
|
||||
break;
|
||||
case 2:
|
||||
ERROR("Error reading from input file \"%s\"\n", config.in_filename);
|
||||
break;
|
||||
case 3:
|
||||
ERROR("Error decoding MIO0 data. Wrong offset (0x%X)?\n", config.offset);
|
||||
break;
|
||||
case 4:
|
||||
ERROR("Error opening output file \"%s\"\n", config.out_filename);
|
||||
break;
|
||||
case 5:
|
||||
ERROR("Error writing bytes to output file \"%s\"\n", config.out_filename);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
#endif // MIO0_STANDALONE
|
||||
|
50
tools/mio0-decompressor/libmio0.h
Normal file
50
tools/mio0-decompressor/libmio0.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef LIBMIO0_H_
|
||||
#define LIBMIO0_H_
|
||||
|
||||
// defines
|
||||
|
||||
#define MIO0_HEADER_LENGTH 16
|
||||
|
||||
// typedefs
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int dest_size;
|
||||
unsigned int comp_offset;
|
||||
unsigned int uncomp_offset;
|
||||
} mio0_header_t;
|
||||
|
||||
// function prototypes
|
||||
|
||||
// decode MIO0 header
|
||||
// returns 1 if valid header, 0 otherwise
|
||||
int mio0_decode_header(const unsigned char *buf, mio0_header_t *head);
|
||||
|
||||
// encode MIO0 header from struct
|
||||
void mio0_encode_header(unsigned char *buf, const mio0_header_t *head);
|
||||
|
||||
// decode MIO0 data in memory
|
||||
// in: buffer containing MIO0 data
|
||||
// out: buffer for output data
|
||||
// end: output offset of the last byte decoded from in (set to NULL if unwanted)
|
||||
// returns bytes extracted to 'out' or negative value on failure
|
||||
int mio0_decode(const unsigned char *in, unsigned char *out, unsigned int *end);
|
||||
|
||||
// encode MIO0 data in memory
|
||||
// in: buffer containing raw data
|
||||
// out: buffer for MIO0 data
|
||||
// returns size of compressed data in 'out' including MIO0 header
|
||||
int mio0_encode(const unsigned char *in, unsigned int length, unsigned char *out);
|
||||
|
||||
// decode an entire MIO0 block at an offset from file to output file
|
||||
// in_file: input filename
|
||||
// offset: offset to start decoding from in_file
|
||||
// out_file: output filename
|
||||
int mio0_decode_file(const char *in_file, unsigned long offset, const char *out_file);
|
||||
|
||||
// encode an entire file
|
||||
// in_file: input filename containing raw data to be encoded
|
||||
// out_file: output filename to write MIO0 compressed data to
|
||||
int mio0_encode_file(const char *in_file, const char *out_file);
|
||||
|
||||
#endif // LIBMIO0_H_
|
153
tools/mio0-decompressor/utils.h
Normal file
153
tools/mio0-decompressor/utils.h
Normal file
@ -0,0 +1,153 @@
|
||||
#ifndef UTILS_H_
|
||||
#define UTILS_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// defines
|
||||
|
||||
// printing size_t varies by compiler
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define SIZE_T_FORMAT "%Iu"
|
||||
#else
|
||||
#define SIZE_T_FORMAT "%zu"
|
||||
#endif
|
||||
|
||||
#define KB 1024
|
||||
#define MB (1024 * KB)
|
||||
|
||||
// number of elements in statically declared array
|
||||
#define DIM(S_ARR_) (sizeof(S_ARR_) / sizeof(S_ARR_[0]))
|
||||
|
||||
#define MIN(A_, B_) ((A_) < (B_) ? (A_) : (B_))
|
||||
#define MAX(A_, B_) ((A_) > (B_) ? (A_) : (B_))
|
||||
|
||||
// align value to N-byte boundary
|
||||
#define ALIGN(VAL_, ALIGNMENT_) (((VAL_) + ((ALIGNMENT_) - 1)) & ~((ALIGNMENT_) - 1))
|
||||
|
||||
// read/write u32/16 big/little endian
|
||||
#define read_u32_be(buf) (unsigned int)(((buf)[0] << 24) + ((buf)[1] << 16) + ((buf)[2] << 8) + ((buf)[3]))
|
||||
#define read_u32_le(buf) (unsigned int)(((buf)[1] << 24) + ((buf)[0] << 16) + ((buf)[3] << 8) + ((buf)[2]))
|
||||
#define write_u32_be(buf, val) do { \
|
||||
(buf)[0] = ((val) >> 24) & 0xFF; \
|
||||
(buf)[1] = ((val) >> 16) & 0xFF; \
|
||||
(buf)[2] = ((val) >> 8) & 0xFF; \
|
||||
(buf)[3] = (val) & 0xFF; \
|
||||
} while(0)
|
||||
#define read_u16_be(buf) (((buf)[0] << 8) + ((buf)[1]))
|
||||
#define write_u16_be(buf, val) do { \
|
||||
(buf)[0] = ((val) >> 8) & 0xFF; \
|
||||
(buf)[1] = ((val)) & 0xFF; \
|
||||
} while(0)
|
||||
|
||||
// print nibbles and bytes
|
||||
#define fprint_nibble(FP, NIB_) fputc((NIB_) < 10 ? ('0' + (NIB_)) : ('A' + (NIB_) - 0xA), FP)
|
||||
#define fprint_byte(FP, BYTE_) do { \
|
||||
fprint_nibble(FP, (BYTE_) >> 4); \
|
||||
fprint_nibble(FP, (BYTE_) & 0x0F); \
|
||||
} while(0)
|
||||
#define print_nibble(NIB_) fprint_nibble(stdout, NIB_)
|
||||
#define print_byte(BYTE_) fprint_byte(stdout, BYTE_)
|
||||
|
||||
// Windows compatibility
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#include <direct.h>
|
||||
#define mkdir(DIR_, PERM_) _mkdir(DIR_)
|
||||
#ifndef strcasecmp
|
||||
#define strcasecmp(A, B) stricmp(A, B)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// typedefs
|
||||
|
||||
#define MAX_DIR_FILES 128
|
||||
typedef struct
|
||||
{
|
||||
char *files[MAX_DIR_FILES];
|
||||
int count;
|
||||
} dir_list;
|
||||
|
||||
// global verbosity setting
|
||||
extern int g_verbosity;
|
||||
|
||||
#define ERROR(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define INFO(...) if (g_verbosity) printf(__VA_ARGS__)
|
||||
#define INFO_HEX(...) if (g_verbosity) print_hex(__VA_ARGS__)
|
||||
|
||||
// functions
|
||||
|
||||
// convert two bytes in big-endian to signed int
|
||||
int read_s16_be(unsigned char *buf);
|
||||
|
||||
// convert four bytes in big-endian to float
|
||||
float read_f32_be(unsigned char *buf);
|
||||
|
||||
// determine if value is power of 2
|
||||
// returns 1 if val is power of 2, 0 otherwise
|
||||
int is_power2(unsigned int val);
|
||||
|
||||
// print buffer as hex bytes
|
||||
// fp: file pointer
|
||||
// buf: buffer to read bytes from
|
||||
// length: length of buffer to print
|
||||
void fprint_hex(FILE *fp, const unsigned char *buf, int length);
|
||||
void fprint_hex_source(FILE *fp, const unsigned char *buf, int length);
|
||||
void print_hex(const unsigned char *buf, int length);
|
||||
|
||||
// perform byteswapping to convert from v64 to z64 ordering
|
||||
void swap_bytes(unsigned char *data, long length);
|
||||
|
||||
// reverse endian to convert from n64 to z64 ordering
|
||||
void reverse_endian(unsigned char *data, long length);
|
||||
|
||||
// get size of file without opening it;
|
||||
// returns file size or negative on error
|
||||
long filesize(const char *file_name);
|
||||
|
||||
// update file timestamp to now, creating it if it doesn't exist
|
||||
void touch_file(const char *filename);
|
||||
|
||||
// read entire contents of file into buffer
|
||||
// returns file size or negative on error
|
||||
long read_file(const char *file_name, unsigned char **data);
|
||||
|
||||
// write buffer to file
|
||||
// returns number of bytes written out or -1 on failure
|
||||
long write_file(const char *file_name, unsigned char *data, long length);
|
||||
|
||||
// generate an output file name from input name by replacing file extension
|
||||
// in_name: input file name
|
||||
// out_name: buffer to write output name in
|
||||
// extension: new file extension to use
|
||||
void generate_filename(const char *in_name, char *out_name, char *extension);
|
||||
|
||||
// extract base filename from file path
|
||||
// name: path to file
|
||||
// returns just the file name after the last '/'
|
||||
char *basename(const char *name);
|
||||
|
||||
// make a directory if it doesn't exist
|
||||
// dir_name: name of the directory
|
||||
void make_dir(const char *dir_name);
|
||||
|
||||
// copy a file from src_name to dst_name. will not make directories
|
||||
// src_name: source file name
|
||||
// dst_name: destination file name
|
||||
long copy_file(const char *src_name, const char *dst_name);
|
||||
|
||||
// list a directory, optionally filtering files by extension
|
||||
// dir: directory to list files in
|
||||
// extension: extension to filter files by (NULL if no filtering)
|
||||
// list: output list and count
|
||||
void dir_list_ext(const char *dir, const char *extension, dir_list *list);
|
||||
|
||||
// free associated date from a directory list
|
||||
// list: directory list filled in by dir_list_ext() call
|
||||
void dir_list_free(dir_list *list);
|
||||
|
||||
// determine if a string ends with another string
|
||||
// str: string to check if ends with 'suffix'
|
||||
// suffix: string to see if 'str' ends with
|
||||
// returns 1 if 'str' ends with 'suffix'
|
||||
int str_ends_with(const char *str, const char *suffix);
|
||||
|
||||
#endif // UTILS_H_
|
Loading…
Reference in New Issue
Block a user