/* OpenAmiga.org
 *  This file is part of the OpenAmiga "xfdmaster library project"
 *
 * For more information look at:
 *   http://openamiga.org/?function=viewproject&projectid=67
 */

#define __NOLIBBASE__
#define __NOGLOBALIFACE__

#include <proto/xfdmaster.h>
#include <proto/exec.h>
#include <proto/z.h>

static const char USED version[]="\0$VER: GZip 53.2 (1.9.2014)";

#define LE32GET(a)      ((((a)[3])<<24)|(((a)[2])<<16)|(((a)[1])<<8)|((a)[0]))
#define GETLONG_LE(x,n) LE32GET(&x[n])

#ifndef MAX_WBITS /* Not defined in <libraries/z.h> */
#define MAX_WBITS 15
#endif

BOOL GZip_recog(struct xfdMasterIFace *Self, UBYTE *buf, ULONG length,
	struct xfdRecogResult *rr)
{
	if (buf[0] == 0x1f && (buf[1] == 0x8b || buf[1] == 0x9e) &&
		buf[2] == 8 && (buf[3] & 0xe2) == 0)
	{
		return TRUE;
	}
	return FALSE;
}

BOOL GZip_decrunch(struct xfdMasterIFace *Self, struct xfdBufferInfo *xbi) {
	struct xfdMasterBase *libBase = (struct xfdMasterBase *)Self->Data.LibBase;
	struct ExecIFace *IExec = libBase->xfdm_IExec;
	struct Library *ZBase;
	struct ZIFace *IZ;
	UBYTE *src = xbi->xfdbi_SourceBuffer, *dst;
	ULONG srclen = xbi->xfdbi_SourceBufLen, dstlen;
	BOOL res = FALSE;
	z_stream stream;
	int err;

	ZBase = IExec->OpenLibrary("z.library", 53);
	IZ = (struct ZIFace *)IExec->GetInterface(ZBase, "main", 1, NULL);
	if (IZ == NULL) {
		xbi->xfdbi_Error = XFDERR_MISSINGRESOURCE;
		goto Error;
	}

	dstlen = GETLONG_LE(src, srclen-4);
	dst = IExec->AllocMem(dstlen, xbi->xfdbi_TargetBufMemType);
	if (dst == NULL) {
		xbi->xfdbi_Error = XFDERR_NOMEMORY;
		goto Error;
	}

	xbi->xfdbi_TargetBuffer = dst;
	xbi->xfdbi_TargetBufSaveLen =
	xbi->xfdbi_TargetBufLen = dstlen;

	stream.next_in = src;
	stream.avail_in = srclen;
	stream.next_out = dst;
	stream.avail_out = dstlen;
	stream.zalloc = (alloc_func)NULL;
	stream.zfree = (free_func)NULL;

	err = IZ->InflateInit2(&stream, 16+MAX_WBITS);
	if (err == Z_OK) {
		err = IZ->Inflate(&stream, Z_FINISH);
		IZ->InflateEnd(&stream);
	}

	if (err == Z_STREAM_END) {
		xbi->xfdbi_TargetBufSaveLen = stream.total_out;
		res = TRUE;
	} else {
		IExec->FreeMem(dst, dstlen);
		xbi->xfdbi_TargetBuffer = NULL;
		xbi->xfdbi_TargetBufSaveLen =
		xbi->xfdbi_TargetBufLen = 0;
		if (err == Z_MEM_ERROR) {
			xbi->xfdbi_Error = XFDERR_NOMEMORY;
		} else {
			xbi->xfdbi_Error = XFDERR_CORRUPTEDDATA;
		}
	}

Error:
	IExec->DropInterface((struct Interface *)IZ);
	IExec->CloseLibrary(ZBase);

	return res;
}

struct xfdSlave FirstSlave = {
	NULL, XFDS_VERSION, 39, "GZip", XFDPFF_DATA, 0,
	GZip_recog, GZip_decrunch, NULL, NULL, 0, 0, 4
};

struct xfdForeMan xfdForeMan = {
	0x70FF4E75,
	XFDF_ID,
	XFDF_VERSION,
	0,
	NULL,
	ZERO,
	&FirstSlave
};

int _start(void) {
	return RETURN_FAIL;
}

