/*
**	dispatch.c - dispatcher for XPK DataType class
**	Copyright © 1995 Michael Letowski
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/dosasl.h>
#include <intuition/classes.h>
#include <datatypes/datatypesclass.h>
#include <datatypes/datatypes.h>
#include <libraries/xpk.h>
#include <utility/tagitem.h>
#include <support/types.h>
#include <support/dos.h>

#include <string.h>

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/datatypes.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#include <clib/support_protos.h>

#include "classbase.h"
#include "dispatch.h"


/****** xpk.datatype/xpk.datatype *******************************************
*
*   NAME
*       xpk.datatype -- data type for XPK and PowerPacker compressed files.
*
*   FUNCTION
*       The xpk data type is used to decompress XPK and PowerPacker
*       compressed files. Decompressed files are then placed in "T:" logical
*       directory and passed again to DataTypes system via NewDTObject().
*       DataTypes system is then free to handle them (it will usually show
*       contents of a decompressed file).
*
*   METHODS
*       OM_NEW -- Decompress source file, place it in "T:" directory and call
*                 OM_NEW method on that file.
*
*       OM_DISPOSE -- Delete the object and all associated temporary files.
*
*   NOTES
*       Since the data type just calls OM_NEW method on a decompressed file
*       it will work for files which were compressed several times (eg. DLTA
*       encoded and SHRI compressed).
*
*   BUGS
*       Will not work for AmigaGuide® compressed files (due to a bug in
*       amigaguide.datatype, I suspect).
*       Can not handle encrypted files.
*
*   SEE ALSO
*       amigaguide.datatype, xpkmaster.library, datatypes.library.
*
*****************************************************************************
*
*/


/*
**	Private structures
*/
struct XPKData
{
	Object *xd_Object;
	STRPTR  xd_Name;
};	/* XPKData */


/*
**	Private prototypes
*/
STATIC ASM Object *Dispatch(R_A0 Class *cl, R_A2 Object *o, R_A1 Msg msg);
STATIC Object *NewXPK(Class *cl, Object *o, Msg msg);
STATIC Object *DisposeXPK(Class *cl, Object *o, Msg msg);
STATIC Object *DefaultXPK(Class *cl, Object *o, Msg msg);
STATIC STRPTR CreateTempFile(struct ClassBase *cb, STRPTR name);
STATIC VOID DeleteTempFile(struct ClassBase *cb, STRPTR name);
STATIC LONG XPK2DOS(struct ClassBase *cb, LONG xpkError);


/*
**	DataTypes stubs
*/
STATIC ULONG LocSetDTAttrs(struct ClassBase *cb, Object *o, ULONG data, ...);
STATIC ULONG LocGetDTAttrs(struct ClassBase *cb, Object *o, ULONG data, ...);


/*
**	xpk.h kludge
*/
#pragma tagcall XpkBase XpkUnpackTags 30 801


/*
**	Public functions
*/
Class *InitClass(struct ClassBase *cb)
{
	Class *CL;

	/* Create our class (an instance of XPKData) */
	if(CL=MakeClass(DATATYPENAME,SUPERCLASSNAME,NULL,sizeof(struct XPKData),0))
	{
		CL->cl_Dispatcher.h_Entry=(HOOKFUNC)Dispatch;
		CL->cl_UserData=(ULONG)cb;
		AddClass(CL);
	}
	return(CL);
}	/* InitClass */


/*
**	Level 1 private functions
*/
STATIC ASM Object *Dispatch(R_A0 Class *cl, R_A2 Object *o, R_A1 Msg msg)
{
	Object *Obj;

	switch(msg->MethodID)
	{
		case OM_NEW:			Obj=NewXPK(cl,o,msg);				break;
		case OM_DISPOSE:	Obj=DisposeXPK(cl,o,msg);		break;
		default:					Obj=DefaultXPK(cl,o,msg);		break;
	}
	return(Obj);
}	/* Dispatch */


/*
**	Level 2 private functions
*/
STATIC Object *NewXPK(Class *cl, Object *o, Msg msg)
{
	STATIC CONST struct TagItem UnpackTags[]=
	{
		{XPK_PassThru,	TRUE},
		{TAG_DONE,			0}
	};	/* UnpackTags */
	STATIC CONST Tag FilterTags[]=
	{
		DTA_SourceType,DTA_DataType,DTA_Name,DTA_Handle,TAG_DONE
	};	/* FilterTags */

	struct ClassBase *cb=(struct ClassBase *)cl->cl_UserData;
	struct TagItem *Clone,*Tag;
	struct TagItem *Attrs=((struct opSet *)msg)->ops_AttrList;
	Object *NewObj=NULL;
	struct XPKData *XD;
	STRPTR Name,RealName;
	BPTR Lock,Lock2,Handle;
	LONG Err,RC=0;

	try(Clone=CloneTagItems(Attrs),	NO_CLONE);

	/* Get name and object lock */
	RealName=	(STRPTR)GetTagData(DTA_Name,NULL,Clone);
	Lock=			(BPTR)GetTagData(DTA_Handle,NULL,Clone);
	try(RealName && Lock, NO_LOCK);

	/* Remove unnecessary attributes */
	FilterTagItems(Clone,FilterTags,TAGFILTER_NOT);

	if(Name=CreateTempFile(cb,RealName))					/* Create temporary file name */
	{
		Err=XPKERR_IOERRIN;													/* No success yet */
		if(Lock2=DupLock(Lock))											/* Get duplicate of lock */
			if(Handle=OpenFromLock(Lock2))						/* Open from that lock */
			{																					/* Could open - Lock2 is invalid */
				/* Do unpacking */
				Err=XpkUnpackTags(XPK_InFH,			Handle,
													XPK_OutName,	Name,
													TAG_MORE,			UnpackTags);
				Close(Handle);													/* Close file */
			}
			else																			/* Could not open */
				UnLock(Lock2);													/* Unlock file */
			
		if(Err==XPKERR_OK || Err==XPKERR_NOTPACKED)	/* Error? */
			/* Create new object */
			if(NewObj=(Object *)DoSuperMethodA(cl,o,msg))
			{
				XD=INST_DATA(cl,NewObj);
				if(XD->xd_Object=NewDTObjectA(Name,Clone))
				{
					SetAttrs(NewObj,DTA_ObjName,RealName,TAG_DONE);
					XD->xd_Name=Name;
				}
				else																		/* Could not create object */
				{
					RC=IoErr();														/* Get DOS error code */
					CoerceMethod(cl,NewObj,OM_DISPOSE);		/* Destroy object (super class) */
					NewObj=NULL;													/* Mark this fact */
				}
			}
			else																			/* Error creating object */
				RC=IoErr();															/* Get DOS error code */
		else																				/* Error unpacking */
			RC=XPK2DOS(cb,Err);												/* Get DOS error code */
	}
	else																					/* Error creating temp file */
		RC=IoErr();																	/* Get DOS error code */

	unless(NewObj)																/* New object not created */
		DeleteTempFile(cb,Name);										/* Delete temporary file */
 	
	/* Exceptions */
	SetIoErr(RC);																	/* Set error code */
	catch(NO_LOCK,	);
	catch(NO_CLONE,	FreeTagItems(Clone));
	return(NewObj);
}	/* NewXPK */

STATIC Object *DisposeXPK(Class *cl, Object *o, Msg msg)
{
	struct ClassBase *cb=(struct ClassBase *)cl->cl_UserData;
	struct XPKData *XD=INST_DATA(cl,o);

	if(XD->xd_Object)	DisposeDTObject(XD->xd_Object);
	if(XD->xd_Name)		DeleteTempFile(cb,XD->xd_Name);
	return((Object *)DoSuperMethodA(cl,o,msg));
}	/* DisposeXPK */

STATIC Object *DefaultXPK(Class *cl, Object *o, Msg msg)
{
	struct XPKData *XD=INST_DATA(cl,o);

	return((Object *)DoMethodA(XD->xd_Object,msg));
}	/* DefaultXPK */


/*
**	Level 3 private functions
*/
STATIC STRPTR CreateTempFile(struct ClassBase *cb, STRPTR name)
{
	STRPTR Name,RealName=FilePart(name);
	ULONG Len;
	BPTR DirLock;

	Len=strlen(RealName)+24;
	if(Name=AllocVec(Len,MEMF_PUBLIC))						/* Allocate space for name */
	{																							/* Space allocated */
		SPrintf(Name,"T:XPKDT-%08lx-T%04ld",ThisProcessS,cb->cb_UniqueCnt++);
		if(DirLock=CreateDir(Name))									/* Try to create directory */
		{																						/* Could create directory */
			AddPart(Name,RealName,Len);								/* Append file name */
			UnLock(DirLock);													/* Free lock */
		}
		else																				/* Dir not created */
		{																						/* Create unique file name */
			strcat(Name,"-");													/* Append dash */
			strcat(Name,RealName);										/* Append file name */
		}
	}
	return(Name);
}	/* CreateTempFile */

STATIC VOID DeleteTempFile(struct ClassBase *cb, STRPTR name)
{
	STRPTR Sep;

	if(name)																			/* Name valid */
	{
		Sep=PathPart(name);													/* Find path/file separator */
		DeleteFile(name);														/* Delete file */
		if(Sep!=FilePart(name))											/* Dir component present */
		{
			*Sep='\0';																/* Null terminate string */
			DeleteFile(name);													/* Delete directory */
		}
		FreeVec(name);															/* Free space */
	}
}	/* DeleteTempFile */

STATIC LONG XPK2DOS(struct ClassBase *cb, LONG xpkErr)
{
	LONG DOSErr=ThisProcessS->pr_Result2;

	unless(DOSErr)																/* Error not set */
		switch(xpkErr)
		{
			case XPKERR_NOFUNC:				DOSErr=ERROR_NOT_IMPLEMENTED;						break;
			case XPKERR_NOFILES:			DOSErr=ERROR_NOT_IMPLEMENTED;						break;
			case XPKERR_IOERRIN:			DOSErr=ERROR_OBJECT_WRONG_TYPE;					break;
			case XPKERR_IOERROUT:			DOSErr=ERROR_OBJECT_WRONG_TYPE;					break;
			case XPKERR_CHECKSUM:			DOSErr=DTERROR_INVALID_DATA;						break;
			case XPKERR_VERSION:			DOSErr=ERROR_INVALID_RESIDENT_LIBRARY;	break;
			case XPKERR_NOMEM:				DOSErr=ERROR_NO_FREE_STORE;							break;
			case XPKERR_LIBINUSE:			DOSErr=ERROR_INVALID_RESIDENT_LIBRARY;	break;
			case XPKERR_WRONGFORM:		DOSErr=DTERROR_UNKNOWN_COMPRESSION;			break;
			case XPKERR_SMALLBUF:			DOSErr=ERROR_BUFFER_OVERFLOW;						break;
			case XPKERR_LARGEBUF:			DOSErr=ERROR_BUFFER_OVERFLOW;						break;
			case XPKERR_WRONGMODE:		DOSErr=DTERROR_UNKNOWN_COMPRESSION;			break;
			case XPKERR_NEEDPASSWD:		DOSErr=ERROR_OBJECT_WRONG_TYPE;					break;
			case XPKERR_CORRUPTPKD:		DOSErr=DTERROR_INVALID_DATA;						break;
			case XPKERR_MISSINGLIB:		DOSErr=ERROR_INVALID_RESIDENT_LIBRARY;	break;
			case XPKERR_BADPARAMS:		DOSErr=ERROR_TOO_MANY_ARGS;							break;
			case XPKERR_EXPANSION:		DOSErr=ERROR_BUFFER_OVERFLOW;						break;
			case XPKERR_NOMETHOD:			DOSErr=ERROR_INVALID_RESIDENT_LIBRARY;	break;
			case XPKERR_ABORTED:			DOSErr=ERROR_BREAK;											break;
			case XPKERR_TRUNCATED:		DOSErr=DTERROR_NOT_ENOUGH_DATA;					break;
			case XPKERR_WRONGCPU:			DOSErr=ERROR_INVALID_RESIDENT_LIBRARY;	break;
			case XPKERR_PACKED:				DOSErr=ERROR_OBJECT_WRONG_TYPE;					break;
			case XPKERR_NOTPACKED:		DOSErr=ERROR_OBJECT_WRONG_TYPE;					break;
			case XPKERR_FILEEXISTS:		DOSErr=ERROR_OBJECT_EXISTS;							break;
			case XPKERR_OLDMASTLIB:		DOSErr=ERROR_INVALID_RESIDENT_LIBRARY;	break;
			case XPKERR_OLDSUBLIB:		DOSErr=ERROR_INVALID_RESIDENT_LIBRARY;	break;
			case XPKERR_NOCRYPT:			DOSErr=ERROR_OBJECT_WRONG_TYPE;					break;
			case XPKERR_NOINFO:				DOSErr=ERROR_INVALID_RESIDENT_LIBRARY;	break;
			case XPKERR_LOSSY:				DOSErr=ERROR_OBJECT_WRONG_TYPE;					break;
			case XPKERR_NOHARDWARE:		DOSErr=ERROR_NOT_IMPLEMENTED;						break;
			case XPKERR_BADHARDWARE:	DOSErr=ERROR_NOT_IMPLEMENTED;						break;
			case XPKERR_WRONGPW:			DOSErr=ERROR_OBJECT_WRONG_TYPE;					break;
			default:									DOSErr=ERROR_OBJECT_WRONG_TYPE;					break;
		}
	return(DOSErr);
}	/* XPK2DOS */


/*
**	DataTypes stubs
*/
STATIC ULONG LocSetDTAttrs(struct ClassBase *cb, Object *o, ULONG data, ...)
{
	return(SetDTAttrsA(o,NULL,NULL,(struct TagItem *)&data));
}	/* LocSetDTAttrs */

STATIC ULONG LocGetDTAttrs(struct ClassBase *cb, Object *o, ULONG data, ...)
{
	return(GetDTAttrsA(o,(struct TagItem *)&data));
}	/* LocGetDTAttrs */
