/* 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/bzip2.h>

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

BOOL BZip2_recog(struct xfdMasterIFace *Self, UBYTE *buf, ULONG length,
	struct xfdRecogResult *rr)
{
	if (buf[0] == 'B' && buf[1] == 'Z' &&
		(buf[2] == 'h' || buf[2] == '0') &&
		(buf[3] >= '1' && buf[3] <= '9'))
	{
		return TRUE;
	}
	return FALSE;
}

BOOL BZip2_decrunch(struct xfdMasterIFace *Self, struct xfdBufferInfo *xbi) {
	struct xfdMasterBase *libBase = (struct xfdMasterBase *)Self->Data.LibBase;
	struct ExecIFace *IExec = libBase->xfdm_IExec;
	struct Library *BZip2Base;
	struct BZip2IFace *IBZip2;
	UBYTE *src = xbi->xfdbi_SourceBuffer, *dst;
	ULONG srclen = xbi->xfdbi_SourceBufLen, dstlen;
	BOOL res = FALSE;
	bz_stream stream;
	int err;

	BZip2Base = IExec->OpenLibrary("bzip2.library", 53);
	IBZip2 = (struct BZip2IFace *)IExec->GetInterface(BZip2Base, "main", 1, NULL);
	if (IBZip2 == NULL) {
		xbi->xfdbi_Error = XFDERR_MISSINGRESOURCE;
		goto Error;
	}

	dstlen = srclen;
	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 = (char *)src;
	stream.avail_in = srclen;
	stream.next_out = (char *)dst;
	stream.avail_out = dstlen;
	stream.bzalloc = NULL;
	stream.bzfree = NULL;

	err = IBZip2->BZ2_bzDecompressInit(&stream, 0, 0);
	if (err == BZ_OK) {
		while ((err = IBZip2->BZ2_bzDecompress(&stream)) != BZ_STREAM_END) {
			if (err != BZ_OK || stream.avail_in == 0) {
				break;
			}

			if (stream.avail_out == 0) {
				UBYTE *newdst;
				ULONG newdstlen = dstlen + (srclen >> 1);
				newdst = IExec->AllocMem(newdstlen, xbi->xfdbi_TargetBufMemType);
				if (newdst == NULL) {
					err = BZ_MEM_ERROR;
					break;
				}

				IExec->CopyMem(dst, newdst, dstlen);
				IExec->FreeMem(dst, dstlen);

				stream.next_out = (char *)newdst + dstlen;
				stream.avail_out = newdstlen - dstlen;

				xbi->xfdbi_TargetBuffer = dst = newdst;
				xbi->xfdbi_TargetBufSaveLen =
				xbi->xfdbi_TargetBufLen = dstlen = newdstlen;
			}
		}
		IBZip2->BZ2_bzDecompressEnd(&stream);
	}

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

Error:
	IExec->DropInterface((struct Interface *)IBZip2);
	IExec->CloseLibrary(BZip2Base);

	return res;
}

struct xfdSlave FirstSlave = {
	NULL, XFDS_VERSION, 39, "BZip2", XFDPFF_DATA, 0,
	BZip2_recog, BZip2_decrunch, NULL, NULL, 0, 0, 4
};

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

int _start(void) {
	return RETURN_FAIL;
}

