#ifndef XPKMASTER_OPEN_C
#define XPKMASTER_OPEN_C

/* Routinesheader

	Name:		open.c
	Main:		xpkmaster
	Versionstring:	$VER: open.c 1.7 (31.03.97)
	Author:		SDI
	Distribution:	PD
	Description:	Opening and initialisation routines for XPK files

 1.0   06.10.96 : first real version
 1.1   28.10.96 : reincluded A4 support
 1.2   03.03.97 : added Prefs, corrected length recognition
 1.3   07.03.97 : fixed prefs handling, new features
 1.4   09.03.97 : added DEBUG statement
 1.5   28.03.97 : auto decrunch password
 1.6   29.03.97 : fixed prefs stuff, moved getinlen into hooks
 1.7   31.03.97 : changed the password stuff
*/

#include <exec/types.h>
#include <dos/dos.h>
#include <xpk/xpkprefs.h>
#include <exec/memory.h>
#include <pragma/exec_lib.h>
#include <pragma/dos_lib.h>
#include <pragma/xpkmaster_lib.h>
#include "xpkmaster.h"
#include "xpk_strings.h"

struct XpkInfo DONTInfo = { 1,0,0,1,"DONT","Copy",
0, 0x55534552, XPKIF_PK_CHUNK|XPKIF_UP_CHUNK, 50000, 10, 50000,
0,0,0,0,100,0,0 /* Mode */,0,0,0,0,0,0};

static LONG GetPrefsPacker(struct XpkBuffer *xbuf);
static LONG GetPassword(struct XpkBuffer *xbuf, struct TagItem *tags);
static struct XpkTypeData *BufRecog(ULONG, struct XpkBuffer *,
  struct XpkPrefsSemaphore *);

#define ROW_OF_MINUS	0x2d2d2d2d	/* '----' */
#define PP_COOKIE	0x50503230	/* 'PP20' */

LONG __asm xpkopen(register __a0 struct XpkBuffer **xbufp,
register __a1 struct TagItem *tags, register __d2 ULONG examine A4PROTO)
{
  struct Library	 *XpkSubBase;
  struct XpkBuffer	 *xbuf;
  struct XpkStreamHeader *globhdr;
  struct XpkFib		 *fib;
  LONG bytesread;	/* used to be able to reset after a short read */

#if defined(DEBUG) && defined(SUPPORT_A4)
  DebugRunTime("xpkopen: A4 = %ld", a4);
#elif defined (DEBUG)
  DebugRunTime("xpkopen");
#endif

  *xbufp = 0;
  if(!(xbuf = initxbuf()))
  {
    parseerrortags(tags);
    return XPKERR_NOMEM;
  }

#ifdef SUPPORT_A4
  xbuf->xb_regA4 = a4;
#endif

  globhdr = &xbuf->xb_Headers.h_Glob;
  fib = &xbuf->xb_Fib;

  if(parsebuftags(xbuf, tags, 0))
    goto Abort;

  *xbufp = xbuf;
  if(xbuf->xb_Flags & XMF_PACKING)
    return xpkopenwrite(xbufp, tags);

  if(!hookread(xbuf, XIO_READ, globhdr, 4))
  {	/* Read first longword only */
    bytesread = xbuf->xb_RMsg.xmm_Size;
    if(xbuf->xb_Result != XPKERR_TRUNCATED)
      goto Abort;
    /* else handle now as uncompressed file */
  }
  else
    bytesread = 4;

  /**************************** Standard XPK file *********************/
  if(globhdr->xsh_Pack == XPK_COOKIE)
  {	/* Standard XPK packed files */
    UWORD exthlen = 0;		/* size of extended header if present */

    xbuf->xb_Format = XPKMODE_UPSTD;

    /* Read rest of the global header */
    if(!hookread(xbuf, XIO_READ, (STRPTR) globhdr + 4, sizeof(struct XpkStreamHeader) - 4))
      goto Abort;

    if(hchecksum((STRPTR) globhdr, sizeof(struct XpkStreamHeader)))
    {
      xbuf->xb_Result = XPKERR_CHECKSUM;
      goto Abort;
    }

    if(!examine && globhdr->xsh_Flags & XPKSTREAMF_PASSWORD &&
    !xbuf->xb_Password)
    {
      if((xbuf->xb_Result = GetPassword(xbuf, tags)))
        goto Abort;
    }

    if(globhdr->xsh_Flags & XPKSTREAMF_LONGHEADERS)
      xbuf->xb_Headers.h_LocSize = sizeof(struct XpkChunkHdrLong);
    else
      xbuf->xb_Headers.h_LocSize = sizeof(struct XpkChunkHdrWord);

    if(globhdr->xsh_Flags & XPKSTREAMF_EXTHEADER)
    {
      if(!hookread(xbuf, XIO_READ, &exthlen, sizeof(UWORD)))
	goto Abort;
      if(!hookread(xbuf, XIO_READ, NULL, exthlen))
	goto Abort;
      exthlen += sizeof(UWORD);	/* for unwinding while XpkExamine */
    }

    if(!hookread(xbuf, XIO_READ, &xbuf->xb_Headers.h_Loc,
    xbuf->xb_Headers.h_LocSize))	/* first lochdr */
      goto Abort;

    xbuf->xb_Fib.xf_CCur = sizeof(struct XpkStreamHeader);
    updatefib(xbuf);
    xbuf->xb_InLen = xbuf->xb_Fib.xf_CLen;

    if(!(xbuf->xb_SubBase = XpkSubBase = opensub(xbuf, globhdr->xsh_Type)))
      goto Abort;

    if(globhdr->xsh_SubVrs > xbuf->xb_SubInfo->xi_LibVersion)
    {
      xbuf->xb_Result = XPKERR_OLDSUBLIB;
      goto Abort;
    }

    xbuf->xb_ULen = globhdr->xsh_ULen;
    xbuf->xb_Prog.xp_Activity = xbuf->xb_SubInfo->xi_UnpackMsg ?
      xbuf->xb_SubInfo->xi_UnpackMsg : strings[TXT_UNPACKING_UPPER];
    xbuf->xb_Prog.xp_PackerName = xbuf->xb_SubInfo->xi_Name;
    xbuf->xb_LastMsg = xbuf->xb_SubInfo->xi_UnpackedMsg ?
      xbuf->xb_SubInfo->xi_UnpackedMsg : strings[TXT_UNPACKED];

    if(globhdr->xsh_Flags & XPKSTREAMF_PASSWORD)
      xbuf->xb_Fib.xf_Flags |= XPKFLAGS_PASSWORD;

    if(examine && !hookread(xbuf, XIO_SEEK, 0,
    -(sizeof(struct XpkStreamHeader) + xbuf->xb_Headers.h_LocSize+exthlen)))
      goto Abort;

    goto Exit;
  }

  if(!hookread(xbuf, XIO_SEEK, 0, -bytesread))
    goto Abort;

  if(xbuf->xb_InLen == -1)
  {
    if(!hookread(xbuf, XIO_TOTSIZE, 0, 0))	/* get input length */
      return xbuf->xb_Result;
    else if(xbuf->xb_RMsg.xmm_Size)
      xbuf->xb_InLen = xbuf->xb_RMsg.xmm_Size;
  }

#ifdef USE_POWERPACKER
  /***************************** PowerPacker file ***********************/
  if(globhdr->xsh_Pack == PP_COOKIE)
  {
    struct Library *PPBase;
    LONG outsize;

    xbuf->xb_Format = XPKMODE_UPPP;

    if(!hookread(xbuf, XIO_SEEK, 0, xbuf->xb_InLen - 4))
      goto Abort;				/* 4 Bytes before EOF */
    if(!hookread(xbuf, XIO_READ, &outsize, 4))
      goto Abort;
    if(!hookread(xbuf, XIO_SEEK, 0, - (xbuf->xb_InLen - 4) - (examine<<2)))
      goto Abort;	/* back to start when examine, else 4 bytes later */

    outsize >>= 8;

#ifdef DEBUG
    DebugRunTime("xpkopen: PP, InLen %ld, OutLen %ld", xbuf->xb_InLen,
    outsize);
#endif

    fib->xf_Type = XPKTYPE_PACKED;
    fib->xf_CLen = xbuf->xb_InLen;
    fib->xf_ULen = outsize;
    fib->xf_NLen = outsize + XPK_MARGIN;
    fib->xf_ID = PP_COOKIE;
    percentages(fib);

    if(examine)
      goto Exit;

    xbuf->xb_Prog.xp_Activity = strings[TXT_UNPACKING_UPPER];
    xbuf->xb_Prog.xp_PackerName = "PowerPacker";
    xbuf->xb_LastMsg = strings[TXT_UNPACKED];
    xbuf->xb_ULen = outsize;

    if(!(PPBase = OpenLibrary ("powerpacker.library", 0)))
    {
      xbuf->xb_Result = XPKERR_MISSINGLIB;
      goto Abort;
    }
    xbuf->xb_SubBase = PPBase;

    goto Exit;
  }
#endif /* USE_POWERPACKER */

  /**************************** Uncompressed file *************************/
  if(examine || xbuf->xb_Flags & XMF_PASSTHRU)		/* Unpacked */
  {
    xbuf->xb_Format = XPKMODE_UPUP;

    fib->xf_Type = XPKTYPE_UNPACKED;
    fib->xf_CLen = xbuf->xb_InLen;
    fib->xf_ULen = xbuf->xb_InLen;
    fib->xf_NLen = Min(CHUNKSIZE, xbuf->xb_InLen) + XPK_MARGIN;
    fib->xf_ID = ROW_OF_MINUS;

    xbuf->xb_Prog.xp_Activity = strings[TXT_READING];
    xbuf->xb_Prog.xp_PackerName = "MASTER";
    xbuf->xb_LastMsg = strings[TXT_READ];
    xbuf->xb_ULen = xbuf->xb_InLen;

    xbuf->xb_Result = XPKERR_OK;	/* if != 0 is was XPKERR_TRUNCATED */

    goto Exit;
  }

  xbuf->xb_Result = XPKERR_NOTPACKED;	/* Can't unpack, can't passthru */

Abort:
  *xbufp = 0;
  return XpkClose((struct XpkFib *) xbuf);

Exit:
  *xbufp = xbuf;
  return XPKERR_OK;
}

/****************************** Open for packing **************************/
LONG xpkopenwrite(struct XpkBuffer **xbufp, struct TagItem *tags)
{
  struct XpkBuffer		*xbuf		= *xbufp;
  struct XpkStreamHeader	*globhdr	= &xbuf->xb_Headers.h_Glob;
  struct Library		*XpkSubBase;
  LONG				 res;

  xbuf->xb_Format = XPKMODE_PKSTD;

  if(xbuf->xb_InLen == -1)
  {
    if(!hookread(xbuf, XIO_TOTSIZE, 0, 0))	/* get input length */
      return xbuf->xb_Result;
    else if(xbuf->xb_RMsg.xmm_Size)
      xbuf->xb_InLen = xbuf->xb_RMsg.xmm_Size;
  }

  if(!(XpkSubBase = xbuf->xb_SubBase) &&  /* Do we know the sublib? */
  GetPrefsPacker(xbuf))
  { /* no sublib and no prefs packer finder */
    xbuf->xb_Result = XPKERR_BADPARAMS;
    goto Abort;
  }

  xbuf->xb_MinChunk = xbuf->xb_SubInfo->xi_MinPkInChunk;

  if(xbuf->xb_Password && !(xbuf->xb_SubInfo->xi_Flags & XPKIF_ENCRYPTION))
  {
    xbuf->xb_Result = XPKERR_NOCRYPT;
    goto Abort;
  }

  if(!xbuf->xb_Password && (xbuf->xb_SubInfo->xi_Flags & XPKIF_NEEDPASSWD))
  { /* automatic password requester */
    if((xbuf->xb_Result = GetPassword(xbuf, tags)))
      goto Abort;
  }

  if(!(xbuf->xb_Flags & XMF_LOSSYOK) &&
  xbuf->xb_SubInfo->xi_Flags & XPKIF_LOSSY)
  {
    xbuf->xb_Result = XPKERR_LOSSY;
    goto Abort;
  }

  if(xbuf->xb_PackingMode > 100)	/* Is packing mode valid? */
    xbuf->xb_PackingMode = 100;		/* Use max */

  if(!hookwrite(xbuf, XIO_TOTSIZE, 0, ROUNDLONG
  (xbuf->xb_InLen + (xbuf->xb_InLen >> 5)) + (XPK_MARGIN<<1)))
    goto Abort;

  /*********************** Find the chunk size *********************/
  if((xbuf->xb_ChunkSize == 0) &&
  ((xbuf->xb_ChunkSize = xbuf->xb_SubInfo->xi_DefPkInChunk) == 0))
    xbuf->xb_ChunkSize = DEFAULTCHUNKSIZE;
  if(xbuf->xb_ChunkSize < xbuf->xb_SubInfo->xi_MinPkInChunk)
    xbuf->xb_ChunkSize = xbuf->xb_SubInfo->xi_MinPkInChunk;
  if((xbuf->xb_SubInfo->xi_MaxPkInChunk) &&
  (xbuf->xb_ChunkSize > xbuf->xb_SubInfo->xi_MaxPkInChunk))
    xbuf->xb_ChunkSize = xbuf->xb_SubInfo->xi_MaxPkInChunk;

  /******************* Prepare global header *********************/
  globhdr->xsh_Pack = 0;		/* Initialize the global header */
  globhdr->xsh_Type = xbuf->xb_SubID;

  if(xbuf->xb_ChunkSize > 65000)
    globhdr->xsh_Flags |= XPKSTREAMF_LONGHEADERS;
  if(xbuf->xb_Password)
    globhdr->xsh_Flags |= XPKSTREAMF_PASSWORD;

  xbuf->xb_Headers.h_LocSize = globhdr->xsh_Flags & XPKSTREAMF_LONGHEADERS
    ? sizeof (struct XpkChunkHdrLong)
    : sizeof (struct XpkChunkHdrWord);

  memset(globhdr->xsh_Initial, 0xff, 16);	/* Read first 16 bytes */

  xbuf->xb_Prog.xp_Activity = xbuf->xb_SubInfo->xi_PackMsg ?
  xbuf->xb_SubInfo->xi_PackMsg : strings[TXT_PACKING_UPPER];
  xbuf->xb_Prog.xp_PackerName = xbuf->xb_SubInfo->xi_Name;
  xbuf->xb_LastMsg = xbuf->xb_SubInfo->xi_PackedMsg ?
  xbuf->xb_SubInfo->xi_PackedMsg : strings[TXT_PACKED];

Abort:
  xbuf->xb_Fib.xf_NLen = Min(xbuf->xb_InLen - xbuf->xb_Fib.xf_UCur,
  xbuf->xb_ChunkSize);

  if((res = xbuf->xb_Result))
    res = XpkClose((struct XpkFib *) xbuf);

  return res;
}

typedef struct XpkTypeData * __asm (*RecogFunc) (register __a0 STRPTR,
  register __a1 STRPTR, register __a2 STRPTR, register __d0 ULONG,
  register __d1 ULONG);

static LONG GetPrefsPacker(struct XpkBuffer *xbuf)
{
  LONG ret = XPKERR_UNKNOWN;
  struct XpkPrefsSemaphore *sem;
  ULONG bufsize;
  struct XpkTypeData *td = 0;

  if((xbuf->xb_Flags & XMF_NOPREFS) || !(sem = GetPrefsSem()))
    return XPKERR_NOFUNC;

  bufsize = Min(xbuf->xb_InLen, sem->xps_RecogSize);

  if(sem->xps_RecogFunc && (td = BufRecog(bufsize, xbuf, sem)) ==
  (struct XpkTypeData *) 0xFFFFFFFF)
    td = BufRecog(xbuf->xb_InLen, xbuf, sem);

  if(!td || td == (struct XpkTypeData *) 0xFFFFFFFF)
    td = sem->xps_MainPrefs ? sem->xps_MainPrefs->xmp_DefaultType : 0;

  if(td)
  {
    if(td->xtd_Flags & XTD_NoPack)
    {
      xbuf->xb_Flags |= XMF_NOPACK;
      xbuf->xb_SubInfo = &DONTInfo;
      ret = XPKERR_OK;
    }
    else if(!(td->xtd_Flags & XTD_ReturnError))
    {
      struct Library *XpkSubBase;
      struct XpkInfo *subinfo;

      if((XpkSubBase = opensub(xbuf, td->xtd_StdID)))
      {
	ret = XPKERR_OK;
        subinfo = XpksPackerInfo();

        xbuf->xb_ChunkSize = td->xtd_ChunkSize;
        xbuf->xb_PackingMode = ( td->xtd_Mode ? td->xtd_Mode :
          subinfo->xi_DefMode);
//	if(!(xbuf->xb_Password) && td->xtd_Password && 
//	(xbuf->xb_PasswordSize = strlen(td->xtd_Password)))
//	{
//	  /* we need a buffer including end byte! --> ++size */
//	  if(!(xbuf->xb_Password = (STRPTR)
//	  AllocMem(++xbuf->xb_PasswordSize, MEMF_PUBLIC)))
//	    ret = XPKERR_NOMEM;
//        else
//	  {
//	    xbuf->Flags |= XMF_OWNPASSWORD;
//	    CopyMem(td->xtd_Password, xbuf->xb_Password, xbuf->xb_PasswordSize);
//	  }
//	}
      }
    }
  }

  if(td->xtd_Memory && td->xtd_MemorySize)
    FreeMem(td->xtd_Memory, td->xtd_MemorySize);

  ReleaseSemaphore((struct SignalSemaphore *) sem);
  return ret;
}

static LONG GetPassword(struct XpkBuffer *xbuf, struct TagItem *tags)
{
 if(xbuf->xb_Flags & XMF_AUTOPASSWD)
 {
   if(!(xbuf->xb_Password = (STRPTR) AllocMem(AUTO_PASS_SIZE, MEMF_PUBLIC)))
     return XPKERR_NOMEM;
   xbuf->xb_PasswordSize = AUTO_PASS_SIZE;
   xbuf->xb_Flags |= XMF_OWNPASSWORD; /* must be freed later */

   if((xbuf->xb_Result = XpkPassRequestTags(XPK_PasswordBuf,
   xbuf->xb_Password, XPK_PassBufSize, xbuf->xb_PasswordSize, TAG_MORE,
   tags, TAG_DONE)))
     return xbuf->xb_Result;
   else
   {
#ifdef DEBUG
     DebugRunTime("GetPassword: %s", xbuf->xb_Password);
#endif
     return XPKERR_OK;
   }
 }
 return XPKERR_NEEDPASSWD;
}

static struct XpkTypeData *BufRecog(ULONG bufsize, struct XpkBuffer *xbuf,
struct XpkPrefsSemaphore *sem)
{
  STRPTR bufptr;
  struct XpkTypeData *ret = 0;

  if((bufptr = hookread(xbuf, XIO_READ, 0, bufsize)))
  {
    ret = (((RecogFunc) sem->xps_RecogFunc) (bufptr, xbuf->xb_InName,
    xbuf->xb_Prog.xp_FileName, bufsize, xbuf->xb_InLen));
    hookread(xbuf, XIO_SEEK, 0, -bufsize);
  }
  return ret;
}

#endif /* XPKMASTER_OPEN_C */
