#ifndef XADMASTER_IO_C
#define XADMASTER_IO_C

/* Programmheader

	Name:		xadIO.c
	Main:		xadmaster
	Versionstring:	$VER: xadIO.c 1.2 (24.03.2001)
	Author:		SDI
	Distribution:	Freeware
	Description:	input/output functions

 1.0   24.10.00 : first version, based on older seperate files
 1.1   07.11.00 : moved xadIOPutChar, xadIOGetChar into structure to
	support different functions
 1.2   24.03.01 : added ReadBits versions
*/

/* In case you are calling this file directly from source, you may use this
defines to make the functions static, inlined or whatever else. In case it
should be used a sobject file, they must be cleared, as they default to
static. */

/* the main functions */
#ifndef XADIOFUNCMODE
#define XADIOFUNCMODE static
#endif

/* the bit functions */
#ifndef XADIOFUNCMODEBITS
#define XADIOFUNCMODEBITS XADIOFUNCMODE
#endif

#define XADIODIRECTMODE
#include "xadIO.h"
#include <exec/memory.h>

#define XIDBUFSIZE		10240

static UBYTE xadIOPutFunc(struct xadInOut *io, UBYTE data)
{
  if(!io->xio_Error)
  {
    if(!io->xio_OutSize && !(io->xio_Flags & XADIOF_NOOUTENDERR))
    {
      io->xio_Error = XADERR_DECRUNCH;
      io->xio_Flags |= XADIOF_ERROR;
    }
    else
    {
      if(io->xio_OutBufferPos >= io->xio_OutBufferSize)
        xadIOWriteBuf(io);
      io->xio_OutBuffer[io->xio_OutBufferPos++] = data;
      if(!--io->xio_OutSize)
        io->xio_Flags |= XADIOF_LASTOUTBYTE;
    }
  }
  return data;
}

static UBYTE xadIOGetFunc(struct xadInOut *io)
{
  UBYTE res = 0;

  if(!io->xio_Error)
  {
    if(!io->xio_InSize)
    {
      if(!(io->xio_Flags & XADIOF_NOINENDERR))
      {
        io->xio_Error = XADERR_DECRUNCH;
        io->xio_Flags |= XADIOF_ERROR;
      }
    }
    else
    {
      if(io->xio_InBufferPos >= io->xio_InBufferSize)
      {
        ULONG i;
        struct xadMasterBase *xadMasterBase = io->xio_xadMasterBase;

        if((i = io->xio_InBufferSize) > io->xio_InSize)
          i = io->xio_InSize;
        if(!io->xio_ArchiveInfo)
        {
          io->xio_Flags |= XADIOF_ERROR;
          io->xio_Error = XADERR_DECRUNCH;
        }
        else if(!(io->xio_Error = xadHookTagAccess(XADAC_READ, i, io->xio_InBuffer, io->xio_ArchiveInfo,
        XAD_USESKIPINFO, 1, TAG_DONE)))
        {
          if(io->xio_InFunc)
            io->xio_InFunc(io, i);
          res = *io->xio_InBuffer;
        }
        else
          io->xio_Flags |= XADIOF_ERROR;
        io->xio_InBufferPos = 1;
      }
      else
        res = io->xio_InBuffer[io->xio_InBufferPos++];
      --io->xio_InSize;
    }
    if(!io->xio_InSize)
      io->xio_Flags |= XADIOF_LASTINBYTE;
  }

  return res;
}

XADIOFUNCMODE struct xadInOut *xadIOAlloc(ULONG flags, struct xadArchiveInfo *ai, struct xadMasterBase *xadMasterBase)
{
  ULONG size = sizeof(struct xadInOut);
  struct xadInOut *io;

  if(flags & XADIOF_ALLOCINBUFFER)
    size += XIDBUFSIZE;
  if(flags & XADIOF_ALLOCOUTBUFFER)
    size += XIDBUFSIZE;
  if((io = (struct xadInOut *) xadAllocVec(size, MEMF_CLEAR|MEMF_PUBLIC)))
  {
    STRPTR b;

    b = (STRPTR) (io+1);
    io->xio_Flags = flags;
    io->xio_PutFunc = xadIOPutFunc;
    io->xio_GetFunc = xadIOGetFunc;
    io->xio_ArchiveInfo = ai;
    io->xio_xadMasterBase = xadMasterBase;
    if(flags & XADIOF_ALLOCINBUFFER)
    {
      io->xio_InBuffer = b; b += XIDBUFSIZE;
      io->xio_InBufferSize = io->xio_InBufferPos = XIDBUFSIZE;
    }
    if(flags & XADIOF_ALLOCOUTBUFFER)
    {
      io->xio_OutBuffer = b;
      io->xio_OutBufferSize = XIDBUFSIZE;
    }
  }
  return io;
}

#ifdef XADIOGETBITSLOW
XADIOFUNCMODEBITS ULONG xadIOGetBitsLow(struct xadInOut *io, UBYTE bits)
{
  ULONG x;

  while(io->xio_BitNum < bits)
  {
    io->xio_BitBuf |= xadIOGetChar(io) << io->xio_BitNum;
    io->xio_BitNum += 8;
  }
  x = io->xio_BitBuf & ((1<<bits)-1);
  io->xio_BitBuf >>= bits;
  io->xio_BitNum -= bits;
  return x;
}
#endif

#ifdef XADIOREADBITSLOW
XADIOFUNCMODEBITS ULONG xadIOReadBitsLow(struct xadInOut *io, UBYTE bits)
{
  while(io->xio_BitNum < bits)
  {
    io->xio_BitBuf |= xadIOGetChar(io) << io->xio_BitNum;
    io->xio_BitNum += 8;
  }
  return io->xio_BitBuf & ((1<<bits)-1);
}

XADIOFUNCMODEBITS void xadIODropBitsLow(struct xadInOut *io, UBYTE bits)
{
  io->xio_BitBuf >>= bits;
  io->xio_BitNum -= bits;
}
#endif

#ifdef XADIOGETBITSHIGH
XADIOFUNCMODEBITS ULONG xadIOGetBitsHigh(struct xadInOut *io, UBYTE bits)
{
  ULONG x;

  while(io->xio_BitNum < bits)
  {
    io->xio_BitBuf = (io->xio_BitBuf << 8) | xadIOGetChar(io);
    io->xio_BitNum += 8;
  }
  x = (io->xio_BitBuf >> (io->xio_BitNum-bits)) & ((1<<bits)-1);
  io->xio_BitNum -= bits;
  return x;
}
#endif

#ifdef XADIOREADBITSHIGH
XADIOFUNCMODEBITS ULONG xadIOReadBitsHigh(struct xadInOut *io, UBYTE bits)
{
  while(io->xio_BitNum < bits)
  {
    io->xio_BitBuf = (io->xio_BitBuf << 8) | xadIOGetChar(io);
    io->xio_BitNum += 8;
  }
  return (io->xio_BitBuf >> (io->xio_BitNum-bits)) & ((1<<bits)-1);
}

XADIOFUNCMODEBITS void xadIODropBitsHigh(struct xadInOut *io, UBYTE bits)
{
  io->xio_BitNum -= bits;
}
#endif

XADIOFUNCMODE LONG xadIOWriteBuf(struct xadInOut *io)
{
  if(!io->xio_Error && io->xio_OutBufferPos)
  {
    struct xadMasterBase *xadMasterBase = io->xio_xadMasterBase;

    if(io->xio_OutFunc)
      io->xio_OutFunc(io, io->xio_OutBufferPos);
    if(!(io->xio_Flags & XADIOF_COMPLETEOUTFUNC))
    {
      if(!io->xio_ArchiveInfo)
      {
        io->xio_Flags |= XADIOF_ERROR;
        io->xio_Error = XADERR_DECRUNCH;
      }
      else if((io->xio_Error = xadHookTagAccess(XADAC_WRITE, io->xio_OutBufferPos,
      io->xio_OutBuffer, io->xio_ArchiveInfo,
      io->xio_Flags & XADIOF_NOCRC16 ? TAG_IGNORE : XAD_GETCRC16, &io->xio_CRC16,
      io->xio_Flags & XADIOF_NOCRC32 ? TAG_DONE   : XAD_GETCRC32, &io->xio_CRC32,
      TAG_DONE)))
        io->xio_Flags |= XADIOF_ERROR;
    }
    io->xio_OutBufferPos = 0;
  }
  return io->xio_Error;
}

#endif /* XADMASTER_IO_C */
