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

/****************************************************************************
*   XFD library system for cruncher handling
*   Copyright (C) 1994-1998 by Georg Hörmann
*   Copyright (C) 1998 by Dirk Stöcker <soft@dstoecker.de>
*   Copyright (C) 2014 by Fredrik Wikström <fredrik@a500.org>
*
*   This library is free software; you can redistribute it and/or
*   modify it under the terms of the GNU Lesser General Public
*   License as published by the Free Software Foundation; either
*   version 2.1 of the License, or (at your option) any later version.
*
*   This library is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*   Lesser General Public License for more details.
*
*   You should have received a copy of the GNU Lesser General Public
*   License along with this library; if not, write to the Free Software
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
****************************************************************************/

#include <interfaces/xfdmaster.h>
#include "../xfdmaster_vectors.h"
#include "../xfdmaster_internal.h"

/****** xfdmaster.library/xfdDecrunchBuffer ******************************************
*
*   NAME
*      xfdDecrunchBuffer -- Description
*
*   SYNOPSIS
*      BOOL xfdDecrunchBuffer(struct xfdBufferInfo * bufferinfo);
*
*   FUNCTION
*
*   INPUTS
*       bufferinfo - 
*
*   RESULT
*       The result ...
*
*   EXAMPLE
*
*   NOTES
*
*   BUGS
*
*   SEE ALSO
*
*****************************************************************************
*
*/

BOOL _xfdMaster_xfdDecrunchBuffer(struct xfdMasterIFace *Self,
	struct xfdBufferInfo *bufferinfo)
{
	DEBUGF("xfdDecrunchBuffer(0x%08lx, 0x%08lx)\n", Self, bufferinfo);
	struct xfdMasterBase *libBase = (struct xfdMasterBase *)Self->Data.LibBase;
	struct ExecIFace *IExec = libBase->xfdm_IExec;
	struct xfdSlave *slave;
	ULONG res;

	if ((bufferinfo->xfdbi_PackerFlags & (XFDPFF_DATA|XFDPFF_ADDR)) == 0) {
		UWORD error;
		error = Self->xfdTestHunkStructureNew(
			bufferinfo->xfdbi_SourceBuffer,
			bufferinfo->xfdbi_SourceBufLen);
		if (error != XFDERR_OK) {
			bufferinfo->xfdbi_Error = error;
			return FALSE;
		}
	}

	if (bufferinfo->xfdbi_SourceBufLen < bufferinfo->xfdbi_MinSourceLen) {
		bufferinfo->xfdbi_Error = XFDERR_BUFFERTRUNCATED;
		return FALSE;
	}

	if ((slave = bufferinfo->xfdbi_Slave) == NULL) {
		bufferinfo->xfdbi_Error = XFDERR_NOSLAVE;
		return FALSE;
	}

	if ((bufferinfo->xfdbi_Flags & XFDPFF_USERTARGET) != 0) {
		if ((slave->xfds_PackerFlags & XFDPFF_USERTARGET) == 0) {
			bufferinfo->xfdbi_Error = XFDERR_TARGETNOTSUPPORTED;
			return FALSE;
		}

		if (bufferinfo->xfdbi_UserTargetBuf == NULL) {
			bufferinfo->xfdbi_Error = XFDERR_NOTARGET;
			return FALSE;
		}

		if (bufferinfo->xfdbi_UserTargetBufLen < bufferinfo->xfdbi_MinTargetLen) {
			bufferinfo->xfdbi_Error = XFDERR_TARGETTOOSMALL;
			return FALSE;
		}

		bufferinfo->xfdbi_TargetBuffer = bufferinfo->xfdbi_UserTargetBuf;
		bufferinfo->xfdbi_TargetBufLen = bufferinfo->xfdbi_UserTargetBufLen;
		bufferinfo->xfdbi_TargetBufSaveLen = bufferinfo->xfdbi_FinalTargetLen;
	} else {
		if ((slave->xfds_PackerFlags & XFDPFF_USERTARGET) != 0 &&
			bufferinfo->xfdbi_MinTargetLen != (ULONG)-1)
		{
			APTR buffer;

			buffer = IExec->AllocMem(bufferinfo->xfdbi_MinTargetLen, bufferinfo->xfdbi_TargetBufMemType);
			if (buffer == NULL) {
				bufferinfo->xfdbi_Error = XFDERR_NOMEMORY;
				goto Error;
			}

			bufferinfo->xfdbi_UserTargetBuf =
			bufferinfo->xfdbi_TargetBuffer = buffer;
			bufferinfo->xfdbi_Flags |= XFDFF_USERTARGET|XFDFF_MASTERALLOC;
			bufferinfo->xfdbi_UserTargetBufLen =
			bufferinfo->xfdbi_TargetBufLen = bufferinfo->xfdbi_MinTargetLen;
			bufferinfo->xfdbi_TargetBufSaveLen = bufferinfo->xfdbi_FinalTargetLen;
		}
	}

	if (slave->xfds_DecrunchBuffer == NULL) {
		bufferinfo->xfdbi_Error = XFDERR_NOTSUPPORTED;
		return FALSE;
	}

	if (IExec->IsNative(slave->xfds_DecrunchBuffer)) {
		xfdDecrunchBufferFunc func = (xfdDecrunchBufferFunc)slave->xfds_DecrunchBuffer;
		res = func(Self, bufferinfo);
	} else {
		res = IExec->EmulateTags(slave->xfds_DecrunchBuffer,
			ET_SaveParamRegs, TRUE,
			ET_RegisterA0,    bufferinfo,
			ET_RegisterA6,    libBase,
			TAG_END);
	}
	if (res) {
		bufferinfo->xfdbi_Error = XFDERR_OK;
		return TRUE;
	}

Error:
	if ((bufferinfo->xfdbi_Flags & XFDFF_MASTERALLOC) != 0) {
		IExec->FreeMem(bufferinfo->xfdbi_UserTargetBuf, bufferinfo->xfdbi_UserTargetBufLen);

		bufferinfo->xfdbi_Flags ^= XFDFF_USERTARGET|XFDFF_MASTERALLOC;
		bufferinfo->xfdbi_UserTargetBufLen = 0;
		bufferinfo->xfdbi_UserTargetBuf = NULL;
	}

	bufferinfo->xfdbi_TargetBuffer = NULL;
	bufferinfo->xfdbi_TargetBufLen = 0;
	bufferinfo->xfdbi_TargetBufSaveLen = 0;
	bufferinfo->xfdbi_DecrAddress = 0;
	bufferinfo->xfdbi_JmpAddress = 0;
	return FALSE;
}

