From 14d2638a2fef8c3340a4a4330bbccb03f4f2e0ca Mon Sep 17 00:00:00 2001 From: Daniel Stien Date: Tue, 9 Jan 2024 01:13:28 +1100 Subject: [PATCH] Support multiple compression formats in context type. --- include/stunpack.h | 61 ++++++++++++++++++++++++++++++++++++++++------ src/lib/stunpack.c | 29 +++++++++++----------- src/main.c | 40 +++++++++++++++++------------- 3 files changed, 91 insertions(+), 39 deletions(-) diff --git a/include/stunpack.h b/include/stunpack.h index 369b8c7..29bc59a 100644 --- a/include/stunpack.h +++ b/include/stunpack.h @@ -51,8 +51,54 @@ #define STPK_HUFF_PREFIX_MSB (1 << (STPK_HUFF_PREFIX_WIDTH - 1)) #define STPK_HUFF_WIDTH_ESC 0x40 -typedef enum { STPK_VER_AUTO, STPK_VER_STUNTS10, STPK_VER_STUNTS11 } stpk_Version; -typedef enum { STPK_LOG_INFO, STPK_LOG_WARN, STPK_LOG_ERR } stpk_LogType; +typedef enum { + // Automatic format detection when decompressing. + STPK_FMT_AUTO, + // Custom compression format used by the PC versions of Stunts/4-D Sports Driving. + STPK_FMT_STUNTS, + // EA compression library by Frank Barchard used by 4-D Sports Driving for Amiga and 4-D Driving for PC98. + STPK_FMT_BARCHARD, + // Amiga RPck archiver format used for 3-d shapes in 4-D Sports Driving for Amiga. + STPK_FMT_RPCK +} stpk_FmtType; + +typedef enum { + STPK_FMT_STUNTS_VER_AUTO, + STPK_FMT_STUNTS_VER_1_0, + STPK_FMT_STUNTS_VER_1_1 +} stpk_FmtStuntsVer; + +//typedef enum { +// STPK_FMT_STUNTS_RLE, +// STPK_FMT_STUNTS_HUFF +//} stpk_FmtStuntsMethod; + +typedef struct { + stpk_FmtStuntsVer version; + int maxPasses; +} stpk_FmtStunts; + +//typedef struct { +//} stpk_FmtBarchard; + +//typedef struct { +//} stpk_FmtRpck; + +typedef struct { + stpk_FmtType type; + union { + stpk_FmtStunts stunts; + //stpk_FmtBarchard barchard; + //stpk_FmtRpck rpck; + }; +} stpk_Format; + +typedef enum { + STPK_LOG_INFO, + STPK_LOG_WARN, + STPK_LOG_ERR +} stpk_LogType; + typedef void (*stpk_LogCallback)(stpk_LogType type, const char *msg, ...); typedef void* (*stpk_AllocCallback)(size_t size); typedef void (*stpk_DeallocCallback)(void *ptr); @@ -66,18 +112,19 @@ typedef struct { typedef struct { stpk_Buffer src; stpk_Buffer dst; - stpk_Version version; - int maxPasses; + stpk_Format format; int verbosity; stpk_LogCallback logCallback; stpk_AllocCallback allocCallback; stpk_DeallocCallback deallocCallback; } stpk_Context; -const char *stpk_versionStr(stpk_Version version); -stpk_Context stpk_init(stpk_Version version, int maxPasses, int verbosity, stpk_LogCallback logCallback, stpk_AllocCallback allocCallback, stpk_DeallocCallback deallocCallback); +stpk_Context stpk_init(stpk_Format format, int verbosity, stpk_LogCallback logCallback, stpk_AllocCallback allocCallback, stpk_DeallocCallback deallocCallback); void stpk_deinit(stpk_Context *ctx); -unsigned int stpk_decomp(stpk_Context *ctx); + +unsigned int stpk_decompress(stpk_Context *ctx); + +const char *stpk_versionStr(stpk_FmtStuntsVer version); unsigned int stpk_decompRLE(stpk_Context *ctx); unsigned int stpk_rleDecodeSeq(stpk_Context *ctx, unsigned char esc); diff --git a/src/lib/stunpack.c b/src/lib/stunpack.c index cb19993..db01afd 100644 --- a/src/lib/stunpack.c +++ b/src/lib/stunpack.c @@ -45,21 +45,21 @@ inline void stpk_dst2src(stpk_Context *ctx); char *stpk_stringBits16(unsigned short val); void stpk_printArray(const stpk_Context *ctx, const unsigned char *arr, unsigned int len, const char *name); -const char *stpk_versionStr(stpk_Version version) +const char *stpk_versionStr(stpk_FmtStuntsVer version) { switch (version) { - case STPK_VER_AUTO: + case STPK_FMT_STUNTS_VER_AUTO: return "auto"; - case STPK_VER_STUNTS10: + case STPK_FMT_STUNTS_VER_1_0: return "stunts1.0"; - case STPK_VER_STUNTS11: + case STPK_FMT_STUNTS_VER_1_1: return "stunts1.1"; default: return "Unknown"; } } -stpk_Context stpk_init(stpk_Version version, int maxPasses, int verbosity, stpk_LogCallback logCallback, stpk_AllocCallback allocCallback, stpk_DeallocCallback deallocCallback) +stpk_Context stpk_init(stpk_Format format, int verbosity, stpk_LogCallback logCallback, stpk_AllocCallback allocCallback, stpk_DeallocCallback deallocCallback) { stpk_Buffer empty = { .data = NULL, @@ -71,8 +71,7 @@ stpk_Context stpk_init(stpk_Version version, int maxPasses, int verbosity, stpk_ stpk_Context ctx; ctx.src = empty; ctx.dst = empty; - ctx.version = version; - ctx.maxPasses = maxPasses; + ctx.format = format; ctx.verbosity = verbosity; ctx.logCallback = logCallback; ctx.allocCallback = allocCallback; @@ -125,12 +124,12 @@ int inline stpk_isRle(stpk_Buffer *buf) } // Decompress sub-files in source buffer. -unsigned int stpk_decomp(stpk_Context *ctx) +unsigned int stpk_decompress(stpk_Context *ctx) { unsigned char passes, type, i; unsigned int retval = 1, finalLen, srcOffset; - STPK_VERBOSE1(" %-10s %s\n", "version", stpk_versionStr(ctx->version)); + STPK_VERBOSE1(" %-10s %s\n", "version", stpk_versionStr(ctx->format.stunts.version)); passes = ctx->src.data[ctx->src.offset]; if (STPK_GET_FLAG(passes, STPK_PASSES_RECUR)) { @@ -175,7 +174,7 @@ unsigned int stpk_decomp(stpk_Context *ctx) srcOffset = ctx->src.offset; retval = stpk_decompHuff(ctx); // If selected version is "auto", check if we should retry with BB Stunts 1.0. - if (ctx->version == STPK_VER_AUTO + if (ctx->format.stunts.version == STPK_FMT_STUNTS_VER_AUTO && ( // Decompression failed. retval == STPK_RET_ERR @@ -186,12 +185,12 @@ unsigned int stpk_decomp(stpk_Context *ctx) ) ) { STPK_WARN("Huffman decompression with Stunts 1.1 bit stream format failed, retrying with Stunts 1.0 format.\n"); - ctx->version = STPK_VER_STUNTS10; + ctx->format.stunts.version = STPK_FMT_STUNTS_VER_1_0; ctx->src.offset = srcOffset; ctx->dst.offset = 0; STPK_NOVERBOSE("Pass %d/%d: ", i + 1, passes); retval = stpk_decompHuff(ctx); - ctx->version = STPK_VER_AUTO; + ctx->format.stunts.version = STPK_FMT_STUNTS_VER_AUTO; } // Data left must be checked for BB Stunts 1.0 bit stream detection @@ -210,8 +209,8 @@ unsigned int stpk_decomp(stpk_Context *ctx) return retval; } - if (i + 1 == ctx->maxPasses && passes != ctx->maxPasses) { - STPK_MSG("Parsing limited to %d decompression pass(es), aborting.\n", ctx->maxPasses); + if (i + 1 == ctx->format.stunts.maxPasses && passes != ctx->format.stunts.maxPasses) { + STPK_MSG("Parsing limited to %d decompression pass(es), aborting.\n", ctx->format.stunts.maxPasses); return 0; } @@ -677,7 +676,7 @@ inline unsigned char stpk_getHuffByte(stpk_Context *ctx) }; unsigned char byte = ctx->src.data[ctx->src.offset++]; - if (ctx->version == STPK_VER_STUNTS10) { + if (ctx->format.stunts.version == STPK_FMT_STUNTS_VER_1_0) { byte = reverseBits[byte]; } return byte; diff --git a/src/main.c b/src/main.c index d147bfe..863fa67 100644 --- a/src/main.c +++ b/src/main.c @@ -40,31 +40,37 @@ #define VERBOSE(msg, ...) if (verbose > 1) printf(msg, ## __VA_ARGS__) void printHelp(char *progName); -int decompress(char *srcFileName, char *dstFileName, stpk_Version version, int passes, int verbose); +int decompress(char *srcFileName, char *dstFileName, stpk_Format format, int verbose); int main(int argc, char **argv) { char *srcFileName = NULL, *dstFileName = NULL; - int retval = 0, opt, passes = 0, verbose = 1, srcFileNameLen = 0; + int retval = 0, opt, verbose = 1, srcFileNameLen = 0; #if defined(__WATCOMC__) const int dstFileNamePostfixLen = 0; #else const int dstFileNamePostfixLen = 4; #endif - stpk_Version gameVersion = STPK_VER_AUTO; + stpk_FmtStunts stunts = { + .version = STPK_FMT_STUNTS_VER_AUTO, + .maxPasses = 0 + }; + stpk_Format format; + format.type = STPK_FMT_STUNTS; + format.stunts = stunts; // Parse options. while ((opt = getopt(argc, argv, "g:p:hqv")) != -1) { switch (opt) { case 'g': - if (strcasecmp(optarg, stpk_versionStr(STPK_VER_AUTO)) == 0) { - gameVersion = STPK_VER_AUTO; + if (strcasecmp(optarg, stpk_versionStr(STPK_FMT_STUNTS_VER_AUTO)) == 0) { + format.stunts.version = STPK_FMT_STUNTS_VER_AUTO; } - else if (strcasecmp(optarg, stpk_versionStr(STPK_VER_STUNTS10)) == 0) { - gameVersion = STPK_VER_STUNTS10; + else if (strcasecmp(optarg, stpk_versionStr(STPK_FMT_STUNTS_VER_1_0)) == 0) { + format.stunts.version = STPK_FMT_STUNTS_VER_1_0; } - else if (strcasecmp(optarg, stpk_versionStr(STPK_VER_STUNTS11)) == 0) { - gameVersion = STPK_VER_STUNTS11; + else if (strcasecmp(optarg, stpk_versionStr(STPK_FMT_STUNTS_VER_1_1)) == 0) { + format.stunts.version = STPK_FMT_STUNTS_VER_1_1; } else { fprintf(stderr, "Invalid game version \"%s\".\n", optarg); @@ -72,7 +78,7 @@ int main(int argc, char **argv) } break; case 'p': - passes = atoi(optarg); + format.stunts.maxPasses = atoi(optarg); break; case 'h': printHelp(argv[0]); @@ -121,7 +127,7 @@ int main(int argc, char **argv) #endif } - retval = decompress(srcFileName, dstFileName, gameVersion, passes, verbose); + retval = decompress(srcFileName, dstFileName, format, verbose); // Clean up. if (dstFileName != NULL && srcFileNameLen) { @@ -137,9 +143,9 @@ void printHelp(char *progName) printf(USAGE, progName); printf(" -g VER game version: \"%s\" (default), \"%s\", \"%s\"\n", - stpk_versionStr(STPK_VER_AUTO), - stpk_versionStr(STPK_VER_STUNTS10), - stpk_versionStr(STPK_VER_STUNTS11) + stpk_versionStr(STPK_FMT_STUNTS_VER_AUTO), + stpk_versionStr(STPK_FMT_STUNTS_VER_1_0), + stpk_versionStr(STPK_FMT_STUNTS_VER_1_1) ); printf(" -p NUM limit to NUM decompression passes\n"); printf(" -v verbose output\n"); @@ -173,12 +179,12 @@ void logCallback(stpk_LogType type, const char *msg, ...) va_end(args); } -int decompress(char *srcFileName, char *dstFileName, stpk_Version version, int passes, int verbose) +int decompress(char *srcFileName, char *dstFileName, stpk_Format format, int verbose) { unsigned int retval = 1; FILE *srcFile, *dstFile; - stpk_Context ctx = stpk_init(version, passes, verbose, logCallback, malloc, free); + stpk_Context ctx = stpk_init(format, verbose, logCallback, malloc, free); if ((srcFile = fopen(srcFileName, "rb")) == NULL) { ERR("Error opening source file \"%s\" for reading. (%s)\n", srcFileName, strerror(errno)); @@ -217,7 +223,7 @@ int decompress(char *srcFileName, char *dstFileName, stpk_Version version, int p goto freeBuffers; } - retval = stpk_decomp(&ctx); + retval = stpk_decompress(&ctx); // Flush unpacked data to file. if (!retval) {