diff --git a/Makefile b/Makefile index d217c26fa..d55700696 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ libsyscall: $(MAKE) -C lib/syscall all install INSTALL_PREFIX=$(abspath $(WORK_DIR)/$(BUILD_DIR)) GAME_CODE=$(GAME_CODE) $(SBIN_LZ): $(BUILD_DIR)/component.files - $(WINE) $(COMPSTATIC) -9 -c -f $< + $(COMPSTATIC) -9 -c -f $< $(BUILD_DIR)/component.files: main ; diff --git a/common.mk b/common.mk index bdc21608d..6c27499ec 100644 --- a/common.mk +++ b/common.mk @@ -25,7 +25,6 @@ MAKEROM := $(TOOLSDIR)/bin/makerom.exe MAKELCF := $(TOOLSDIR)/bin/makelcf.exe MAKEBNR := $(TOOLSDIR)/bin/makebanner.exe NTRCOMP := $(TOOLSDIR)/bin/ntrcomp.exe -COMPSTATIC := $(TOOLSDIR)/bin/compstatic.exe export LM_LICENSE_FILE := $(TOOLSDIR)/mwccarm/license.dat @@ -40,6 +39,9 @@ ASPATCH := $(TOOLSDIR)/mwasmarm_patcher/mwasmarm_patcher$(EXE) CSV2BIN := $(TOOLSDIR)/csv2bin/csv2bin$(EXE) MKFXCONST := $(TOOLSDIR)/gen_fx_consts/gen_fx_consts$(EXE) +# Decompiled NitroSDK tools +COMPSTATIC := $(TOOLSDIR)/compstatic/compstatic$(EXE) + NTRMERGE := $(TOOLSDIR)/ntr_merge_elf/ntr_merge_elf.sh NATIVE_TOOLS := \ @@ -51,7 +53,8 @@ NATIVE_TOOLS := \ $(MSGENC) \ $(ASPATCH) \ $(CSV2BIN) \ - $(MKFXCONST) + $(MKFXCONST) \ + $(COMPSTATIC) TOOLDIRS := $(foreach tool,$(NATIVE_TOOLS),$(dir $(tool))) diff --git a/tools/compstatic/.gitignore b/tools/compstatic/.gitignore new file mode 100644 index 000000000..0ccf76ad4 --- /dev/null +++ b/tools/compstatic/.gitignore @@ -0,0 +1 @@ +compstatic diff --git a/tools/compstatic/Makefile b/tools/compstatic/Makefile new file mode 100644 index 000000000..4352c0cbe --- /dev/null +++ b/tools/compstatic/Makefile @@ -0,0 +1,32 @@ +CC := gcc +CFLAGS := -O3 + +SRCS = $(wildcard *.c) +OBJS = $(SRCS:%.c=%.o) + +prog := compstatic + +.PHONY: all clean + +DEPDIR := .deps +DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d + +all: $(prog) + @: + +clean: + $(RM) -r $(DEPDIR) $(OBJS) $(prog) $(prog).exe + +$(prog): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $^ + +$(OBJS): %.o: %.c +$(OBJS): %.o: %.c $(DEPDIR)/%.d | $(DEPDIR) + $(CC) $(CFLAGS) $(DEPFLAGS) -c -o $@ $< + +$(DEPDIR): ; @mkdir -p $@ + +DEPFILES := $(SRCS:%.c=$(DEPDIR)/%.d) +$(DEPFILES): + +include $(wildcard $(DEPFILES)) diff --git a/tools/compstatic/component.c b/tools/compstatic/component.c new file mode 100644 index 000000000..0df9d3a57 --- /dev/null +++ b/tools/compstatic/component.c @@ -0,0 +1,187 @@ +#include +#include +#include "component.h" +#include "compress.h" +#include "global.h" +#include "overlay_defs.h" +#include "overlay_modules.h" +#include "overlay_table.h" +#include "print.h" +#include "static_module.h" +#include "str.h" + +static bool ReadComponent(Component *component, char *staticModule, char *overlayDefs, char *overlayTable); +static bool WriteComponent(Component *component); +static bool ComponentAddSuffix(Component *component, char *suffix); +static void FreeComponent(Component *component); +static void DebugPrintComponent(Component *component); + +bool ConvertComponent(Options *options, char *staticModule, char *overlayDefs, char *overlayTable) { + bool finalSuccess = false; + Component component; + memset(&component, 0, sizeof(component)); + + bool success = ReadComponent(&component, staticModule, overlayDefs, overlayTable); + if (success) { + DebugPrintComponent(&component); + if (!options->overlayModules || CompressOverlayModules(&component)) { + if (!options->overlayDigest || CalculateHMAC_OverlayModules(&component, options->digestType, options->digestKey)) { + if (!options->staticModule || CompressStaticModule(&component, options->headerSize)) { + if (options->outSuffix == NULL || ComponentAddSuffix(&component, options->outSuffix)) { + DebugPrintComponent(&component); + finalSuccess = WriteComponent(&component); + } + } + } + } + } + + FreeComponent(&component); + return finalSuccess; +} + +static bool ReadComponent(Component *component, char *staticModule, char *overlayDefs, char *overlayTable) { + bool finalSuccess = false; + + bool success = ReadOverlayDefs(component, overlayDefs); + if (success) { + success = ReadStaticModule(component, staticModule); + if (success) { + success = ReadOverlayTable(component, overlayTable); + if (success) { + finalSuccess = ReadOverlayModules(component); + } + } + } + + return finalSuccess; +} + +static bool WriteComponent(Component *component) { + bool finalSuccess = false; + + bool success = WriteOverlayDefs(component); + if (success) { + success = WriteStaticModule(component); + if (success) { + success = WriteOverlayTable(component); + if (success) { + finalSuccess = WriteOverlayModules(component); + } + } + } + + return finalSuccess; +} + +static bool ComponentAddSuffix(Component *component, char *suffix) { + if (suffix == NULL || *suffix == '\0') return true; + + bool finalSuccess = false; + bool success = AddSuffixOverlayDefs(component, suffix); + if (success) { + success = AddSuffixStaticModule(component, suffix); + if (success) { + success = AddSuffixOverlayTable(component, suffix); + if (success) { + finalSuccess = AddSuffixOverlayModules(component, suffix); + } + } + } + + return finalSuccess; +} + +static void FreeComponent(Component *component) { + FreeOverlayDefs(component); + FreeStaticModule(component); + FreeOverlayTable(component); + FreeOverlayModules(component); + component->numOverlays = 0; +} + +static void DebugPrintComponent(Component *component) { + int i; + char *filename; + char c; + uint staticParamsOffset = *(uint *)(component->staticModule.footerContent + 4); + StaticParams *staticParams = (StaticParams *)(component->staticModule.fileInfo.content + staticParamsOffset); + + DebugPrintf("-------------------------------------------------------------\n"); + + DebugPrintf("static.buffer.filename = %s\n" + " .ptr = %p\n" + " .size = %p %9d\n" + " .compressed = %p %9d\n" + " .footerSize = %p %9d\n" + " .footer = %p\n" + " .footer.magicNumber = %08x %9s\n" + " .staticParamsOffset = %p %9d\n" + " .digestParamsOffset = %p %9d\n", + component->staticModule.fileInfo.filename, + component->staticModule.fileInfo.content, + component->staticModule.fileInfo.fileSize, component->staticModule.fileInfo.fileSize, + component->staticModule.fileInfo.compressedSize, component->staticModule.fileInfo.compressedSize, + component->staticModule.footerSize, component->staticModule.footerSize, + component->staticModule.footerContent, + *(uint *)(component->staticModule.footerContent + STATIC_FOOTER_MAGIC_OFFSET), (*(uint *)(component->staticModule.footerContent + STATIC_FOOTER_MAGIC_OFFSET) == 0xDEC00621) ? "NITROCODE" : "UNKNOWN!!", + *(uint *)(component->staticModule.footerContent + STATIC_FOOTER_STATIC_PARAM_OFFSET), *(uint *)(component->staticModule.footerContent + STATIC_FOOTER_STATIC_PARAM_OFFSET), + *(uint *)(component->staticModule.footerContent + STATIC_FOOTER_DIGEST_PARAM_OFFSET), *(uint *)(component->staticModule.footerContent + STATIC_FOOTER_DIGEST_PARAM_OFFSET)); + + DebugPrintf("static.Params = %p\n" + " .Params.autoloadList = %p\n" + " .Params.autoloadListEnd = %p\n" + " .Params.autoloadStart = %p\n" + " .Params.staticBssStart = %p\n" + " .Params.staticBssEnd = %p\n" + " .Params.compressedStatic = %p\n", + component->staticModule.fileInfo.content + staticParamsOffset, + staticParams->autoloadList, + staticParams->autoloadListEnd, + staticParams->autoloadStart, + staticParams->staticBssStart, + staticParams->staticBssEnd, + staticParams->compressedStatic); + + DebugPrintf("-------------------------------------------------------------\n"); + DebugPrintf("numOverlays = %d\n", component->numOverlays); + DebugPrintf("-------------------------------------------------------------\n"); + + DebugPrintf("ovdefs.buffer.filename = %s\n" + " .ptr = %p\n" + " .size = %p %9d\n" + " .compressed = %p %9d\n" + " .header = %p\n" + " .header.loadAddress = %p\n" + " .header.entryAddress = %p\n" + " .header.moduleSize = %p %9d\n" + " .header.autoloadDone = %p\n" + " .dirname = %s\n", + component->overlayDefs.fileInfo.filename, + component->overlayDefs.fileInfo.content, + &component->overlayDefs.fileInfo.fileSize, component->overlayDefs.fileInfo.fileSize, + &component->overlayDefs.fileInfo.compressedSize, component->overlayDefs.fileInfo.compressedSize, + component->overlayDefs.header, + *(uint *)(component->overlayDefs.header + OVERLAY_DEFS_HEADER_LOAD_ADDRESS_OFFSET), + *(uint *)(component->overlayDefs.header + OVERLAY_DEFS_HEADER_ENTRY_ADDRESS_OFFSET), + component->overlayDefs.header + OVERLAY_DEFS_HEADER_MODULE_SIZE_OFFSET, *(uint *)(component->overlayDefs.header + OVERLAY_DEFS_HEADER_MODULE_SIZE_OFFSET), + *(uint *)(component->overlayDefs.header + OVERLAY_DEFS_HEADER_AUTOLOAD_DONE_OFFSET), + component->dirName); + + filename = component->overlayFilenames; + for (i = 0; i < component->numOverlays; i++) { + DebugPrintf(" .filename[%2d] = %s\n", i, filename); + do { + c = *filename; + filename++; + } while (c != '\0'); + } + + DebugPrintf("-------------------------------------------------------------\n"); + DebugPrintf("-------------------------------------------------------------\n"); +} + +void ConvertFinal(Options *options) { + options->digestKey = StrDup(options->digestKey, NULL); + options->outSuffix = StrDup(options->outSuffix, NULL); +} diff --git a/tools/compstatic/component.h b/tools/compstatic/component.h new file mode 100644 index 000000000..fdcbd3488 --- /dev/null +++ b/tools/compstatic/component.h @@ -0,0 +1,10 @@ +#ifndef COMPONENT_H +#define COMPONENT_H + +#include +#include "global.h" + +bool ConvertComponent(Options *options, char *staticModule, char *overlayDefs, char *overlayTable); +void ConvertFinal(Options *options); + +#endif // COMPONENT_H diff --git a/tools/compstatic/compress.c b/tools/compstatic/compress.c new file mode 100644 index 000000000..528d0ad31 --- /dev/null +++ b/tools/compstatic/compress.c @@ -0,0 +1,333 @@ +#include +#include +#include +#include "component.h" +#include "compress.h" +#include "digest.h" +#include "global.h" +#include "misc.h" +#include "overlay_table.h" +#include "print.h" +#include "static_module.h" + +static int Compress(char *content, int size); +static int LZCompressRV(char *uncompressed, int uncompressedSize, char *compressed, int compressedSize); +static int FindMatched(char *chunk, int chunkSize, char *remainder, int remainderSize, int *optimalRemainderIdx); +static int HowManyMatched(char *buffer1, char *buffer2, int size); +static int CheckOverwrite(int sourceSize, char *compressed, int compressedSize, int *newSourceSize, int *newCompressedSize); + +bool CompressOverlayModules(Component *component) { + OverlayModule *overlayModule = component->overlayModules; + char *overlayTable = component->overlayTable.table; + DebugPrintf("Compressing OverlayModules\n"); + if (component->numOverlays == 0 || overlayModule == NULL || overlayTable == NULL) { + DebugPrintf("No overlay to compress\n"); + } else { + for (int i = 0; i < component->numOverlays; i++) { + if ((overlayTable[OT_COMPRESSED_FLAGS_OFFSET] & 1) == 0) { + if (overlayModule->fileInfo.fileSize < *(uint *)(overlayTable + OT_FILESIZE_OFFSET)) { + ErrorPrintf("Overlay module file is shorter than the size reported in the overlay table\n" + "FileSize=%d InOverlayTable=%d\n", overlayModule->fileInfo.fileSize, *(uint *)(overlayTable + OT_FILESIZE_OFFSET)); + return false; + } + int compressResult = Compress(overlayModule->fileInfo.content, *(int *)(overlayTable + OT_FILESIZE_OFFSET)); + if (compressResult < 0) { + if (compressResult == -2 || compressResult != -1) return false; + + printf("OverlayModule[%02d]. Not compressed %9d (enlarged or same size as before)\n", i, overlayModule->fileInfo.fileSize); + } else { + if (compressResult > 0x00ffffff) { + ErrorPrintf("Compressed file size too large (over 24bit wide)\n"); + } + printf("OverlayModule[%02d]. Compressed ... %9d -> %9d\n", i, overlayModule->fileInfo.fileSize, compressResult); + uint *overlayTableCompressedSize = (uint *)(overlayTable + OT_COMPRESSED_FILESIZE_OFFSET); + *overlayTableCompressedSize = (*overlayTableCompressedSize & 0xff000000) | (compressResult & 0x00ffffff); + overlayModule->fileInfo.compressedSize = compressResult & 0x00ffffff; + overlayModule->fileInfo.compressedSize = compressResult & 0x00ffffff; + overlayTable[OT_COMPRESSED_FLAGS_OFFSET] |= 1; + overlayModule->fileInfo.rewrite = true; + component->overlayTable.fileInfo.rewrite = true; + } + } else { + printf("OverlayModule[%02d]. Already compressed\n", i); + } + overlayModule++; + overlayTable += OVERLAY_ENTRY_SIZE; + } + } + return true; +} + +bool CompressStaticModule(Component *component, int headerSize) { + bool success; + + int staticParamsOffset = *(int *)(component->staticModule.footerContent + 4); + char *content = component->staticModule.fileInfo.content; + StaticParams *staticParams = (StaticParams *)(content + staticParamsOffset); + if (staticParams->compressedStatic == 0 && + component->staticModule.fileInfo.compressedSize == 0) { + if (headerSize < 0) { + ErrorPrintf("Specified header size is less than 0 (=%d)\n", headerSize); + success = false; + } else if (component->staticModule.fileInfo.fileSize < headerSize) { + printf("StaticModule ..... Not compressed (Module is smaller than the header [%d bytes])\n", headerSize); + success = true; + } else { + int compressResult = + Compress(component->staticModule.fileInfo.content + headerSize, + component->staticModule.fileInfo.fileSize - headerSize); + if (compressResult < 0) { + if (compressResult == -2 || compressResult != -1) { + success = false; + } else { + printf("StaticModule ..... Not compressed (enlarged or same size as before)\n"); + success = true; + } + } else { + component->staticModule.fileInfo.compressedSize = compressResult + headerSize; + printf("StaticModule ..... Compressed ... %9d -> %9d\n", + component->staticModule.fileInfo.fileSize, component->staticModule.fileInfo.compressedSize); + int *header = (int *)component->overlayDefs.header; + header[2] = component->staticModule.fileInfo.compressedSize; + staticParams->compressedStatic = component->staticModule.fileInfo.compressedSize + header[0]; + CopyBuffer(component->staticModule.footerContent, + component->staticModule.fileInfo.content + ALIGN_4(component->staticModule.fileInfo.compressedSize), + component->staticModule.footerSize); + component->overlayDefs.fileInfo.rewrite = true; + component->staticModule.fileInfo.rewrite = true; + success = true; + } + } + } else { + printf("StaticModule ..... Already compressed\n"); + success = true; + } + return success; +} + +bool CalculateHMAC_OverlayModules(Component *component, int digestType, char *digestKey) { + bool success; + + OverlayModule *overlayModule = component->overlayModules; + char *overlayTable = component->overlayTable.table; + int numOverlays = component->numOverlays; + if (numOverlays == 0 || overlayModule == NULL || overlayTable == NULL) { + DebugPrintf("No overlay to calculate HMAC\n"); + success = true; + } else { + bool initDigestSuccess = Init_Digest(digestType, digestKey); + if (!initDigestSuccess) { + ErrorPrintf("Cannot setup digest library\n"); + success = false; + } else { + DebugPrintf("Calculating HMAC for OverlayModules\n"); + int digestParamsOffset = *(int *)(component->staticModule.footerContent + STATIC_FOOTER_DIGEST_PARAM_OFFSET); + char *content = component->staticModule.fileInfo.content; + char *digestParams = (char *)(content + digestParamsOffset); + for (int i = 0; i < numOverlays; i++) { + if ((overlayTable[OT_COMPRESSED_FLAGS_OFFSET] & 2U) != 0) { + printf("OverlayModule[%02d]: HMAC already calculated - do it again.\n", i); + } + if ((uint)overlayModule->fileInfo.fileSize < *(uint *)(overlayTable + OT_FILESIZE_OFFSET)) { + ErrorPrintf("Overlay module file is shorter than the size reported in overlay table\n" + " File size=%d InOverlayTabe=%d\n", overlayModule->fileInfo.fileSize, *(uint *)(overlayTable + OT_FILESIZE_OFFSET)); + return false; + } + uint overlaySize = *(uint *)(overlayTable + OT_COMPRESSED_FILESIZE_OFFSET) & 0x00ffffff; + if (overlaySize == 0) { + overlaySize = *(uint *)(overlayTable + OT_FILESIZE_OFFSET); + } + + printf("OverlayModule[%02d]", i); + Calc_Digest(overlayModule->fileInfo.content, overlaySize, digestParams, false); + overlayTable[OT_COMPRESSED_FLAGS_OFFSET] |= 2; + component->staticModule.fileInfo.rewrite = true; + component->overlayTable.fileInfo.rewrite = true; + digestParams += DIGEST_HASH_SIZE; + overlayModule++; + overlayTable += OVERLAY_ENTRY_SIZE; + } + printf("OverlayTable "); + Calc_Digest(component->overlayTable.table, numOverlays * OVERLAY_ENTRY_SIZE, content + digestParamsOffset - DIGEST_HASH_SIZE, true); + success = true; + } + } + return success; +} + +static int Compress(char *content, int size) { + int ret; + int sourceOffset; + int compressedOffset; + + char *compressed = malloc(size); + char *compressedCpy = compressed; + if (compressed == NULL) { + ErrorPrintf("Cannot allocate memory size=%d\n", size); + ret = -2; + } else if (((size_t)content % 4) == 0) { + char *contentCpy = content; + int sizeCpy = size; + int compressedSize = size; + int lzResult = LZCompressRV(content, size, compressed, size); + if (lzResult < 0) { + DebugPrintf("Compressed buffer size exceeds original data size.\n"); + free(compressedCpy); + ret = -1; + } else { + compressedSize -= lzResult; + compressed += lzResult; + DebugPrintf("1: source size = %d compressed = %d\n", size, compressedSize); + int overwriteResult = CheckOverwrite(sizeCpy, compressed, compressedSize, &sourceOffset, &compressedOffset); + if (overwriteResult == 0) { + contentCpy += sourceOffset; + sizeCpy -= sourceOffset; + compressed += compressedOffset; + compressedSize -= compressedOffset; + DebugPrintf(" !! Shrink back Compressed region to avoid overwriting.\n" + " !! Expand non-compressed region = +%d\n" + "2: source size = %d compressed = %d\n", sourceOffset, sizeCpy, compressedSize); + } + int compressedEnd = compressedSize + sourceOffset; + uint compressedEndAligned = ALIGN_4(compressedEnd); + ret = compressedEndAligned + 8; + if (ret < size) { + CopyBuffer(compressed, contentCpy, compressedSize); + free(compressedCpy); + for (int i = compressedEnd; i < (int)compressedEndAligned; i++) { + content[i] = -1; + } + char *contentEnd = content + compressedEndAligned; + *(uint *)contentEnd = (*(uint *)contentEnd & 0xff000000) | (ret - sourceOffset) & 0xffffff; + char compressedEndU8 = (char)compressedEnd; + char retU8 = (char)ret; + *(contentEnd + 3) = retU8 - compressedEndU8; + *(int *)(contentEnd + 4) = size - ret; + } else { + DebugPrintf("Compressed buffer size exceeds or equals original data size.\n"); + free(compressedCpy); + ret = -1; + } + } + } else { + ErrorPrintf("Top of buffer is not aligned by 4.\n"); + ret = -2; + } + return ret; +} + +static int LZCompressRV(char *uncompressed, int uncompressedSize, char *compressed, int compressedSize) { + int optimalRemainderIdx; + ushort optimalRemainderIdx16; + int bytesLeft = uncompressedSize; + while (true) { + if (bytesLeft < 1) return compressedSize; + if (compressedSize < 1) break; + + uint bitVector = 0; + int compressedPtr = compressedSize - 1; + compressedSize = compressedPtr; + for (int i = 0; i < 8; i++) { + bitVector <<= 1; + if (bytesLeft > 0) { + char *remainder = uncompressed + bytesLeft; + int bytesRead = uncompressedSize - bytesLeft; + int chunkSize = MIN(bytesLeft, 0x12); + char *chunk = remainder - chunkSize; + bytesRead = MIN(bytesRead, 0x1002); + int numMatches = FindMatched(chunk, chunkSize, remainder, bytesRead, &optimalRemainderIdx); + if (numMatches < 3) { + if (compressedSize < 1) return -1; + + compressedSize--; + bytesLeft--; + compressed[compressedSize] = uncompressed[bytesLeft]; + } else { + if (compressedSize < 2) return -1; + + bytesLeft -= numMatches; + optimalRemainderIdx -= 2; + optimalRemainderIdx16 = (ushort)optimalRemainderIdx & 0xfff; + compressed[compressedSize - 1] = + (char)((ushort)(((short)numMatches - 3) * 0x1000) >> 8) | (char)(optimalRemainderIdx16 >> 8); + compressedSize -= 2; + compressed[compressedSize] = (char)optimalRemainderIdx16; + bitVector |= 1; + } + } + } + compressed[compressedPtr] = (char)bitVector; + } + return -1; +} + +// `chunk` is a pointer to a section of the uncompressed byte array +// of size `chunkSize`. `remainder` is a pointer to the remainder of the +// byte array of size `remainderSize`. +// +// This function iterates over all the bytes after the chunk to find another +// chunk with the most bytes matching `chunk`. It returns the number of +// byte-wise matches found in the optimal chunk. +// +// `optimalRemainderIdx` points to the end of the optimal chunk. +static int FindMatched(char *chunk, int chunkSize, char *remainder, int remainderSize, int *optimalRemainderIdx) { + char lastChar = chunk[chunkSize - 1]; + int maxMatches = 0; + for (int i = 0; i < remainderSize; i++) { + if (lastChar == remainder[i]) { + int bufferSize = MIN(i + 1, chunkSize); + int numMatches = HowManyMatched(chunk + chunkSize - 1, remainder + i, bufferSize); + if (numMatches > maxMatches) { + *optimalRemainderIdx = i; + maxMatches = numMatches; + } + } + } + return maxMatches; +} + +// Returns the number of consecutive bytes that match in both buffers, starting +// from the end of each buffer. +static int HowManyMatched(char *buffer1, char *buffer2, int size) { + int i = 0; + for (; i < size && *buffer1 == *buffer2; buffer1--) { + buffer2--; + i++; + } + return i; +} + +static int CheckOverwrite(int sourceSize, char *compressed, int compressedSize, int *sourceOffset, int *compressedOffset) { + do { + if (sourceSize < 1) { + *sourceOffset = 0; + *compressedOffset = 0; + return 1; + } + compressedSize--; + uint bitVector = compressed[compressedSize]; + for (int i = 0; i < 8; i++) { + if (sourceSize > 0) { + if ((bitVector & 0x80) == 0) { + compressedSize--; + sourceSize--; + } else { + int nextCompressedSize = compressedSize - 2; + sourceSize -= ((byte)compressed[compressedSize - 1] >> 4) + 3; + if (sourceSize < 0) { + ErrorPrintf("System error in CheckOverwrite???\n"); + exit(-1); + } + compressedSize = nextCompressedSize; + if (sourceSize < nextCompressedSize) { + *sourceOffset = sourceSize; + *compressedOffset = compressedSize; + return 0; + } + } + bitVector <<= 1; + } + } + + } while (true); +} diff --git a/tools/compstatic/compress.h b/tools/compstatic/compress.h new file mode 100644 index 000000000..ed62fac36 --- /dev/null +++ b/tools/compstatic/compress.h @@ -0,0 +1,11 @@ +#ifndef COMPRESS_H +#define COMPRESS_H + +#include +#include "component.h" + +bool CompressOverlayModules(Component *component); +bool CompressStaticModule(Component *component, int headerSize); +bool CalculateHMAC_OverlayModules(Component *component, int digestType, char *digestKey); + +#endif // COMPRESS_H diff --git a/tools/compstatic/digest.c b/tools/compstatic/digest.c new file mode 100644 index 000000000..132b18b27 --- /dev/null +++ b/tools/compstatic/digest.c @@ -0,0 +1,366 @@ +#include +#include +#include +#include +#include +#include "digest.h" +#include "files.h" +#include "global.h" +#include "misc.h" +#include "print.h" + +typedef struct { + uint intermediateHash[5]; + uint lengthLow; + uint lengthHigh; + int messageBlockIdx; + byte messageBlock[64]; + bool computed; + uint corrupted; +} DGTHash2Context; + +typedef bool (*Hash2ResetCb)(DGTHash2Context *hashCtx); +typedef uint (*Hash2SetSourceCb)(DGTHash2Context *hashCtx, char *digestKey, int digestKeySize); +typedef uint (*Hash2GetDigestCb)(DGTHash2Context *hashCtx, char *digestBuffer); + +typedef struct { + int unk0; + int blockSize; + DGTHash2Context *hashCtx; + char *unkC; + Hash2ResetCb hash2Reset; + Hash2SetSourceCb hash2SetSource; + Hash2GetDigestCb hash2GetDigest; +} HmacParam; + +static void DGT_Hash2CalcHmac(char *hash, char *content, int size, char *digestKey, int digestKeySize); +static bool DGT_SetOverlayTableMode(bool mode); +static bool DGT_Hash2Reset(DGTHash2Context *hashCtx); +static uint DGT_Hash2SetSource(DGTHash2Context *hashCtx, char *digestKey, int digestKeySize); +static uint DGT_Hash2GetDigest(DGTHash2Context *hashCtx, char *digestBuffer); +static void DGT_Hash2SetPadding(DGTHash2Context *hashCtx); +static void DGT_Hash2DoProcess(DGTHash2Context *hashCtx); +static void HmacCalc(char *hash, char *content, int size, char *digestKey, int digestKeySize, HmacParam *param); + +// Set default DigestFunc +DigestFunc gDigestFunc = DGT_Hash2CalcHmac; + +static DigestFunc sDigestFuncs[] = { NULL, DGT_Hash2CalcHmac }; +static char sDefaultDigestKey[] = { + 0x21, 0x06, 0xc0, 0xde, 0xba, 0x98, 0xce, 0x3f, 0xa6, 0x92, 0xe3, 0x9d, + 0x46, 0xf2, 0xed, 0x01, 0x76, 0xe3, 0xcc, 0x08, 0x56, 0x23, 0x63, 0xfa, + 0xca, 0xd4, 0xec, 0xdf, 0x9a, 0x62, 0x78, 0x34, 0x8f, 0x6d, 0x63, 0x3c, + 0xfe, 0x22, 0xca, 0x92, 0x20, 0x88, 0x97, 0x23, 0xd2, 0xcf, 0xae, 0xc2, + 0x32, 0x67, 0x8d, 0xfe, 0xca, 0x83, 0x64, 0x98, 0xac, 0xfd, 0x3e, 0x37, + 0x87, 0x46, 0x58, 0x24, +}; + +bool Init_Digest(uint digestType, char *digestKey) { + bool success; + + if (digestType < 1 || digestType > 2) { + ErrorPrintf("Unknown digest type\n"); + success = false; + } else { + DigestFunc digestFunc = sDigestFuncs[digestType - 1]; + if (digestFunc == NULL) { + ErrorPrintf("Unsupported digest\n"); + success = false; + } else { + gDigestFunc = digestFunc; + if (gDigestKey != NULL) { + free(gDigestKey); + gDigestKey = NULL; + } + if (digestKey == NULL) { + gDigestKey = calloc(1, DIGEST_KEY_SIZE); + if (gDigestKey == NULL) { + ErrorPrintf("Cannot allocate memory.\n"); + return false; + } + memcpy(gDigestKey, sDefaultDigestKey, DIGEST_KEY_SIZE); + gDigestKeySize = DIGEST_KEY_SIZE; + } else { + gDigestKeySize = ReadFile(digestKey, &gDigestKey); + if (gDigestKeySize < 0) { + return false; + } + if (gDigestKeySize < DIGEST_KEY_SIZE) { + ErrorPrintf("Key file size should be >= %d bytes\n", DIGEST_KEY_SIZE); + return false; + } + } + success = true; + } + } + return success; +} + +void Calc_Digest(char *content, int size, char *hash, bool overlayTableMode) { + byte hashBuffer[40]; + + bool oldMode = DGT_SetOverlayTableMode(overlayTableMode); + gDigestFunc((char *)hashBuffer, content, size, gDigestKey, gDigestKeySize); + DGT_SetOverlayTableMode(oldMode); + int iCpy = 0; + for (int i = 0; i < 20; i++) { + hashBuffer[i] = hashBuffer[iCpy + 1] ^ hashBuffer[iCpy]; + iCpy++; + } + printf(": Digest "); + for (int i = 0; i < 20; i++) { + printf("%02X", hashBuffer[i]); + } + printf(" %8ld\n", (long int)size); + CopyBuffer((char *)hashBuffer, hash, 20); +} + +static void DGT_Hash2CalcHmac(char *hash, char *content, int size, char *digestKey, int digestKeySize) { + DGTHash2Context hashCtx; + char unkBuffer1[0x20]; + HmacParam param = {0}; + + param.unk0 = 20; + param.blockSize = DIGEST_KEY_SIZE; + param.hashCtx = &hashCtx; + param.unkC = unkBuffer1; + param.hash2Reset = DGT_Hash2Reset; + param.hash2SetSource = DGT_Hash2SetSource; + param.hash2GetDigest = DGT_Hash2GetDigest; + HmacCalc(hash, content, size, digestKey, digestKeySize, ¶m); +} + +static bool DGT_SetOverlayTableMode(bool mode) { + bool oldMode = gOverlayTableMode; + gOverlayTableMode = mode; + return oldMode; +} + +static bool DGT_Hash2Reset(DGTHash2Context *hashCtx) { + if (hashCtx != NULL) { + hashCtx->intermediateHash[0] = 0x67452301; + hashCtx->intermediateHash[1] = 0xefcdab89; + hashCtx->intermediateHash[2] = 0x98badcfe; + hashCtx->intermediateHash[3] = 0x10325476; + hashCtx->intermediateHash[4] = 0xc3d2e1f0; + hashCtx->lengthLow = 0; + hashCtx->lengthHigh = 0; + hashCtx->messageBlockIdx = 0; + hashCtx->computed = false; + hashCtx->corrupted = 0; + } + return hashCtx == NULL; +} + +static uint DGT_Hash2SetSource(DGTHash2Context *hashCtx, char *digestKey, int digestKeySize) { + uint ret; + + if (digestKeySize == 0) { + ret = 0; + } else if (hashCtx == NULL || digestKey == NULL) { + ret = 1; + } else if (!hashCtx->computed) { + if (hashCtx->corrupted == 0) { + while (digestKeySize -= 1, digestKeySize != -1 && hashCtx->corrupted == 0) { + hashCtx->messageBlock[hashCtx->messageBlockIdx] = *digestKey; + hashCtx->messageBlockIdx += 1; + hashCtx->lengthLow += 8; + if (hashCtx->messageBlockIdx == 0 && (hashCtx->lengthHigh += 1, hashCtx->lengthHigh == 0)) { + hashCtx->corrupted = 1; + } + if (hashCtx->messageBlockIdx == 0x40) { + DGT_Hash2DoProcess(hashCtx); + } + digestKey++; + } + ret = 0; + } else { + ret = hashCtx->corrupted; + } + } else { + hashCtx->corrupted = 3; + ret = 3; + } + return ret; +} + +static uint DGT_Hash2GetDigest(DGTHash2Context *hashCtx, char *digestBuffer) { + uint ret; + + if (hashCtx == NULL || digestBuffer == NULL) { + ret = 1; + } else if (hashCtx->corrupted == 0) { + if (!hashCtx->computed) { + DGT_Hash2SetPadding(hashCtx); + for (int i = 0; i < 0x40; i++) { + hashCtx->messageBlock[i] = '\0'; + } + hashCtx->lengthLow = 0; + hashCtx->lengthHigh = 0; + hashCtx->computed = true; + } + for (int i = 0; i < 20; i++) { + digestBuffer[i] = (char)(*(uint *)(hashCtx->messageBlock + (i >> 2) * 4 + -0x20) >> ((3u - ((byte)i % 4)) * 8u & 0x1f)); + } + ret = 0; + } else { + ret = hashCtx->corrupted; + } + return ret; +} + +static void DGT_Hash2SetPadding(DGTHash2Context *hashCtx) { + if (hashCtx->messageBlockIdx < 0x38) { + hashCtx->messageBlock[hashCtx->messageBlockIdx] = -0x80; + hashCtx->messageBlockIdx += 1; + while (hashCtx->messageBlockIdx < 0x38) { + hashCtx->messageBlock[hashCtx->messageBlockIdx] = '\0'; + hashCtx->messageBlockIdx += 1; + } + } else { + hashCtx->messageBlock[hashCtx->messageBlockIdx] = -0x80; + hashCtx->messageBlockIdx += 1; + while (hashCtx->messageBlockIdx < 0x40) { + hashCtx->messageBlock[hashCtx->messageBlockIdx] = '\0'; + hashCtx->messageBlockIdx += 1; + } + DGT_Hash2DoProcess(hashCtx); + while (hashCtx->messageBlockIdx < 0x38) { + hashCtx->messageBlock[hashCtx->messageBlockIdx] = '\0'; + hashCtx->messageBlockIdx += 1; + } + } + hashCtx->messageBlock[0x38] = hashCtx->lengthHigh >> 0x18; + hashCtx->messageBlock[0x39] = (byte)(hashCtx->lengthHigh >> 0x10); + hashCtx->messageBlock[0x3a] = (byte)(hashCtx->lengthHigh >> 8); + hashCtx->messageBlock[0x3b] = (byte)(hashCtx->lengthHigh); + hashCtx->messageBlock[0x3c] = hashCtx->lengthLow >> 0x18; + hashCtx->messageBlock[0x3d] = (byte)(hashCtx->lengthLow >> 0x10); + hashCtx->messageBlock[0x3e] = (byte)(hashCtx->lengthLow >> 8); + hashCtx->messageBlock[0x3f] = (byte)(hashCtx->lengthLow); + DGT_Hash2DoProcess(hashCtx); +} + +// SHA hash +static void DGT_Hash2DoProcess(DGTHash2Context *hashCtx) { + uint temp; + int temp2; + uint buffer[99]; + int i, idx; + + if (gOverlayTableMode) { + buffer[10] = *(uint *)(hashCtx->messageBlock + 0x18); + buffer[9] = *(uint *)(hashCtx->messageBlock + 0x38); + *(uint *)(hashCtx->messageBlock + 0x18) = 0; + *(uint *)(hashCtx->messageBlock + 0x38) = 0; + } + for (i = 0; i < 0x10; i++) { + buffer[i + 0x10] = (uint)(byte)hashCtx->messageBlock[i * 4] << 0x18; + buffer[i + 0x10] = (uint)(byte)hashCtx->messageBlock[i * 4 + 1] << 0x10 | buffer[i + 0x10]; + buffer[i + 0x10] = (uint)(byte)hashCtx->messageBlock[i * 4 + 2] << 8 | buffer[i + 0x10]; + buffer[i + 0x10] = (uint)(byte)hashCtx->messageBlock[i * 4 + 3] | buffer[i + 0x10]; + } + for (i = 0x10; i < 0x50; i++) { + temp = buffer[i + 0xd] ^ buffer[i + 8] ^ buffer[i + 2] ^ buffer[i]; + buffer[i + 0x10] = temp << 1 | (uint)((int)temp < 0); + } + buffer[14] = hashCtx->intermediateHash[1]; + buffer[13] = hashCtx->intermediateHash[2]; + buffer[12] = hashCtx->intermediateHash[3]; + buffer[11] = hashCtx->intermediateHash[4]; + buffer[15] = hashCtx->intermediateHash[0]; + for (i = 0; i < 0x14; i++) { + temp2 = (~buffer[14] & buffer[12] | buffer[14] & buffer[13]) + + (buffer[15] << 5 | buffer[15] >> 0x1b) + buffer[11]; + idx = i + 0x10; + buffer[11] = buffer[12]; + buffer[12] = buffer[13]; + buffer[13] = buffer[14] << 0x1e | buffer[14] >> 2; + buffer[14] = buffer[15]; + buffer[15] = buffer[idx] + temp2 + 0x5a827999; + } + for (i = 0x14; i < 0x28; i = i + 1) { + temp2 = (buffer[13] ^ buffer[14] ^ buffer[12]) + (buffer[15] << 5 | buffer[15] >> 0x1b) + + buffer[11]; + idx = i + 0x10; + buffer[11] = buffer[12]; + buffer[12] = buffer[13]; + buffer[13] = buffer[14] << 0x1e | buffer[14] >> 2; + buffer[14] = buffer[15]; + buffer[15] = buffer[idx] + temp2 + 0x6ed9eba1; + } + for (i = 0x28; i < 0x3c; i++) { + temp2 = (buffer[12] & buffer[13] | (buffer[12] | buffer[13]) & buffer[14]) + + (buffer[15] << 5 | buffer[15] >> 0x1b) + buffer[11]; + idx = i + 0x10; + buffer[11] = buffer[12]; + buffer[12] = buffer[13]; + buffer[13] = buffer[14] << 0x1e | buffer[14] >> 2; + buffer[14] = buffer[15]; + buffer[15] = buffer[idx] + temp2 + 0x8f1bbcdc; + } + for (i = 0x3c; i < 0x50; i++) { + temp2 = (buffer[13] ^ buffer[14] ^ buffer[12]) + (buffer[15] << 5 | buffer[15] >> 0x1b) + + buffer[11]; + idx = i + 0x10; + buffer[11] = buffer[12]; + buffer[12] = buffer[13]; + buffer[13] = buffer[14] << 0x1e | buffer[14] >> 2; + buffer[14] = buffer[15]; + buffer[15] = buffer[idx] + temp2 + 0xca62c1d6; + } + hashCtx->intermediateHash[0] += buffer[15]; + hashCtx->intermediateHash[1] += buffer[14]; + hashCtx->intermediateHash[2] += buffer[13]; + hashCtx->intermediateHash[3] += buffer[12]; + hashCtx->intermediateHash[4] += buffer[11]; + hashCtx->messageBlockIdx = 0; + if (gOverlayTableMode) { + *(uint *)(hashCtx->messageBlock + 0x18) = buffer[10]; + *(uint *)(hashCtx->messageBlock + 0x38) = buffer[9]; + } +} + +static void HmacCalc(char *hash, char *content, int size, char *digestKey, int digestKeySize, HmacParam *param) { + char digestBuffer[0x4C]; + char *key; + int keySize; + char innerKey[0x48]; + char outerKey[0x40]; + int i; + + if (hash != NULL && content != NULL && size != 0 && digestKey != NULL && + digestKeySize != 0 && param != NULL) { + if (param->blockSize < digestKeySize) { + param->hash2Reset(param->hashCtx); + param->hash2SetSource(param->hashCtx, digestKey, digestKeySize); + param->hash2GetDigest(param->hashCtx, digestBuffer); + key = digestBuffer; + keySize = param->unk0; + } else { + key = digestKey; + keySize = digestKeySize; + } + for (i = 0; i < keySize; i++) { + innerKey[i] = key[i] ^ 0x36; + } + for (; i < param->blockSize; i++) { + innerKey[i] = 0x36; + } + param->hash2Reset(param->hashCtx); + param->hash2SetSource(param->hashCtx, innerKey, param->blockSize); + param->hash2SetSource(param->hashCtx, content, size); + param->hash2GetDigest(param->hashCtx, param->unkC); + + for (i = 0; i < keySize; i++) { + outerKey[i] = key[i] ^ 0x5c; + } + for (; i < param->blockSize; i++) { + outerKey[i] = 0x5c; + } + param->hash2Reset(param->hashCtx); + param->hash2SetSource(param->hashCtx, outerKey, param->blockSize); + param->hash2SetSource(param->hashCtx, param->unkC, param->unk0); + param->hash2GetDigest(param->hashCtx, hash); + } +} diff --git a/tools/compstatic/digest.h b/tools/compstatic/digest.h new file mode 100644 index 000000000..5890ba1f9 --- /dev/null +++ b/tools/compstatic/digest.h @@ -0,0 +1,20 @@ +#ifndef DIGEST_H +#define DIGEST_H + +#include +#include "global.h" + +#define DIGEST_KEY_SIZE (0x40) +#define DIGEST_HASH_SIZE (0x14) + +typedef void (*DigestFunc)(char *hash, char *content, int size, char *digestKey, int digestKeySize); + +DigestFunc gDigestFunc; +char *gDigestKey; +int gDigestKeySize; +bool gOverlayTableMode; + +bool Init_Digest(uint digestType, char *digestKey); +void Calc_Digest(char *content, int size, char *hash, bool overlayTableMode); + +#endif // DIGEST_H diff --git a/tools/compstatic/files.c b/tools/compstatic/files.c new file mode 100644 index 000000000..5f815b2e6 --- /dev/null +++ b/tools/compstatic/files.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include +#include +#include "files.h" +#include "global.h" +#include "print.h" +#include "str.h" + +char *GetDirName(char *filename) { + char *dirName; + + int i = strlen(filename); + while (true) { + i--; + if (i < 0) { + dirName = strdup("."); + return dirName; + } + if (filename[i] == '/' || filename[i] == '\\') break; + if (filename[i] == ':') { + dirName = malloc(i + 3); + if (dirName != NULL) { + strncpy(dirName, filename, i); + strcpy(dirName + i, ":."); + } + return dirName; + } + } + + // Same size as filename, but has null-character after directory name. + dirName = strdup(filename); + if (dirName == NULL) { + return NULL; + } + dirName[i] = '\0'; + return dirName; +} + +bool GetFiles(char *responseFile, SourceFiles *sourceFiles) { + char *filename; + bool success; + int numWords; + int wordStart; + char *content; + + char *dirName = GetDirName(responseFile); + int bytesRead = ReadFile(responseFile, &content); + if (bytesRead < 0) { + success = false; + } else { + numWords = 0; + wordStart = 0; + for (int i = 0; i < bytesRead; i++) { + if (content[i] == '\0') { + filename = StrCat(3, dirName, "/", &content[wordStart]); + sourceFiles->filenames[numWords] = filename; + + wordStart = i + 1; + numWords++; + if (numWords == NUM_SOURCE_FILES) break; + } + } + free(content); + free(dirName); + if (numWords == NUM_SOURCE_FILES) { + success = true; + } else { + while (numWords > 0) { + numWords--; + free(sourceFiles->filenames[numWords]); + } + ErrorPrintf("%s wrong format", responseFile); + success = false; + } + } + return success; +} + +// Returns size of file. +int ReadFile(char *filename, char **content) { + int bytesRead; + struct stat fileInfo; + FILE *fp = NULL; + *content = NULL; + + if (filename == NULL) { + ErrorPrintf("Not specified filename\n"); + } else { + int error = stat(filename, &fileInfo); + if ((error == 0) && (fp = fopen(filename, "rb"), fp != NULL)) { + *content = malloc(fileInfo.st_size); + if (*content == NULL) { + ErrorPrintf("Cannot allocate memory size=%d\n", fileInfo.st_size); + } else { + bytesRead = fread(*content, 1, fileInfo.st_size, fp); + if (bytesRead == fileInfo.st_size) { + DebugPrintf("%p %8d bytes ReadFile \'%s\'\n", *content, fileInfo.st_size, filename); + fclose(fp); + return fileInfo.st_size; + } + ErrorPrintf("Cannot read file \'%s\'\n", filename); + } + } + ErrorPrintf("Cannot read file \'%s\'\n", filename); + } + + if (*content != NULL) { + free(content); + } + if (fp != NULL) { + fclose(fp); + } + return -1; +} + +// Returns number of bytes written. +int WriteFile(char *filename, char *content, int size) { + FILE *fp = NULL; + int ret; + + DebugPrintf("%p %8d bytes WriteFile \'%s\'\n", content, size, filename); + fp = fopen(filename, "wb"); + if (fp == NULL) { + ErrorPrintf("Cannot open file \'%s\'\n", filename); + ret = -1; + } else { + int sizeWritten = fwrite(content, 1, size, fp); + if (size == sizeWritten) { + int closeResult = fclose(fp); + if (closeResult < 0) { + ErrorPrintf("Cannot close file \'%s\'\n", filename); + unlink(filename); + ret = -1; + } else { + ret = sizeWritten; + } + } else { + ErrorPrintf("Cannot write file \'%s\'\n", filename); + fclose(fp); + unlink(filename); + ret = -1; + } + } + return ret; +} diff --git a/tools/compstatic/files.h b/tools/compstatic/files.h new file mode 100644 index 000000000..e66d34dbd --- /dev/null +++ b/tools/compstatic/files.h @@ -0,0 +1,12 @@ +#ifndef FILES_H +#define FILES_H + +#include +#include "global.h" + +char *GetDirName(char *filename); +bool GetFiles(char *responseFile, SourceFiles *sourceFiles); +int ReadFile(char *filename, char **content); +int WriteFile(char *filename, char *content, int size); + +#endif // FILES_H diff --git a/tools/compstatic/global.h b/tools/compstatic/global.h new file mode 100644 index 000000000..2e4c28f2e --- /dev/null +++ b/tools/compstatic/global.h @@ -0,0 +1,74 @@ +#ifndef GLOBAL_H +#define GLOBAL_H + +#include + +typedef unsigned char byte; +typedef unsigned short ushort; +typedef unsigned int uint; + +#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) +#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) + +#define ALIGN_4(addr) ((addr + 3U) & 0xfffffffc) + +typedef struct { + bool staticModule; + int headerSize; // size in bytes + bool overlayModules; + bool overlayDigest; + int digestType; + char *digestKey; + char *outSuffix; +} Options; + +#define SOURCE_FILES_STATIC_MODULE 0 +#define SOURCE_FILES_OVERLAY_DEFS 1 +#define SOURCE_FILES_OVERLAY_TABLE 2 +#define NUM_SOURCE_FILES 3 + +typedef struct { + char *filenames[NUM_SOURCE_FILES]; +} SourceFiles; + +bool gDebugMode; + +typedef struct { + char *filename; + char *content; + int fileSize; + int compressedSize; + bool rewrite; // File should be updated after compression +} FileInfo; // win32 sizeof == 0x14 + +typedef struct { + FileInfo fileInfo; + char *footerContent; + uint footerSize; +} StaticModule; // win32 sizeof == 0x1c + +typedef struct { + FileInfo fileInfo; + char *header; +} OverlayDefs; // win32 sizeof == 0x18 + +typedef struct { + FileInfo fileInfo; +} OverlayModule; // win32 sizeof == 0x14 + +typedef struct { + FileInfo fileInfo; + char *table; // == fileInfo.content +} OverlayTable; // win32 sizeof == 0x18 + +typedef struct { + StaticModule staticModule; + int numOverlays; + OverlayDefs overlayDefs; + char *overlayFilenames; + char *dirName; + OverlayTable overlayTable; + OverlayModule *overlayModules; +} Component; // win32 sizeof: 0x5c + +#endif // GLOBAL_H diff --git a/tools/compstatic/main.c b/tools/compstatic/main.c new file mode 100644 index 000000000..0c051d8af --- /dev/null +++ b/tools/compstatic/main.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include "component.h" +#include "files.h" +#include "global.h" +#include "print.h" +#include "str.h" + +bool gDebugMode = false; + +void InitializeOptions(Options *options) { + options->staticModule = false; + options->headerSize = 0x4000; + options->overlayModules = false; + options->overlayDigest = false; + options->digestType = 2; + options->digestKey = StrDup(NULL, NULL); + options->outSuffix = StrDup(NULL, "_LZ"); +} + +void usage(void) { + printf("NDS Development Tool - compstatic - Compress static module\n\n" + "Usage: compstatic [-sHSIZE|-9|-7] [-c] [-a[FILE]]\n" + " [-F] [-eSUFFIX] [-fRESFILE] [-d] [-h]\n" + " [STATICMODULE OVERLAYDEFS [OVERLAYTABLE]]\n\n" + " Compress component modules\n\n" + " -sHSIZE Compress STATICMODULE\n" + " without header region specified by HSIZE(bytes)\n" + " -9 Compress as ARM9 static module (HEADER_SIZE = 16k)\n" + " -7 Compress as ARM7 static module (HEADER_SIZE = 1k)\n" + " -c Compress overlay modules\n" + " -a[FILE] Generate overlay digest code with FILE as key\n" + " -F Overwrite original component files\n" + " -eSUFFIX SUFFIX for output file (default:\"_LZ\")\n" + " -fRESFILE Use response file to specify source files\n" + " -d Show debug messages (for test purpose)\n" + " -h Show this message\n\n"); +} + +int main(int argc, char *argv[]) { + int opt; + int useResponseFile = false; + int argsLeft; + char **remainingArgs; + char *overlayTable; + bool success; + SourceFiles sourceFiles; + Options options; + + InitializeOptions(&options); + while ((opt = getopt(argc, argv, "s:ca::A:97Fe:fdhv")) != -1) { + switch (opt) { + case '7': + options.staticModule = true; + options.headerSize = 0x400; + break; + case '9': + options.staticModule = true; + options.headerSize = 0x4000; + break; + // Not referenced in usage + case 'A': + options.digestType = atoi(optarg); + break; + case 'F': + options.outSuffix = StrDup(options.outSuffix, NULL); + break; + case 'a': + options.overlayDigest = true; + options.digestKey = StrDup(options.digestKey, optarg); + break; + case 'c': + options.overlayModules = true; + break; + case 'd': + gDebugMode = true; + break; + case 'e': + options.outSuffix = strdup(optarg); + break; + case 'f': + useResponseFile = true; + break; + case 's': + options.staticModule = true; + options.headerSize = atoi(optarg); + break; + default: + usage(); + break; + } + DebugPrintf("option -%c: %s\n", opt, (optarg == NULL) ? "No ARG" : optarg); + } + + argsLeft = argc - optind; + remainingArgs = &argv[optind]; + + DebugPrintf("argc=%d optind=%d\n", argsLeft, optind); + for (int i = 0; i < argsLeft; i++) { + DebugPrintf("argv[%d] = [%s]\n", i, argv[optind + i]); + } + + if (!useResponseFile || argsLeft != 1) { + if (!useResponseFile && (argsLeft == 2 || argsLeft == 3)) { + overlayTable = (argsLeft == 3) ? remainingArgs[2] : NULL; + success = ConvertComponent(&options, remainingArgs[0], remainingArgs[1], overlayTable); + } else { + usage(); + } + } else { + success = GetFiles(remainingArgs[0], &sourceFiles); + if (success) { + printf("StaticModule = %s\n", sourceFiles.filenames[SOURCE_FILES_STATIC_MODULE]); + printf("OverlayDefs = %s\n", sourceFiles.filenames[SOURCE_FILES_OVERLAY_DEFS]); + printf("OverlayTable = %s\n", sourceFiles.filenames[SOURCE_FILES_OVERLAY_TABLE]); + + success = ConvertComponent(&options, sourceFiles.filenames[SOURCE_FILES_STATIC_MODULE], sourceFiles.filenames[SOURCE_FILES_OVERLAY_DEFS], sourceFiles.filenames[SOURCE_FILES_OVERLAY_TABLE]); + free(sourceFiles.filenames[SOURCE_FILES_STATIC_MODULE]); + free(sourceFiles.filenames[SOURCE_FILES_OVERLAY_DEFS]); + free(sourceFiles.filenames[SOURCE_FILES_OVERLAY_TABLE]); + } + } + + ConvertFinal(&options); + if (!success) { + ErrorPrintf("exit...\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/tools/compstatic/misc.c b/tools/compstatic/misc.c new file mode 100644 index 000000000..e54bbb6b9 --- /dev/null +++ b/tools/compstatic/misc.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include "global.h" +#include "misc.h" +#include "print.h" + +void CopyBuffer(char *src, char *dest, int size) { + if (dest < src) { + for (int i = 0; i < size; i++) { + dest[i] = src[i]; + } + } else { + for (int i = size - 1; i >= 0; i--) { + dest[i] = src[i]; + } + } +} + +void FreeBuffer(FileInfo *fileInfo) { + if (fileInfo->content != NULL) { + free(fileInfo->content); + fileInfo->content = NULL; + } + if (fileInfo->filename != NULL) { + free(fileInfo->filename); + fileInfo->filename = NULL; + } + fileInfo->compressedSize = 0; + fileInfo->fileSize = 0; + fileInfo->rewrite = false; +} + +bool AddSuffixBuffer(FileInfo *fileInfo, char *suffix) { + bool success; + + if (fileInfo->fileSize == 0 || fileInfo->filename == NULL || suffix == NULL || *suffix == '\0') { + success = true; + } else { + int filenameLength = strlen(fileInfo->filename); + int suffixLength = strlen(suffix); + char *dest = malloc(filenameLength + suffixLength + 1); + if (dest == NULL) { + ErrorPrintf("Cannot allocate memory.\n"); + success = false; + } else { + sprintf(dest, "%s%s", fileInfo->filename, suffix); + free(fileInfo->filename); + fileInfo->filename = dest; + fileInfo->rewrite = true; + success = true; + } + } + return success; +} diff --git a/tools/compstatic/misc.h b/tools/compstatic/misc.h new file mode 100644 index 000000000..b21351fde --- /dev/null +++ b/tools/compstatic/misc.h @@ -0,0 +1,10 @@ +#ifndef MISC_H +#define MISC_H + +#include "global.h" + +void CopyBuffer(char *src, char *dest, int size); +void FreeBuffer(FileInfo *fileInfo); +bool AddSuffixBuffer(FileInfo *fileInfo, char *suffix); + +#endif // MISC_H diff --git a/tools/compstatic/overlay_defs.c b/tools/compstatic/overlay_defs.c new file mode 100644 index 000000000..8839dfaac --- /dev/null +++ b/tools/compstatic/overlay_defs.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include "files.h" +#include "global.h" +#include "misc.h" +#include "overlay_defs.h" +#include "print.h" + +bool ReadOverlayDefs(Component *component, char *filename) { + int numWords; + int checkPtr; + bool success = false; + + DebugPrintf("Reading overlayDefs=%s\n", filename); + int fileSize = ReadFile(filename, &component->overlayDefs.fileInfo.content); + component->overlayDefs.fileInfo.fileSize = fileSize; + component->overlayDefs.fileInfo.compressedSize = 0; + component->overlayDefs.fileInfo.rewrite = false; + component->overlayDefs.fileInfo.filename = strdup(filename); + + if (component->overlayDefs.fileInfo.fileSize < 0) { + success = false; + } else if (component->overlayDefs.fileInfo.fileSize < MIN_OVERLAY_DEFS_FILE_SIZE) { + ErrorPrintf("Overlaydefs is too small (size=%d). Must be >= %d\n", component->overlayDefs.fileInfo.fileSize, MIN_OVERLAY_DEFS_FILE_SIZE); + success = false; + } else { + component->overlayDefs.header = component->overlayDefs.fileInfo.content; + component->overlayFilenames = component->overlayDefs.header + OVERLAY_DEFS_FILENAMES_OFFSET; + component->dirName = GetDirName(filename); + if (component->dirName == NULL) { + success = false; + } else { + // Count the number of words in the footer. Once two '\0' chars + // appear back-to-back, stop checking. + numWords = 0; + checkPtr = 0; + for (int i = 0; i < component->overlayDefs.fileInfo.fileSize - OVERLAY_DEFS_FILENAMES_SIZE; i++) { + if (component->overlayFilenames[i] == '\0') { + if (checkPtr == i) break; + numWords++; + checkPtr = i + 1; + } + } + component->numOverlays = numWords; + success = true; + } + } + + return success; +} + +bool WriteOverlayDefs(Component *component) { + int sizeWritten; + bool success; + + OverlayDefs *overlayDefs = &component->overlayDefs; + if (!overlayDefs->fileInfo.rewrite) { + success = true; + } else { + DebugPrintf("Writing overlayDefs=%s\n", overlayDefs->fileInfo.filename); + if (overlayDefs->fileInfo.compressedSize == 0) { + sizeWritten = WriteFile(overlayDefs->fileInfo.filename, overlayDefs->fileInfo.content, overlayDefs->fileInfo.fileSize); + success = sizeWritten >= 0; + } else { + ErrorPrintf("Don\'t support compressed overlaydefs\n"); + success = false; + } + } + return success; +} + +bool AddSuffixOverlayDefs(Component *component, char *suffix) { + DebugPrintf("Adding suffix overlayDefs=%s\n", suffix); + return AddSuffixBuffer(&component->overlayDefs.fileInfo, suffix); +} + +void FreeOverlayDefs(Component *component) { + DebugPrintf("Releasing overlayDefs\n"); + FreeBuffer(&component->overlayDefs.fileInfo); + component->overlayDefs.header = NULL; + component->overlayFilenames = NULL; + if (component->dirName != NULL) { + free(component->dirName); + component->dirName = NULL; + } +} diff --git a/tools/compstatic/overlay_defs.h b/tools/compstatic/overlay_defs.h new file mode 100644 index 000000000..0d83c4fcc --- /dev/null +++ b/tools/compstatic/overlay_defs.h @@ -0,0 +1,22 @@ +#ifndef OVERLAY_DEFS_H +#define OVERLAY_DEFS_H + +#include +#include "global.h" + +#define MIN_OVERLAY_DEFS_FILE_SIZE 0x10 +#define OVERLAY_DEFS_FOOTER_OFFSET 0x8 +#define OVERLAY_DEFS_FILENAMES_OFFSET 0x10 +#define OVERLAY_DEFS_FILENAMES_SIZE 0x10 + +#define OVERLAY_DEFS_HEADER_LOAD_ADDRESS_OFFSET 0 +#define OVERLAY_DEFS_HEADER_ENTRY_ADDRESS_OFFSET 4 +#define OVERLAY_DEFS_HEADER_MODULE_SIZE_OFFSET 8 +#define OVERLAY_DEFS_HEADER_AUTOLOAD_DONE_OFFSET 12 + +bool ReadOverlayDefs(Component *component, char *filename); +bool WriteOverlayDefs(Component *component); +bool AddSuffixOverlayDefs(Component *component, char *suffix); +void FreeOverlayDefs(Component *component); + +#endif // OVERLAY_DEFS_H diff --git a/tools/compstatic/overlay_modules.c b/tools/compstatic/overlay_modules.c new file mode 100644 index 000000000..440df669c --- /dev/null +++ b/tools/compstatic/overlay_modules.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include "files.h" +#include "global.h" +#include "misc.h" +#include "overlay_modules.h" +#include "print.h" + +bool ReadOverlayModules(Component *component) { + bool success; + char c; + OverlayModule *overlayModules; + + char *overlayFilenames = component->overlayFilenames; + char *dirName = component->dirName; + int dirNameLength = strlen(dirName); + + if (component->numOverlays == 0) { + DebugPrintf("No overlay to read\n"); + component->overlayModules = NULL; + success = true; + } else { + overlayModules = calloc(component->numOverlays, sizeof(OverlayModule)); + if (overlayModules == NULL) { + ErrorPrintf("Cannot allocate memory.\n"); + success = false; + } else { + component->overlayModules = overlayModules; + for (int i = 0; i < component->numOverlays; i++) { + int filenameLength = strlen(overlayFilenames); + overlayModules->fileInfo.filename = malloc(filenameLength + dirNameLength + 2);; + if (overlayModules->fileInfo.filename == NULL) { + ErrorPrintf("Cannot allocate memory\n"); + return false; + } + sprintf(overlayModules->fileInfo.filename, "%s/%s", dirName, overlayFilenames); + DebugPrintf("Reading overlay[%d]=%s\n", i, overlayModules->fileInfo.filename); + + int fileSize = ReadFile(overlayModules->fileInfo.filename, &overlayModules->fileInfo.content); + overlayModules->fileInfo.fileSize = fileSize; + overlayModules->fileInfo.compressedSize = 0; + overlayModules->fileInfo.rewrite = false; + if (overlayModules->fileInfo.fileSize < 0) return false; + + overlayModules++; + do { + c = *overlayFilenames; + overlayFilenames++; + } while (c != '\0'); + } + success = true; + } + } + return success; +} + +bool WriteOverlayModules(Component *component) { + OverlayModule *overlayModule = component->overlayModules; + int i = 0; + + do { + if (i >= component->numOverlays) { + return true; + } + if (overlayModule->fileInfo.rewrite) { + DebugPrintf("Writing overlay[%d]=%s\n", i, overlayModule->fileInfo.filename); + int overlaySize = overlayModule->fileInfo.compressedSize ? overlayModule->fileInfo.compressedSize : overlayModule->fileInfo.fileSize; + int sizeWritten = WriteFile(overlayModule->fileInfo.filename, overlayModule->fileInfo.content, overlaySize); + if (sizeWritten < 0) { + return false; + } + } + overlayModule++; + i++; + } while (true); +} + +bool AddSuffixOverlayModules(Component *component, char *suffix) { + bool success; + + OverlayModule *overlayModule = component->overlayModules; + if (component->numOverlays == 0 || overlayModule == NULL) { + DebugPrintf("No overlay to add suffix\n"); + success = true; + } else if (suffix == NULL || *suffix == '\0') { + success = true; + } else { + int suffixLength = strlen(suffix); + int fileSize = suffixLength * component->numOverlays + component->overlayDefs.fileInfo.fileSize; + char *buffer = malloc(fileSize); + if (buffer == NULL) { + ErrorPrintf("Cannot allocate memory\n"); + success = false; + } else { + uint *header32 = (uint *)component->overlayDefs.header; + *(uint *)buffer = header32[0]; + *(uint *)(buffer + 4) = header32[1]; + *(uint *)(buffer + 8) = header32[2]; + *(uint *)(buffer + 0xc) = header32[3]; + + char *contentPtr = buffer + 0x10; + char *ovFilename = component->overlayFilenames; + for (int i = 0; i < component->numOverlays; i++) { + DebugPrintf("Adding suffix overlay[%d]\n", i); + strcpy(contentPtr, ovFilename); + strcat(contentPtr, suffix); + + int length = strlen(ovFilename); + ovFilename += length + 1; + length = strlen(contentPtr); + contentPtr += length + 1; + + bool addSuffixSuccess = AddSuffixBuffer(&overlayModule->fileInfo, suffix); + if (!addSuffixSuccess) { + return false; + } + overlayModule++; + } + CopyBuffer(ovFilename, contentPtr, fileSize - ((size_t)contentPtr - (size_t)buffer)); + free(component->overlayDefs.fileInfo.content); + component->overlayDefs.fileInfo.content = buffer; + component->overlayDefs.fileInfo.fileSize = fileSize; + component->overlayDefs.fileInfo.rewrite = true; + component->overlayDefs.header = buffer; + component->overlayFilenames = buffer + 0x10; + success = true; + } + } + return success; +} + +void FreeOverlayModules(Component *component) { + OverlayModule *overlayModule = component->overlayModules; + if (component->numOverlays == 0 || overlayModule == NULL) { + DebugPrintf("No overlay to release\n"); + } else { + for (int i = 0; i < component->numOverlays; i++) { + DebugPrintf("Releasing overlay[%d]\n", i); + FreeBuffer(&overlayModule->fileInfo); + overlayModule++; + } + } + if (component->overlayModules != NULL) { + free(component->overlayModules); + component->overlayModules = NULL; + } +} diff --git a/tools/compstatic/overlay_modules.h b/tools/compstatic/overlay_modules.h new file mode 100644 index 000000000..e10eae9ef --- /dev/null +++ b/tools/compstatic/overlay_modules.h @@ -0,0 +1,12 @@ +#ifndef OVERLAY_MODULE_H +#define OVERLAY_MODULE_H + +#include +#include "global.h" + +bool ReadOverlayModules(Component *component); +bool WriteOverlayModules(Component *component); +bool AddSuffixOverlayModules(Component *component, char *suffix); +void FreeOverlayModules(Component *component); + +#endif // OVERLAY_MODULE_H diff --git a/tools/compstatic/overlay_table.c b/tools/compstatic/overlay_table.c new file mode 100644 index 000000000..cf0a3445c --- /dev/null +++ b/tools/compstatic/overlay_table.c @@ -0,0 +1,76 @@ +#include +#include +#include "files.h" +#include "global.h" +#include "misc.h" +#include "overlay_table.h" +#include "print.h" + +bool ReadOverlayTable(Component *component, char *filename) { + OverlayTable *overlayTable = &component->overlayTable; + if (filename == NULL || component->numOverlays < 1) { + DebugPrintf("No overlay table to read\n"); + overlayTable->fileInfo.content = NULL; + overlayTable->fileInfo.fileSize = 0; + overlayTable->fileInfo.filename = NULL; + } else { + DebugPrintf("Reading overlayTable=%s\n", filename); + int fileSize = ReadFile(filename, &overlayTable->fileInfo.content); + overlayTable->fileInfo.fileSize = fileSize; + overlayTable->fileInfo.filename = strdup(filename); + } + overlayTable->fileInfo.compressedSize = 0; + overlayTable->fileInfo.rewrite = false; + bool success = overlayTable->fileInfo.fileSize == component->numOverlays * OVERLAY_ENTRY_SIZE; + if (success) { + overlayTable->table = overlayTable->fileInfo.content; + } else { + ErrorPrintf("Not matched number of overlays and size of overlay table\n"); + } + return success; +} + +bool WriteOverlayTable(Component *component) { + bool success; + OverlayTable *overlayTable = &component->overlayTable; + + if (!overlayTable->fileInfo.rewrite) { + success = true; + } else if (overlayTable->fileInfo.filename == NULL) { + ErrorPrintf("No filename specified for overlay table\n"); + success = false; + } else { + DebugPrintf("Writing overlayTable=%s\n", overlayTable->fileInfo.filename); + if (overlayTable->fileInfo.compressedSize == 0) { + int sizeWritten = WriteFile(overlayTable->fileInfo.filename, overlayTable->fileInfo.content, overlayTable->fileInfo.fileSize); + success = sizeWritten >= 0; + } else { + ErrorPrintf("Don\'t support compressed overlay table\n"); + success = false; + } + } + return success; +} + +bool AddSuffixOverlayTable(Component *component, char *suffix) { + bool success; + + if (component->overlayTable.fileInfo.content == NULL || component->numOverlays == 0) { + DebugPrintf("No overlaytable to add suffix\n"); + success = true; + } else { + DebugPrintf("Adding suffix overlayTable=%s\n", suffix); + success = AddSuffixBuffer(&component->overlayTable.fileInfo, suffix); + } + return success; +} + +void FreeOverlayTable(Component *component) { + if (component->overlayTable.fileInfo.content == NULL) { + DebugPrintf("No overlaytable to release\n"); + } else { + DebugPrintf("Releasing overlayTable\n"); + FreeBuffer(&component->overlayTable.fileInfo); + } + component->overlayTable.table = NULL; +} diff --git a/tools/compstatic/overlay_table.h b/tools/compstatic/overlay_table.h new file mode 100644 index 000000000..7343a2dba --- /dev/null +++ b/tools/compstatic/overlay_table.h @@ -0,0 +1,18 @@ +#ifndef OVERLAY_TABLE_H +#define OVERLAY_TABLE_H + +#include +#include "component.h" + +#define OVERLAY_ENTRY_SIZE 32 + +#define OT_FILESIZE_OFFSET 0x8 +#define OT_COMPRESSED_FILESIZE_OFFSET 0x1c +#define OT_COMPRESSED_FLAGS_OFFSET 0x1f + +bool ReadOverlayTable(Component *component, char *filename); +bool WriteOverlayTable(Component *component); +bool AddSuffixOverlayTable(Component *component, char *suffix); +void FreeOverlayTable(Component *component); + +#endif // OVERLAY_TABLE_H diff --git a/tools/compstatic/print.c b/tools/compstatic/print.c new file mode 100644 index 000000000..d0750529b --- /dev/null +++ b/tools/compstatic/print.c @@ -0,0 +1,22 @@ +#include +#include +#include "global.h" +#include "print.h" + +void DebugPrintf(const char *format, ...) { + if (!gDebugMode) return; + + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +} + +void ErrorPrintf(const char *format, ...) { + printf("Error: "); + + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +} diff --git a/tools/compstatic/print.h b/tools/compstatic/print.h new file mode 100644 index 000000000..afc6a409b --- /dev/null +++ b/tools/compstatic/print.h @@ -0,0 +1,7 @@ +#ifndef PRINT_H +#define PRINT_H + +void DebugPrintf(const char *format, ...); +void ErrorPrintf(const char *format, ...); + +#endif // PRINT_H diff --git a/tools/compstatic/static_module.c b/tools/compstatic/static_module.c new file mode 100644 index 000000000..84a26d5ad --- /dev/null +++ b/tools/compstatic/static_module.c @@ -0,0 +1,74 @@ +#include +#include +#include "files.h" +#include "global.h" +#include "misc.h" +#include "overlay_defs.h" +#include "print.h" +#include "static_module.h" + +bool ReadStaticModule(Component *component, char *filename) { + bool success; + + StaticModule *staticModule = &component->staticModule; + + DebugPrintf("Reading staticModule=%s\n", filename); + int fileSize = ReadFile(filename, &staticModule->fileInfo.content); + staticModule->fileInfo.fileSize = fileSize; + staticModule->fileInfo.compressedSize = 0; + staticModule->fileInfo.rewrite = false; + staticModule->fileInfo.filename = strdup(filename); + if (staticModule->fileInfo.fileSize < 0) { + success = false; + } else if (staticModule->fileInfo.fileSize % 4 == 0) { + int footerOffset = *(int *)(component->overlayDefs.header + OVERLAY_DEFS_FOOTER_OFFSET); + uint footerOffsetAligned = ALIGN_4(footerOffset); + staticModule->footerSize = staticModule->fileInfo.fileSize - footerOffsetAligned; + staticModule->footerContent = staticModule->fileInfo.content + footerOffsetAligned; + staticModule->fileInfo.fileSize = footerOffset; + + int staticParamsOffset = *(uint *)(staticModule->footerContent + 4); + if (staticModule->footerSize < 0xc || *(uint *)(staticModule->footerContent + STATIC_FOOTER_MAGIC_OFFSET) != 0xDEC00621) { + ErrorPrintf("Static module is older format (No footer extension).\n"); + success = false; + } else if (((staticParamsOffset % 4) == 0) && + (staticParamsOffset > -1) && + (staticParamsOffset < footerOffset)) { + success = true; + } else { + ErrorPrintf("Static module is illegal format (Unknown footer format).\n"); + success = false; + } + } else { + ErrorPrintf("Static module size is illegal (size=%d). Must be aligned by 4\n"); + success = false; + } + return success; +} + +bool WriteStaticModule(Component *component) { + bool success; + StaticModule *staticModule = &component->staticModule; + + if (!staticModule->fileInfo.rewrite) { + success = true; + } else { + DebugPrintf("Writing staticModule=%s\n", staticModule->fileInfo.filename); + int fileSize = staticModule->fileInfo.compressedSize ? staticModule->fileInfo.compressedSize : staticModule->fileInfo.fileSize; + int sizeWritten = WriteFile(staticModule->fileInfo.filename, staticModule->fileInfo.content, fileSize + staticModule->footerSize); + success = sizeWritten >= 0; + } + return success; +} + +bool AddSuffixStaticModule(Component *component, char *suffix) { + DebugPrintf("Adding suffix staticModule=%s\n", suffix); + return AddSuffixBuffer(&component->staticModule.fileInfo, suffix); +} + +void FreeStaticModule(Component *component) { + DebugPrintf("Releasing staticModule\n"); + FreeBuffer(&component->staticModule.fileInfo); + component->staticModule.footerContent = NULL; + component->staticModule.footerSize = 0; +} diff --git a/tools/compstatic/static_module.h b/tools/compstatic/static_module.h new file mode 100644 index 000000000..15691a2fa --- /dev/null +++ b/tools/compstatic/static_module.h @@ -0,0 +1,25 @@ +#ifndef STATIC_MODULE_H +#define STATIC_MODULE_H + +#include +#include "global.h" + +#define STATIC_FOOTER_MAGIC_OFFSET (0) +#define STATIC_FOOTER_STATIC_PARAM_OFFSET (4) +#define STATIC_FOOTER_DIGEST_PARAM_OFFSET (8) + +typedef struct { + uint autoloadList; + uint autoloadListEnd; + uint autoloadStart; + uint staticBssStart; + uint staticBssEnd; + int compressedStatic; +} StaticParams; + +bool ReadStaticModule(Component *component, char *filename); +bool WriteStaticModule(Component *component); +bool AddSuffixStaticModule(Component *component, char *suffix); +void FreeStaticModule(Component *component); + +#endif // STATIC_MODULE_H diff --git a/tools/compstatic/str.c b/tools/compstatic/str.c new file mode 100644 index 000000000..6bf16ea5e --- /dev/null +++ b/tools/compstatic/str.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include "print.h" +#include "str.h" + +char *StrCat(int count, ...) { + char *dest; + char *word; + int length; + + va_list args; + va_start(args, count); + va_list argsCpy; + va_copy(argsCpy, args); + + int totalLength = 0; + for (int i = 0; i < count; i++) { + word = va_arg(args, char *); + length = strlen(word); + totalLength += length; + } + va_end(args); + dest = malloc(totalLength + 1); + if (dest == NULL) { + ErrorPrintf("Cannot allocate memory\n"); + exit(1); + } + + dest[0] = '\0'; + for (int i = 0; i < count; i++) { + word = va_arg(argsCpy, char *); + length = strlen(word); + strcat(dest, word); + } + va_end(argsCpy); + return dest; +} + +char *StrDup(char *prevDest, char *src) { + if (prevDest != NULL) { + free(prevDest); + } + if (src != NULL) { + src = strdup(src); + if (src == NULL) { + ErrorPrintf("Cannot allocate memory\n"); + exit(1); + } + } + return src; +} diff --git a/tools/compstatic/str.h b/tools/compstatic/str.h new file mode 100644 index 000000000..0305660a9 --- /dev/null +++ b/tools/compstatic/str.h @@ -0,0 +1,7 @@ +#ifndef STR_H +#define STR_H + +char *StrCat(int count, ...); +char *StrDup(char *prevDest, char *src); + +#endif // STR_H