From 505061a8d2d9bee304e7f080abb55d9b68ac3042 Mon Sep 17 00:00:00 2001
From: Shpoike <Shpoike@users.noreply.github.com>
Date: Mon, 27 Mar 2023 13:53:40 +0100
Subject: [PATCH] Add deflate64 support, cos why not.

---
 engine/Makefile         |  19 ++++-
 engine/common/fs_zip.c  | 138 +++++++++++++++++++++++++++++--
 engine/qclib/qcctui.c   | 174 ++++++++++++++++++++++++++++------------
 engine/qclib/qcd_main.c | 151 +++++++++++++++++++++++++++-------
 4 files changed, 391 insertions(+), 91 deletions(-)

diff --git a/engine/Makefile b/engine/Makefile
index d143a043c..ab94569d8 100644
--- a/engine/Makefile
+++ b/engine/Makefile
@@ -1044,6 +1044,14 @@ endif
 ifeq (1,$(LINK_ZLIB))
     CLIENTLIBFLAGS+=-DZLIB_STATIC
     CLIENTLDDEPS+=-lz
+
+    #and deflate64, because why not.
+    ifneq ("$(wildcard $(ARCHLIBS)/infback9.h)","")
+        CLIENTLIBFLAGS+=-DZLIB_DEFLATE64
+        CLIENTLDDEPS+=-lz9
+        QCC_CFLAGS+=-DZLIB_DEFLATE64
+        QCC_LDFLAGS+=-lz9
+    endif
 endif
 ifeq (1,$(LINK_ODE))
     ALL_CFLAGS+=$(shell $(PKGCONFIG) ode --cflags --silence-errors) -DODE_STATIC
@@ -2124,7 +2132,7 @@ m-profile:
 
 
 _qcc-tmp: $(REQDIR)
-	@$(MAKE) $(TYPE) EXE_NAME="$(EXE_NAME)$(EXEPOSTFIX)" PRECOMPHEADERS="" OUT_DIR="$(OUT_DIR)" WCFLAGS="$(CLIENT_ONLY_CFLAGS) $(WCFLAGS)" LDFLAGS="$(LDFLAGS) $(QCC_LDFLAGS)" OBJS="QCC_OBJS SOBJS"
+	@$(MAKE) $(TYPE) EXE_NAME="$(EXE_NAME)$(EXEPOSTFIX)" PRECOMPHEADERS="" OUT_DIR="$(OUT_DIR)" WCFLAGS="$(QCC_CFLAGS) $(WCFLAGS)" LDFLAGS="$(LDFLAGS) $(QCC_LDFLAGS)" OBJS="QCC_OBJS SOBJS"
 qcc-rel:
 	@$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqcc$(BITS)" OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(QCC_DIR)" SOBJS="qcctui.o packager.o $(if $(findstring win,$(FTE_TARGET)),fteqcc.o)"
 qccgui-rel:
@@ -2429,6 +2437,13 @@ libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc:
 	test -f zlib-$(ZLIBVER).tar.gz || wget http://zlib.net/zlib-$(ZLIBVER).tar.gz
 	-test -f libs-$(ARCH)/libz.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../zlib-$(ZLIBVER).tar.gz && cd zlib-$(ZLIBVER) && $(TOOLOVERRIDES) ./configure --static && $(TOOLOVERRIDES) $(MAKE) libz.a CC="$(CC) $(W32_CFLAGS) -fPIC" && cp libz.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libz.a && cp zlib.h zconf.h zutil.h zlib.pc ../ )
 endif
+libs-$(ARCH)/libz9.a: libs-$(ARCH)/libz.a
+	(cd libs-$(ARCH)/zlib-$(ZLIBVER) && \
+		$(CC) -o contrib/infback9/infback9.o -c contrib/infback9/infback9.c -I.	&& \
+		$(CC) -o contrib/infback9/inftree9.o -c contrib/infback9/inftree9.c -I.	&& \
+		cp contrib/infback9/infback9.h .. && \
+		$(AR) rcs ../libz9.a contrib/infback9/infback9.o contrib/infback9/inftree9.o)
+
 
 libs-$(ARCH)/libpng.a libs-$(ARCH)/libpng.pc: libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc
 	test -f libpng-$(PNGVER).tar.gz || wget http://prdownloads.sourceforge.net/libpng/libpng-$(PNGVER).tar.gz?download -O libpng-$(PNGVER).tar.gz
@@ -2465,7 +2480,7 @@ libs-$(ARCH)/libBulletDynamics.a:
 ifeq ($(FTE_TARGET),web)
 makelibs: libs-$(ARCH)/libz.a $(MAKELIBS)
 else
-makelibs: libs-$(ARCH)/libjpeg.a libs-$(ARCH)/libz.a libs-$(ARCH)/libpng.a libs-$(ARCH)/libogg.a libs-$(ARCH)/libvorbis.a libs-$(ARCH)/libopus.a libs-$(ARCH)/libspeex.a libs-$(ARCH)/libspeexdsp.a libs-$(ARCH)/libfreetype.a $(MAKELIBS)
+makelibs: libs-$(ARCH)/libjpeg.a libs-$(ARCH)/libz9.a libs-$(ARCH)/libz.a libs-$(ARCH)/libpng.a libs-$(ARCH)/libogg.a libs-$(ARCH)/libvorbis.a libs-$(ARCH)/libopus.a libs-$(ARCH)/libspeex.a libs-$(ARCH)/libspeexdsp.a libs-$(ARCH)/libfreetype.a $(MAKELIBS)
 endif
 
 HTTP_OBJECTS=http/httpserver.c http/iwebiface.c common/fs_stdio.c http/ftpserver.c
diff --git a/engine/common/fs_zip.c b/engine/common/fs_zip.c
index fcb771327..760bb101a 100644
--- a/engine/common/fs_zip.c
+++ b/engine/common/fs_zip.c
@@ -607,6 +607,9 @@ typedef struct
 #define ZFL_SYMLINK		(1u<<3)	//file is a symlink
 #define ZFL_CORRUPT		(1u<<4)	//file is corrupt or otherwise unreadable (usually just means we don't support reading it rather than actually corrupt, but hey).
 #define ZFL_WEAKENCRYPT	(1u<<5)	//traditional zip encryption
+#define ZFL_DEFLATE64D	(1u<<6)	//need to use zlib's 'inflateBack9' stuff.
+
+#define ZFL_COMPRESSIONTYPE (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED|ZFL_DEFLATE64D|ZFL_BZIP2)
 
 
 typedef struct zipfile_s
@@ -1030,6 +1033,95 @@ static struct decompressstate *FSZIP_Deflate_Init(zipfile_t *source, qofs_t star
 }
 #endif
 
+#if defined(ZLIB_DEFLATE64) && !defined(AVAIL_ZLIB)
+	#undef ZLIB_DEFLATE64	//don't be silly.
+#endif
+#if defined(ZLIB_DEFLATE64)
+#include "infback9.h"	//an obscure compile-your-own part of zlib.
+struct def64ctx
+{
+	vfsfile_t *src;
+	vfsfile_t *dst;
+	qofs_t csize;
+	qofs_t usize;
+	unsigned int crc;
+
+	qbyte inbuf[0x8000];
+};
+static unsigned int FSZIP_Deflate64_Grab(void *vctx, unsigned char **bufptr)
+{
+	struct def64ctx *ctx = vctx;
+	int avail;
+	avail = sizeof(ctx->inbuf);
+	if (avail > ctx->csize)
+		avail = ctx->csize;	//don't over-read.
+	if (avail <= 0)
+	{
+		*bufptr = NULL;
+		return 0;
+	}
+
+	avail = VFS_READ(ctx->src, ctx->inbuf, avail);
+	*bufptr = ctx->inbuf;
+
+	if (avail < 0)
+		avail = 0;	//treated as eof...
+	ctx->csize -= avail;
+	return avail;
+}
+static int FSZIP_Deflate64_Spew(void *vctx, unsigned char *buf, unsigned int buflen)
+{
+	struct def64ctx *ctx = vctx;
+
+	//update the crc
+	ctx->crc = crc32(ctx->crc, buf, buflen);
+	ctx->usize += buflen;
+
+	if (VFS_WRITE(ctx->dst, buf, buflen) != buflen)
+		return 1;	//failure returns non-zero.
+	return 0;
+}
+//inflateBack stuff is apparently not restartable, and must read the entire file
+static vfsfile_t *FSZIP_Deflate64(vfsfile_t *src, qofs_t csize, qofs_t usize, unsigned int crc32)
+{
+	z_stream strm = {NULL};
+	qbyte window[65536];
+	struct def64ctx ctx;
+	ctx.src = src;
+	ctx.dst = VFSPIPE_Open(1, true);
+	ctx.csize = csize;
+	ctx.usize = 0;
+	ctx.crc = 0;
+
+	strm.data_type = Z_UNKNOWN;
+	inflateBack9Init(&strm, window);
+	//getting inflateBack9 to
+	if (Z_STREAM_END != inflateBack9(&strm, FSZIP_Deflate64_Grab, &ctx, FSZIP_Deflate64_Spew, &ctx))
+	{	//some stream error?
+		Con_Printf("Decompression error\n");
+		VFS_CLOSE(ctx.dst);
+		ctx.dst = NULL;
+	}
+	else if (ctx.csize != 0 || ctx.usize != usize)
+	{	//corrupt file table?
+		Con_Printf("Decompression size error\n");
+		Con_Printf("read %i of %i bytes\n", (unsigned)ctx.csize, (unsigned)csize);
+		Con_Printf("wrote %i of %i bytes\n", (unsigned)ctx.usize, (unsigned)usize);
+		VFS_CLOSE(ctx.dst);
+		ctx.dst = NULL;
+	}
+	else if (ctx.crc != crc32)
+	{	//corrupt file table?
+		Con_Printf("CRC32 error\n");
+		VFS_CLOSE(ctx.dst);
+		ctx.dst = NULL;
+	}
+	inflateBack9End(&strm);
+
+	return ctx.dst;
+}
+#endif
+
 #ifdef AVAIL_BZLIB
 //if the offset is still within our decompressed block then we can just rewind a smidge
 static qboolean FSZIP_BZip2_Seek(struct decompressstate *st, qofs_t *offset, qofs_t newoffset)
@@ -1403,13 +1495,40 @@ static vfsfile_t *QDECL FSZIP_OpenVFS(searchpathfuncs_t *handle, flocation_t *lo
 	vfsz->funcs.WriteBytes = NULL;
 	vfsz->funcs.seekstyle = SS_SLOW;
 
+	//NOTE: pf->name is the quakeified name, and may have an extra prefix/stripped prefix for certain zips - different from what you'd see if you opened the zip yourself. this is only relevant for debugging, whuch might be misleading but is not fatal.
 	if (!FSZIP_ValidateLocalHeader(zip, pf, &vfsz->startpos, &datasize))
 	{
-		Con_Printf("file %s:%s is incompatible or inconsistent with zip central directory\n", zip->filename, pf->name);
+		Con_Printf(CON_WARNING"file %s:%s is incompatible or inconsistent with zip central directory\n", zip->filename, pf->name);
 		Z_Free(vfsz);
 		return NULL;
 	}
 
+	if (flags & ZFL_DEFLATE64D)
+	{	//crap
+#if defined(ZLIB_DEFLATE64)
+		vfsfile_t *tmp = NULL;
+		qofs_t startpos = vfsz->startpos;
+		qofs_t usize = vfsz->length;
+		qofs_t csize = datasize;
+		Z_Free(vfsz);
+
+		Con_Printf(CON_WARNING"file %s:%s was compressed with deflate64\n", zip->filename, pf->name);
+
+		if (Sys_LockMutex(zip->mutex))
+		{
+			VFS_SEEK(vfsz->parent->raw, startpos);
+			tmp = FSZIP_Deflate64(zip->raw, csize, usize, pf->crc);
+			Sys_UnlockMutex(zip->mutex);
+		}
+
+		return tmp;
+#else
+		Con_Printf(CON_WARNING"%s:%s: deflate64 not supported\n", COM_SkipPath(zip->filename), pf->name);
+		Z_Free(vfsz);
+		return NULL;
+#endif
+	}
+
 	if (flags & ZFL_DEFLATED)
 	{
 #ifdef AVAIL_ZLIB
@@ -1670,11 +1789,19 @@ static qboolean FSZIP_ValidateLocalHeader(zipfile_t *zip, zpackfile_t *zfile, qo
 		return false;	//FIXME: proper spanned zips fragment compressed data over multiple spans, but we don't support that
 
 	if (local.cmethod == 0)
-		return (zfile->flags & (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED|ZFL_BZIP2)) == ZFL_STORED;
+		return (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_STORED;
+#if defined(AVAIL_ZLIB)
 	if (local.cmethod == 8)
-		return (zfile->flags & (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED|ZFL_BZIP2)) == ZFL_DEFLATED;
+		return (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_DEFLATED;
+#endif
+#if defined(ZLIB_DEFLATE64)
+	if (local.cmethod == 9)
+		return (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_DEFLATE64D;
+#endif
+#ifdef AVAIL_BZLIB
 	if (local.cmethod == 12)
-		return (zfile->flags & (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED|ZFL_BZIP2)) == ZFL_BZIP2;
+		return (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_BZIP2;
+#endif
 	return false;	//some other method that we don't know.
 }
 
@@ -1850,7 +1977,8 @@ static qboolean FSZIP_ReadCentralEntry(zipfile_t *zip, qbyte *data, struct zipce
 	//7: tokenize
 	else if (entry->cmethod == 8)	//8: deflate
 		entry->flags |= ZFL_DEFLATED;
-	//9: deflate64 - patented. sometimes written by microsoft's crap, so this might be problematic. only minor improvements.
+	else if (entry->cmethod == 9)	//9: deflate64 - patented. sometimes written by microsoft's crap, so this might be problematic. only minor improvements.
+		entry->flags |= ZFL_DEFLATE64D;
 	//10: implode
 	else if (entry->cmethod == 12)	//12: bzip2
 		entry->flags |= ZFL_BZIP2;
diff --git a/engine/qclib/qcctui.c b/engine/qclib/qcctui.c
index c7879557c..fd15effed 100644
--- a/engine/qclib/qcctui.c
+++ b/engine/qclib/qcctui.c
@@ -120,7 +120,16 @@ static void QCC_FileList(const char *name, const void *compdata, size_t compsize
 {
 	totalsize += plainsize;
 	filecount += 1;
-	if (!method && compsize==plainsize)
+	if (method < 0)
+	{
+		if (method == -1-9)
+			externs->Printf("%s%8u DF64 %s%s\n", col_error, (unsigned)plainsize, name, col_none);
+		else if (method == -1)	//general error
+			externs->Printf("%s%8u ERR %s%s\n", col_error, (unsigned)plainsize, name, col_none);
+		else
+			externs->Printf("%s%8u m%-3i %s%s\n", col_error, (unsigned)plainsize, -1-method, name, col_none);
+	}
+	else if (!method && compsize==plainsize)
 		externs->Printf("%8u      %s\n", (unsigned)plainsize, name);
 	else
 		externs->Printf("%8u %3u%% %s\n", (unsigned)plainsize, plainsize?(unsigned)((100*compsize)/plainsize):100u, name);
@@ -191,10 +200,13 @@ int qcc_wildcmp(const char *wild, const char *string)
 
 
 
-static const char *extractonly;
-static pbool extractonlyfound;
+static const char *extractonly;	//the file we're looking for
+static pbool extractonlyfound;	//for errors.
+static pbool extractecho;	//print the file to stdout instead of writing it.
 static void QCC_FileExtract(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize)
 {
+	if (method < 0)
+		return;	//QC_decode will fail. provided for enumeration reasons.
 	if (extractonly)
 	{
 		const char *sl = strrchr(extractonly, '/');
@@ -216,11 +228,19 @@ static void QCC_FileExtract(const char *name, const void *compdata, size_t comps
 		void *buffer = malloc(plainsize);
 		if (buffer && QC_decode(progfuncs, compsize, plainsize, method, compdata, buffer))
 		{
-			QCC_Mkdir(name);
-			if (!QCC_WriteFile(name, buffer, plainsize))
-				externs->Printf(" write failure\n");
+			if (extractecho)
+			{
+				externs->Printf("\n");
+				fwrite(buffer, 1, plainsize, stdout);
+			}
 			else
-				externs->Printf(" done\n");
+			{
+				QCC_Mkdir(name);
+				if (!QCC_WriteFile(name, buffer, plainsize))
+					externs->Printf(" write failure\n");
+				else
+					externs->Printf(" done\n");
+			}
 		}
 		else
 			externs->Printf(" read failure\n");
@@ -253,6 +273,7 @@ int main (int argc, const char **argv)
 	pbool writelog = false;	//other systems are sane.
 #endif
 	int colours = 2;	//auto
+	int ziparg = -1;
 	progexterns_t ext;
 	progfuncs_t funcs;
 	progfuncs = &funcs;
@@ -265,52 +286,6 @@ int main (int argc, const char **argv)
 	funcs.funcs.parms->Printf = logprintf;
 	funcs.funcs.parms->Sys_Error = Sys_Error;
 
-	if ((argc == 3 && !strcmp(argv[1], "-l")) || (argc >= 3 && !strcmp(argv[1], "-x")))
-	{
-		size_t blobsize;
-		void *blob = QCC_ReadFile(argv[2], NULL, NULL, &blobsize, false);
-		if (!blob)
-		{
-			logprintf("Unable to read %s\n", argv[2]);
-			return EXIT_FAILURE;
-		}
-		if (argc > 3)
-		{
-			for (i = 3; i < argc; i++)
-			{
-				extractonly = argv[i];
-				extractonlyfound = false;
-				QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);
-				if (!extractonlyfound)
-					externs->Printf("Unable to find file %s\n", extractonly);
-			}
-			extractonly = NULL;
-		}
-		else if (argv[1][1] == 'x')
-			QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);
-		else
-		{
-			QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileList);
-			externs->Printf("Total size %u bytes, %u files\n", (unsigned)totalsize, (unsigned)filecount);
-		}
-		free(blob);
-		return EXIT_SUCCESS;
-	}
-	if (argc == 3 && (!strncmp(argv[1], "-z", 2) || !strcmp(argv[1], "-0") || !strcmp(argv[1], "-9")))
-	{	//exe -0 foo.pk3dir
-		enum pkgtype_e t;
-		if (argv[1][1] == '9')
-			t = PACKAGER_PK3;
-		else if (argv[1][1] == '0')
-			t = PACKAGER_PAK;	//not really any difference but oh well
-		else
-			t = PACKAGER_PK3_SPANNED;
-
-		if (Packager_CompressDir(argv[2], t, QCC_PR_PackagerMessage, NULL))
-			return EXIT_SUCCESS;
-		else
-			return EXIT_FAILURE;
-	}
 	for (i = 0; i < argc; i++)
 	{
 		if (!argv[i])
@@ -327,7 +302,20 @@ int main (int argc, const char **argv)
 			colours = 0;
 		else if (!strcmp(argv[i], "--color=auto"))
 			colours = 2;
+		else if (!strcmp(argv[i], "-l") ||
+				 !strcmp(argv[i], "-x") ||
+				 !strcmp(argv[i], "-p") ||
+				 !strcmp(argv[i], "-z") ||
+				 !strcmp(argv[i], "-0") ||
+				 !strcmp(argv[i], "-9"))
+		{
+			ziparg = i;
+			break;	//other args are all filenames. don't misinterpret stuff.
+		}
 	}
+
+	for (i = 0; i < COL_MAX; i++)
+		qcccol[i] = "";
 #if defined(__linux__) || defined(__unix__)
 	if (colours == 2)
 		colours = isatty(STDOUT_FILENO);
@@ -346,6 +334,86 @@ int main (int argc, const char **argv)
 	(void)colours;
 #endif
 
+	if (ziparg >= 0)
+	{
+		if (ziparg+1 >= argc)
+		{
+			logprintf("archive name not specified\n");
+			return EXIT_FAILURE;
+		}
+		switch(argv[ziparg][1])
+		{
+		case 'l':	//list all files.
+			{
+				size_t blobsize;
+				void *blob = QCC_ReadFile(argv[ziparg+1], NULL, NULL, &blobsize, false);
+				if (blob)
+				{
+					QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileList);
+					externs->Printf("Total size %lu bytes, %u files\n", (unsigned long)totalsize, (unsigned)filecount);
+					free(blob);
+					return EXIT_SUCCESS;
+				}
+				logprintf("Unable to read %s\n", argv[ziparg+1]);
+			}
+			break;
+		case 'p':	//print (named) files to stdout.
+			extractecho = true;
+			//fall through
+		case 'x':	//extract (named) files to working directory.
+			{	//list/extract/view
+				size_t blobsize;
+				void *blob = QCC_ReadFile(argv[ziparg+1], NULL, NULL, &blobsize, false);
+				int ret = EXIT_FAILURE;
+				if (!blob)
+					logprintf("Unable to read %s\n", argv[ziparg+1]);
+				else if (ziparg+2 < argc)
+				{
+					for (i = ziparg+2; i < argc; i++)
+					{
+						extractonly = argv[i];
+						extractonlyfound = false;
+						QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);
+						if (!extractonlyfound)
+							externs->Printf("Unable to find file %s\n", extractonly);
+						else
+							ret = EXIT_SUCCESS;
+					}
+					extractonly = NULL;
+				}
+				else
+				{
+					QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);
+					ret = EXIT_SUCCESS;
+				}
+				free(blob);
+				return ret;
+			}
+		case 'z':	//fancy spanned stuff
+		case '0':	//store-only (pak)
+		case '9':	//best compression (pk3)
+
+			{	//exe -0 foo.pk3dir
+				enum pkgtype_e t;
+				if (argv[1][1] == '9')
+					t = PACKAGER_PK3;
+				else if (argv[1][1] == '0')
+					t = PACKAGER_PAK;	//not really any difference but oh well
+				else
+					t = PACKAGER_PK3_SPANNED;
+
+				if (Packager_CompressDir(argv[ziparg+1], t, QCC_PR_PackagerMessage, NULL))
+					return EXIT_SUCCESS;
+			}
+			break;
+		default:
+			//should be unreachable.
+			break;
+		}
+		return EXIT_FAILURE;
+	}
+
+
 	logfile = writelog?fopen("fteqcc.log", "wt"):false;
 
 	if (logfile)
diff --git a/engine/qclib/qcd_main.c b/engine/qclib/qcd_main.c
index ea8ea0ff3..5b731de01 100644
--- a/engine/qclib/qcd_main.c
+++ b/engine/qclib/qcd_main.c
@@ -41,6 +41,44 @@ pbool QC_decodeMethodSupported(int method)
 	return false;
 }
 
+
+#ifdef ZLIB_DEFLATE64
+#include "infback9.h"	//an obscure compile-your-own part of zlib.
+struct def64ctx
+{
+	const char *in;
+	char *out;
+	size_t csize;
+	size_t usize;
+
+	char window[65536];
+};
+static unsigned int QC_Deflate64_Grab(void *vctx, unsigned char **bufptr)
+{
+	struct def64ctx *ctx = vctx;
+
+	unsigned int avail = ctx->csize;
+	*bufptr = (unsigned char *)ctx->in;
+	ctx->csize = 0;
+	ctx->in += avail;
+
+	return avail;
+}
+static int QC_Deflate64_Spew(void *vctx, unsigned char *buf, unsigned int buflen)
+{
+	struct def64ctx *ctx = vctx;
+
+	if (buflen > ctx->usize)
+		return 1;	//over the size of our buffer...
+	memcpy(ctx->out, buf, buflen);
+	ctx->out += buflen;
+	ctx->usize -= buflen;
+	return 0;
+}
+#endif
+
+
+
 char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const void *info, char *buffer)
 {
 	int i;
@@ -87,6 +125,35 @@ char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const
 			externs->Sys_Error("Failed block decompression\n");
 		inflateEnd(&strm);
 	}
+#endif
+#ifdef ZLIB_DEFLATE64
+	else if (method == 9)
+	{
+		z_stream strm = {NULL};
+		struct def64ctx ctx;
+		ctx.in = info;
+		ctx.csize = complen;
+		ctx.out = buffer;
+		ctx.usize = len;
+
+		strm.data_type = Z_UNKNOWN;
+		inflateBack9Init(&strm, ctx.window);
+		//getting inflateBack9 to
+		if (Z_STREAM_END != inflateBack9(&strm, QC_Deflate64_Grab, &ctx, QC_Deflate64_Spew, &ctx))
+		{	//some stream error?
+			externs->Printf("Decompression error\n");
+			buffer = NULL;
+		}
+		else if (ctx.csize != 0 || ctx.usize != 0)
+		{	//corrupt file table?
+			externs->Printf("Decompression size error\n");
+			externs->Printf("read %i of %i bytes\n", (unsigned)ctx.csize, (unsigned)complen);
+			externs->Printf("wrote %i of %i bytes\n", (unsigned)ctx.usize, (unsigned)len);
+			buffer = NULL;
+		}
+		inflateBack9End(&strm);
+		return buffer;
+	}
 #endif
 	//add your decryption/decompression routine here.
 	else
@@ -191,8 +258,17 @@ int QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(cons
 	unsigned int cdlen;
 	const unsigned char *eocd;
 	const unsigned char *cd;
-	int nl,el,cl;
+	unsigned int ofs_le;
+	unsigned int cd_nl,cd_el,cd_cl;
 	int ret = 0;
+
+	const unsigned char *le;
+	unsigned int csize, usize, method;
+	unsigned int le_nl,le_el;
+	char name[256];
+
+	const void *data;
+
 	if (blobsize < 22)
 		return ret;
 	if (!strncmp(blob, "PACK", 4))
@@ -221,13 +297,20 @@ int QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(cons
 		return ret;
 
 
-	for(; cdentries --> 0; cd += 46 + nl+el+cl)
+	for(; cdentries --> 0 && (QC_ReadRawInt(cd+0) == 0x02014b50); cd += 46 + cd_nl+cd_el+cd_cl)
 	{
-		if (QC_ReadRawInt(cd+0) != 0x02014b50)
-			break;
-		nl = QC_ReadRawShort(cd+28);
-		el = QC_ReadRawShort(cd+30);
-		cl = QC_ReadRawShort(cd+32);
+		data = NULL, csize=usize=0, method=-1;
+
+		cd_nl = QC_ReadRawShort(cd+28);	//name length
+		cd_el = QC_ReadRawShort(cd+30);	//extras length
+		cd_cl = QC_ReadRawShort(cd+32);	//comment length
+
+		ofs_le = QC_ReadRawInt(cd+42);
+
+		if (cd_nl < sizeof(name))	//make can't be too long...
+			QC_strlcpy(name, cd+46, (cd_nl+1<sizeof(name))?cd_nl+1:sizeof(name));
+		else
+			QC_strlcpy(name, "?", sizeof(name));
 
 		//1=encrypted
 		//2,4=encoder flags
@@ -240,35 +323,41 @@ int QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(cons
 		//1000=enh comp
 		//2000=masked localheader
 		//4000,8000=reserved
-		if (QC_ReadRawShort(cd+8) & ~0x80e)
-			continue;
-
+		if (!(QC_ReadRawShort(cd+8) & ~0x80e))	//only accept known cd general purpose flags
 		{
-			const unsigned char *le = (const unsigned char*)blob + QC_ReadRawInt(cd+42);
-			unsigned int csize, usize, method;
-			char name[256];
+			if (ofs_le+46 < blobsize)
+			{
+				le = (const unsigned char*)blob + QC_ReadRawInt(cd+42);
 
-			if (QC_ReadRawInt(le+0) != 0x04034b50)
-				continue;
-			if (QC_ReadRawShort(le+6) & ~0x80e)	//general purpose flags
-				continue;
-			method = QC_ReadRawShort(le+8);
-			if (method != 0 && method != 8)
-				continue;
-			if (nl != QC_ReadRawShort(le+26))
-				continue;	//name is weird...
-//			if (el != QC_ReadRawShort(le+28))
-//				continue;	//extradata is weird...
+				if (QC_ReadRawInt(le+0) == 0x04034b50)	//needs proper local entry tag
+				if (!(QC_ReadRawShort(le+6) & ~0x80e))	//ignore unsupported general purpose flags
+				{
+					le_nl = QC_ReadRawShort(le+26);
+					le_el = QC_ReadRawShort(le+28);
+					if (cd_nl == le_nl)	//name (length) must match...
+//					if (cd_el != le_el) //extras does NOT match
+					{
+						csize = QC_ReadRawInt(le+18);
+						usize = QC_ReadRawInt(le+22);
 
-			csize = QC_ReadRawInt(le+18);
-			usize = QC_ReadRawInt(le+22);
-			if (nl >= sizeof(name))
-				continue;	//name is too long
-			QC_strlcpy(name, cd+46, (nl+1<sizeof(name))?nl+1:sizeof(name));
+						data = le+30+le_nl+le_el;
 
-			cb(name, le+30+QC_ReadRawShort(le+26)+QC_ReadRawShort(le+28), csize, method, usize);
-			ret++;
+						method = QC_ReadRawShort(le+8);
+						if (method != 0
+#ifdef AVAIL_ZLIB
+							&& method != 8
+#endif
+#ifdef ZLIB_DEFLATE64
+							&& method != 9
+#endif
+						 )
+							method=-1-method;
+					}
+				}
+			}
 		}
+		cb(name, data, csize, method, usize);
+		ret++;
 	}
 	return ret;
 }