#ifndef XPKMASTER_OPEN_C
#define XPKMASTER_OPEN_C

/* Routinesheader

	Name:		open.c
	Main:		xpkmaster
	Versionstring:	$VER: open.c 1.16 (26.03.1998)
	Author:		SDI
	Distribution:	Freeware
	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
 1.8   19.12.97 : added xfdmaster.library support
 1.9   22.12.97 : fixed auto password problem
 1.10  27.12.97 : fixed GetPassword error
 1.11  09.01.98 : better key handling
 1.12  21.01.98 : added password verification for packing
 1.13  24.01.98 : fixed xfdmaster support a bit
 1.14  17.02.98 : fixe long file problem added in last version
 1.15  21.02.98 : uses new style register definition
 1.16  26.03.98 : some optimizations
*/

#include <xpk/xpkprefs.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/xpkmaster.h>
#include <proto/xpksub.h>
#include <proto/xfdmaster.h>
#include "xpkmaster.h"
#include "texts.h"

static const struct XpkInfo DONTInfo = { 1,0,0,1,"DONT","Copy",
0, 0x55534552, XPKIF_PK_CHUNK|XPKIF_UP_CHUNK, DEFAULTCHUNKSIZE, 1,
DEFAULTCHUNKSIZE,0,0,0,0,100,0,0,0,0,0,0,0,0};
/* XpkMode is not initialized! Should not be used anywhere */

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

XPK_ALLINONE ASM(LONG) xpkopen(REG(a0, struct XpkBuffer **xbufp),
REG(a1, struct TagItem *tags), REG(d2, ULONG examine A4PROTO))
{
  struct XpkBuffer	 *xbuf;
  struct XpkStreamHeader *globhdr;
  struct XpkFib		 *fib;

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

  if(!(*xbufp = xbuf = initxbuf()))
    return parseerrortags(tags, XPKERR_NOMEM);

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

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

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

  if(xbuf->xb_Flags & XMF_PACKING) /* Call pack open function */
    return xpkopenwrite(xbufp, tags);

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

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

    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(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;

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

    if(!(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_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)
      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, -xbuf->xb_RMsg.xmm_Size))
    goto Abort; /* redo last read bytes */

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

  fib->xf_CLen = xbuf->xb_InLen;

#ifdef USE_POWERPACKER
  /***************************** PowerPacker file ***********************/
  if(globhdr->xsh_Pack == PP_COOKIE)
  {
    ULONG 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_ULen = outsize;
    fib->xf_NLen = outsize + XPK_MARGIN;
    fib->xf_ID = PP_COOKIE;
    percentages(fib);
    xbuf->xb_Prog.xp_Activity = strings[TXT_UNPACKING_UPPER];
    xbuf->xb_Prog.xp_PackerName = "PowerPacker";
    xbuf->xb_LastMsg = strings[TXT_UNPACKED];

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

    goto Exit;
  }
#endif /* USE_POWERPACKER */

  /**************************** xfdmaster file **************************/
  if(xbuf->xb_Flags & XMF_XFD &&
  (xbuf->xb_SubBase = OpenLibrary("xfdmaster.library", 38)))
  {
    struct xfdMasterBase *xfdMasterBase = (struct xfdMasterBase *) xbuf->xb_SubBase;
    struct xfdBufferInfo *xbi;

    if(!(xbi = xbuf->xb_xfd = (struct xfdBufferInfo *) xfdAllocObject(XFDOBJ_BUFFERINFO)))
      goto Abort;
    if(!(xbi->xfdbi_SourceBuffer = hookread(xbuf, XIO_READ, 0, xbuf->xb_InLen)))
      goto Abort;
    xbi->xfdbi_SourceBufLen = xbuf->xb_InLen;
    xbi->xfdbi_Flags = XFDFF_RECOGEXTERN|XFDFF_RECOGTARGETLEN|XFDFF_RECOGUSERTARGET;

    if(xfdRecogBuffer(xbi) && (xbi->xfdbi_PackerFlags & XFDPFF_DATA) &&
    (LONG) xbi->xfdbi_FinalTargetLen != -1)
    {
      xbuf->xb_Format = XPKMODE_UPXFD;
      if(xbi->xfdbi_PackerFlags & XFDPFF_PASSWORD)
        xbuf->xb_Fib.xf_Flags |= XPKFLAGS_PASSWORD;
      if(xbi->xfdbi_PackerFlags & XFDPFF_KEY16)
        xbuf->xb_Fib.xf_Flags |= XPKFLAGS_KEY16;
      if(xbi->xfdbi_PackerFlags & XFDPFF_KEY32)
        xbuf->xb_Fib.xf_Flags |= XPKFLAGS_KEY32;
      fib->xf_Type = XPKTYPE_PACKED;
      fib->xf_ULen = xbi->xfdbi_FinalTargetLen;
      fib->xf_NLen = xbi->xfdbi_MinTargetLen;
      fib->xf_ID = XFD_COOKIE;
      percentages(fib);
      xbuf->xb_Prog.xp_Activity = strings[TXT_UNPACKING_UPPER];
      xbuf->xb_Prog.xp_PackerName = "XFDMaster";
      xbuf->xb_LastMsg = strings[TXT_UNPACKED];

      if(examine && !hookread(xbuf, XIO_SEEK, 0, -xbuf->xb_InLen))
        goto Abort; /* return to start */

#ifdef DEBUG
      DebugRunTime("xpkopen: XFD, InLen %ld, OutLen %ld, NLen %ld",
      xbuf->xb_InLen, fib->xf_ULen, fib->xf_NLen);
#endif
      goto Exit;
    } /* xfdRecogBuffer */

    if(!hookread(xbuf, XIO_SEEK, 0, -xbuf->xb_InLen))
      goto Abort; /* return to start */
  }

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

    fib->xf_Type = XPKTYPE_UNPACKED;
    fib->xf_ULen = xbuf->xb_InLen;
    fib->xf_NLen = Min(DEFAULTCHUNKSIZE, 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_Result = XPKERR_OK;  /* if != 0 it 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:
  if(!examine && (
  ((fib->xf_Flags & XPKFLAGS_PASSWORD) && !xbuf->xb_Password) ||
  ((fib->xf_Flags & XPKFLAGS_KEY16) && !(xbuf->xb_Flags & XMF_KEY16)) ||
  ((fib->xf_Flags & XPKFLAGS_KEY32) && !(xbuf->xb_Flags & XMF_KEY32)))
  && (xbuf->xb_Result = GetPassword(xbuf, tags, FALSE)))
      goto Abort;
  return XPKERR_OK;
}

/***************************** Open for packing *************************/
static 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? */
  (xbuf->xb_Result = GetPrefsPacker(xbuf)))
    goto Abort; /* no sublib and no prefs packer finder */

  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, TRUE)))
      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) /* (0xFFFF-XPK_MARGIN) and some bytes security */
    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 ASM(struct XpkTypeData *) (*RecogFunc) (REG(a0, STRPTR),
	REG(a1, STRPTR), REG(d0, ULONG), REG(d1, ULONG),
	REG(a2, struct TagItem *));

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)
    return XPKERR_BADPARAMS;
  if(!(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 = (struct XpkInfo *) &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);
//	  }
//	}
      }
    }
    else
      ret = XPKERR_NOMETHOD;
  }

  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,
ULONG verify)
{
 if(xbuf->xb_Flags & XMF_AUTOPASSWD)
 {
   if(xbuf->xb_Fib.xf_Flags & XPKFLAGS_KEY32)
   {
     xbuf->xb_Result = XpkPassRequestTags(XPK_Key32BitPtr,
     &xbuf->xb_PassKey32, TAG_MORE, tags, TAG_DONE);
     xbuf->xb_Flags |= XMF_KEY32;
   }
   else if(xbuf->xb_Fib.xf_Flags & XPKFLAGS_KEY16)
   {
     xbuf->xb_Result = XpkPassRequestTags(XPK_Key16BitPtr,
     &xbuf->xb_PassKey16, TAG_MORE, tags, TAG_DONE);
     xbuf->xb_Flags |= XMF_KEY16;
   }
   else
   {
     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 */

     xbuf->xb_Result = XpkPassRequestTags(XPK_PasswordBuf,
     xbuf->xb_Password, XPK_PassBufSize, xbuf->xb_PasswordSize,
     XPK_PassVerify, verify, TAG_MORE, tags, TAG_DONE);
   }

   return xbuf->xb_Result;
 }
 return XPKERR_NEEDPASSWD;
}

static struct XpkTypeData *BufRecog(ULONG bufsize, struct XpkBuffer *xbuf,
struct XpkPrefsSemaphore *sem)
{
  STRPTR bufptr;
  struct XpkTypeData *ret = 0;
  struct TagItem tag[] = {
  { XPK_FileName, 0},
  { XPK_PackMode, 0},
  { TAG_DONE, 0}};

  tag[0].ti_Data = (ULONG) xbuf->xb_Prog.xp_FileName;
  tag[1].ti_Data = xbuf->xb_PackingMode;

  if((bufptr = (STRPTR) hookread(xbuf, XIO_READ, 0, bufsize)))
  {
    ret = (((RecogFunc) sem->xps_RecogFunc) (bufptr,
    xbuf->xb_RMsg.xmm_FileName, bufsize, xbuf->xb_InLen, tag));
    hookread(xbuf, XIO_SEEK, 0, -bufsize);
  }
  return ret;
}

#endif /* XPKMASTER_OPEN_C */
