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

/****************************************************************************
*   XPK decrunching slave
*
*   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 Wikstrom <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 "../xfdmaster_internal.h"
#include <proto/xfdmaster.h>
#include <proto/xpkmaster.h>

extern struct xfdSlave S_XPKpacked;
extern struct xfdSlave S_XPKcrypted;

static BOOL SD_XPKpacked(
	struct xfdMasterIFace *Self,
	CONST_APTR buffer,
	ULONG length)
{
	if (READ_ULONG(buffer, 0) == 'XPKF' &&
		(READ_UBYTE(buffer, 0x20) & 2) == 0)
	{
		return TRUE;
	}
	return FALSE;
}

static BOOL RB_XPKpacked(
	struct xfdMasterIFace *Self,
	CONST_APTR buffer,
	ULONG length,
	struct xfdRecogResult *rr)
{
	if (SD_XPKpacked(Self, buffer, length)) {
		rr->xfdrr_FinalTargetLen = READ_ULONG(buffer, 0xC);
		rr->xfdrr_MinTargetLen = rr->xfdrr_FinalTargetLen + XPK_MARGIN;
		rr->xfdrr_MinSourceLen = READ_ULONG(buffer, 4) + 8;
		return TRUE;
	}
	return FALSE;
}

static BOOL SD_XPKcrypted(
	struct xfdMasterIFace *Self,
	CONST_APTR buffer,
	ULONG length)
{
	if (READ_ULONG(buffer, 0) == 'XPKF' &&
		(READ_UBYTE(buffer, 0x20) & 2) != 0)
	{
		return TRUE;
	}
	return FALSE;
}

static BOOL RB_XPKcrypted(
	struct xfdMasterIFace *Self,
	CONST_APTR buffer,
	ULONG length,
	struct xfdRecogResult *rr)
{
	if (SD_XPKcrypted(Self, buffer, length)) {
		rr->xfdrr_FinalTargetLen = READ_ULONG(buffer, 0xC);
		rr->xfdrr_MinTargetLen = rr->xfdrr_FinalTargetLen + XPK_MARGIN;
		rr->xfdrr_MinSourceLen = READ_ULONG(buffer, 4) + 8;
		return TRUE;
	}
	return FALSE;
}

static ULONG VD_XPK(
	struct xfdMasterIFace *Self,
	CONST_APTR buffer,
	ULONG length)
{
	const UBYTE *bufptr = buffer + 8;
	int i;
	ULONG packedsize;
	for (i = 0; i < 4; i++) {
		if (*bufptr < 0x30 || *bufptr > 0x5a) {
			return FALSE;
		}
		bufptr++;
	}
	packedsize = READ_ULONG(buffer, 4) + 8;
	if (packedsize > length) {
		return FALSE;
	}
	return packedsize;
}

static const UWORD ErrorTable[] = {
	XFDERR_NOTSUPPORTED,	//XPKERR_NOFUNC
	XFDERR_NOTSUPPORTED,	//XPKERR_NOFILES
	XFDERR_NOTSUPPORTED,	//XPKERR_IOERRIN
	XFDERR_NOTSUPPORTED,	//XPKERR_IOERROUT
	XFDERR_CORRUPTEDDATA,	//XPKERR_CHECKSUM
	XFDERR_NOTSUPPORTED,	//XPKERR_VERSION
	XFDERR_NOMEMORY,		//XPKERR_NOMEM
	XFDERR_NOTSUPPORTED,	//XPKERR_LIBINUSE
	XFDERR_NOTSUPPORTED,	//XPKERR_WRONGFORM
	XFDERR_NOMEMORY,		//XPKERR_SMALLBUF
	XFDERR_NOTSUPPORTED,	//XPKERR_LARGEBUF
	XFDERR_NOTSUPPORTED,	//XPKERR_WRONGMODE
	XFDERR_WRONGPASSWORD,	//XPKERR_NEEDPASSWD
	XFDERR_CORRUPTEDDATA,	//XPKERR_CORRUPTPKD
	XFDERR_MISSINGRESOURCE,	//XPKERR_MISSINGLIB
	XFDERR_NOTSUPPORTED,	//XPKERR_BADPARAMS
	XFDERR_NOTSUPPORTED,	//XPKERR_EXPANSION
	XFDERR_NOTSUPPORTED,	//XPKERR_NOMETHOD
	XFDERR_WRONGPASSWORD,	//XPKERR_ABORTED
	XFDERR_BUFFERTRUNCATED,	//XPKERR_TRUNCATED
	XFDERR_BETTERCPU,		//XPKERR_WRONGCPU
	XFDERR_NOTSUPPORTED,	//XPKERR_PACKED
	XFDERR_NOTSUPPORTED,	//XPKERR_NOTPACKED
	XFDERR_NOTSUPPORTED,	//XPKERR_FILEEXISTS
	XFDERR_NOTSUPPORTED,	//XPKERR_OLDMASTLIB
	XFDERR_NOTSUPPORTED,	//XPKERR_OLDSUBLIB
	XFDERR_NOTSUPPORTED,	//XPKERR_NOCRYPT
	XFDERR_NOTSUPPORTED,	//XPKERR_NOINFO
	XFDERR_NOTSUPPORTED,	//XPKERR_LOSSY
	XFDERR_NOTSUPPORTED,	//XPKERR_NOHARDWARE
	XFDERR_NOTSUPPORTED,	//XPKERR_BADHARDWARE
	XFDERR_WRONGPASSWORD,	//XPKERR_WRONGPW
	XFDERR_NOTSUPPORTED,	//XPKERR_UNKNOWN
	XFDERR_WRONGPASSWORD	//XPKERR_REQTIMEOUT
};

static BOOL DB_XPK(
	struct xfdMasterIFace *Self,
	struct xfdBufferInfo *xbi)
{
	struct xfdMasterBase *libBase = (struct xfdMasterBase *)Self->Data.LibBase;
	struct ExecIFace *IExec = libBase->xfdm_IExec;
	struct Library *XpkBase = NULL;
	struct XpkIFace *IXpk = NULL;
	BOOL password;
	LONG error;

	XpkBase = IExec->OpenLibrary("xpkmaster.library", 0);
	IXpk = (struct XpkIFace *)IExec->GetInterface(XpkBase, "main", 1, NULL);
	if (IXpk == NULL) {
		IExec->CloseLibrary(XpkBase);
		xbi->xfdbi_Error = XFDERR_MISSINGRESOURCE;
		return FALSE;
	}

	password = (*(UBYTE *)(xbi->xfdbi_SourceBuffer + 0x20) & 2) ? TRUE : FALSE;

	error = IXpk->XpkUnpackTags(
		XPK_InBuf,     xbi->xfdbi_SourceBuffer,
		XPK_InLen,     xbi->xfdbi_SourceBufLen,
		XPK_OutBuf,    xbi->xfdbi_UserTargetBuf,
		XPK_OutBufLen, xbi->xfdbi_TargetBufLen,
		password ? XPK_Password : TAG_IGNORE, xbi->xfdbi_Special,
		TAG_END);
	if (error != XPKERR_OK) {
		if (error < 0 && error >= XPKERR_REQTIMEOUT)
			error = ErrorTable[~error];
		else
			error = XFDERR_NOTSUPPORTED;
		xbi->xfdbi_Error = error;
	}

	IExec->DropInterface((struct Interface *)IXpk);
	IExec->CloseLibrary(XpkBase);
	return (error == XFDERR_OK) ? TRUE : FALSE;
}

struct xfdSlave S_XPKpacked = {
	&S_XPKcrypted,
	2,
	39,
	"XPK Packed",
	XFDPFF_DATA|XFDPFF_RECOGLEN|XFDPFF_USERTARGET,
	0,
	(APTR)RB_XPKpacked,
	(APTR)DB_XPK,
	(APTR)SD_XPKpacked,
	(APTR)VD_XPK,
	XFDID_XPKPacked,
	0,
	0x24+16
};

struct xfdSlave S_XPKcrypted = {
	NULL,
	2,
	39,
	"XPK Crypted",
	XFDPFF_DATA|XFDPFF_PASSWORD|XFDPFF_RECOGLEN|XFDPFF_USERTARGET,
	-1,
	(APTR)RB_XPKcrypted,
	(APTR)DB_XPK,
	(APTR)SD_XPKcrypted,
	(APTR)VD_XPK,
	XFDID_XPKCrypted,
	0,
	0x24+16
};

