#ifndef XADMASTER_FS_AMIGA_C
#define XADMASTER_FS_AMIGA_C

/*  $Id: FS_Amiga.c,v 1.12 2006/06/01 06:55:03 stoecker Exp $
    amiga filesytem client

    XAD library system for archive handling
    Copyright (C) 1998 and later by Dirk Stˆcker <soft@dstoecker.de>

    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "../unix/xadClient.h"

#ifndef XADMASTERVERSION
  #define XADMASTERVERSION      10
#endif

XADCLIENTVERSTR("FS_Amiga 1.2 (22.2.2004)")

#define FSAMIGA_VERSION         1
#define FSAMIGA_REVISION        2

#define T_SHORT          2
#define T_DATA           8
#define T_LIST          16
#define T_DIRCACHE      33
#define ST_ROOT          1
#define ST_USERDIR       2
#define ST_FILE         -3
#define ST_LINKFILE     -4
#define ST_LINKDIR       4
#define ST_SOFTLINK      3

/* all the structures have been rearranged to get variable size parts
to structure end. Therefor they cannot be copied from file directly, but need
to be modified! The afs_ structure elements are standard elements, which
offsets are equal in all structures! */

#define afs_SoftLinkName        afs_HashTable
#define afs_BlockList           afs_HashTable
#define afs_DirCache            afs_Extension

struct RootBlock
{
  xadUINT8 afs_Type[4];            /* T_SHORT */
  xadUINT8 afs_HeaderKey[4];       /* == 0 */
  xadUINT8 afs_BlockCount[4];      /* == 0 */
  xadUINT8 afs_HashTableSize[4];
  xadUINT8 afs_FirstBlock[4];      /* == 0 */
  xadUINT8 afs_CheckSum[4];

  xadINT8  afs_BitmapFlag[4];      /* signed, -1=valid, 0=invalid */
  xadUINT8 afs_BitmapKeys[25][4];
  xadUINT8 afs_BitmapExtend[4];
  xadUINT8 afs_Date[12];           /* last root alteration */
  xadUINT8 afs_Name[32];           /* BCPL name, max 30 chars */
  xadUINT8 Unused3[4];                 
  xadUINT8 Unused4[4];                 
  xadUINT8 afs_DiskDate[12];       /* last disk alteration */
  xadUINT8 afs_DiskMade[12];       /* disk creation date */
  xadUINT8 afs_HashChain[4];       /* == 0 */
  xadUINT8 afs_Parent[4];          /* == 0 */
  xadUINT8 afs_Extension[4];       /* dircache pointer */
  xadUINT8 afs_SecondaryType[4];   /* ST_ROOT */

  xadUINT8 afs_HashTable[72][4];
};

struct FileHeaderBlock
{
  xadUINT8 afs_Type[4];         /* T_SHORT */
  xadUINT8 afs_HeaderKey[4];
  xadUINT8 afs_BlockCount[4];
  xadUINT8 afs_DataSize[4];
  xadUINT8 afs_FirstBlock[4];
  xadUINT8 afs_CheckSum[4];

  xadUINT8 Unused1[4];
  xadUINT8 afs_OwnerInfo[4];
  xadUINT8 afs_Protection[4];
  xadUINT8 afs_Size[4];         /* real files only */
  xadUINT8 afs_Comment[92];     /* BCPL name, max 79 chars */
  xadUINT8 afs_Date[12];
  xadUINT8 afs_Name[32];        /* BCPL name, max 30 chars */
  xadUINT8 Unused2[4];
  xadUINT8 afs_LinkOriginal[4]; /* hard links only */
  xadUINT8 afs_LinkList[4];     /* not for soft links */
  xadUINT8 Unused3[5][4];
  xadUINT8 afs_HashList[4];
  xadUINT8 afs_Parent[4];
  xadUINT8 afs_Extension[4];    /* real files only, Dircache for DOS4,5 dirs */
  xadINT8  afs_SecondaryType[4];/* signed; ST_FILE, ST_LINKFILE, ST_LINKDIR, ST_USERDIR, ST_SOFTLINK */

  xadUINT8 afs_HashTable[72][4];/* maybe larger!, unused for links */
};

struct FileExtensionBlock
{
  xadUINT8  afs_Type[4];         /* T_SHORT */
  xadUINT8  afs_HeaderKey[4];
  xadUINT8  afs_BlockCount[4];
  xadUINT8  afs_DataSize[4];
  xadUINT8  afs_FirstBlock[4];
  xadUINT8  afs_CheckSum[4];
               
  xadUINT8  Unused1[47][4];
               
  xadUINT8  afs_Parent[4];
  xadUINT8  afs_Extension[4];
  xadINT8   afs_SecondaryType[4];/* signed; ST_FILE */
               
  xadUINT8  afs_BlockList[72][4];/* maybe larger!, unused for links */
};

struct DirCacheBlock /* no reorder necessary */
{
  xadUINT8 afs_Type[4];
  xadUINT8 afs_HeaderKey[4];
  xadUINT8 afs_DCParent[4];
  xadUINT8 afs_DCRecords[4];
  xadUINT8 afs_NextDirCache[4];
  xadUINT8 afs_CheckSum[4];
  xadUINT8 afs_Records[488][4];  /* maybe larger! */
};

struct BitMapBlock /* no reorder necessary */
{
  xadUINT8 bm_CheckSum[4];
  xadUINT8 bm_Data[127][4];        /* maybe larger! */
};

struct BitmapExtensionBlock
{
  xadUINT8 be_NextBlock[4];

  xadUINT8 be_BMPages[127][4];         /* maybe larger! */
};

struct xadLink {
  struct xadLink *       xl_Next;
  struct xadFileInfo *   xl_Parent;
  struct FileHeaderBlock xl_Header;
};

struct DiskParseData
{
  xadUINT32               Corrupt;
  struct xadArchiveInfo * ai;
  struct xadMasterBase *  MasterBase;
  struct xadFileInfo *    DirList;
  struct xadFileInfo *    CurDir;
  struct xadLink *        LinkList;
  struct FileHeaderBlock *fh1; /* always the dir/root header */
  struct FileHeaderBlock *fh2;
  xadSTRPTR               BList;
};

static xadINT32 getdiskblock(struct xadMasterBase *xadMasterBase, struct xadArchiveInfo *ai,
xadUINT32 block, xadSTRPTR buf, xadUINT32 r)
{
  xadINT32 err = 0;
  xadSize i;
  struct xadImageInfo *ii;

  ii = ai->xai_ImageInfo;

  if(block < ii->xii_FirstSector || block > ii->xii_FirstSector+ii->xii_NumSectors-1)
    err = XADERR_ILLEGALDATA;
  else
  {
    if((i = ((block - ii->xii_FirstSector)*ii->xii_SectorSize)-ai->xai_InPos))
      err = xadHookAccess(XADM XADAC_INPUTSEEK, i, 0, ai);
    if(!err)
      err = xadHookAccess(XADM XADAC_READ, ii->xii_SectorSize, buf, ai);
  }

  if(r && !err)
  {
    xadUINT8 b[50*4];
    xadCopyMem(XADM buf+(ii->xii_SectorSize-(50*4)), b, 50*4);
    xadCopyMem(XADM buf+(6*4), buf+(56*4), (ii->xii_SectorSize-(56*4)));
    xadCopyMem(XADM b, buf+(6*4), 50*4);
  }
  return err;
}

static xadINT32 parsedir(struct DiskParseData *parse)
{
  xadINT32 err = 0;
  xadUINT32 blk, i, j, c, curnamesize = 0;
  xadSTRPTR curdirname = 0, str;
  struct xadLink *l;
  struct FileHeaderBlock *fh;
  struct xadFileInfo *fi;
  struct xadMasterBase *xadMasterBase;
  struct xadImageInfo *ii;

  if(parse->CurDir)
  {
    curdirname = parse->CurDir->xfi_FileName;
    curnamesize = strlen((const char *)curdirname);
  }

  ii = parse->ai->xai_ImageInfo;
  xadMasterBase = parse->MasterBase;
  blk = (ii->xii_SectorSize>>2)-56;
  fh = parse->fh2;
  for(i = 0; !err && i < blk; ++i)
  {
    if((j = EndGetM32(parse->fh1->afs_HashTable[i])))
    {
      c = 0;
      do
      {
        xadUINT32 b, a = 0, e = 1;

        if(j >= ii->xii_FirstSector && j <= ii->xii_FirstSector+ii->xii_NumSectors-1)
        {
          b = j - ii->xii_FirstSector;
          if(!(parse->BList[b>>3]&(1<<(b&7))))
          {
            parse->BList[b>>3] |= (1<<(b&7));
            if(!(err = getdiskblock(xadMasterBase, parse->ai, j, (xadSTRPTR) parse->fh2, 1)) &&
            EndGetM32(parse->fh2->afs_Type) == T_SHORT)
            {
              if(EndGetM32(parse->fh2->afs_HeaderKey) != j)
                ++c;
              for(b = 0; b < ii->xii_SectorSize; b += 4)
                a += EndGetM32(((xadUINT8 *)parse->fh2) + b);
              if(!a)
                e = 0;
            }
          }
        }
        if(!e)
        {
          switch(EndGetM32(fh->afs_SecondaryType))
          {
          case ST_LINKFILE: case ST_LINKDIR:
            if((l = xadAllocVec(XADM sizeof(struct xadLink)-512+ii->xii_SectorSize, XADMEMF_CLEAR|XADMEMF_PUBLIC)))
            {
              xadCopyMem(XADM (xadPTR) fh, (xadPTR) &l->xl_Header, ii->xii_SectorSize);
              l->xl_Next = parse->LinkList;
              l->xl_Parent = parse->CurDir;
              parse->LinkList = l;
            }
            else
              err = XADERR_NOMEMORY;
            break;
          case ST_SOFTLINK:
            j = strlen((const char *)fh->afs_SoftLinkName)+1;
            if((fi = (struct xadFileInfo *) xadAllocObject(XADM XADOBJ_FILEINFO, XAD_OBJNAMESIZE,
            fh->afs_Name[0] + curnamesize+1+1+j,fh->afs_Comment[0] ? XAD_OBJCOMMENTSIZE :
            TAG_DONE, fh->afs_Comment[0]+1, TAG_DONE)))
            {
              fi->xfi_PrivateInfo = (xadPTR)(uintptr_t) j; /* store block number */
              str = fi->xfi_FileName;
              if(curnamesize)
              {
                xadCopyMem(XADM curdirname, str, curnamesize);
                str += curnamesize;
                *(str++) = '/';
              }
              xadCopyMem(XADM fh->afs_Name+1,str,fh->afs_Name[0]);
              fi->xfi_LinkName = str + fh->afs_Name[0] +1;
              fi->xfi_Flags |= XADFIF_DIRECTORY|XADFIF_LINK;
              xadCopyMem(XADM (xadPTR) fh->afs_SoftLinkName,fi->xfi_LinkName,j);
              if(fh->afs_Comment[0])
                xadCopyMem(XADM fh->afs_Comment+1,fi->xfi_Comment,fh->afs_Comment[0]);
              xadConvertDates(XADM XAD_DATEDATESTAMP, &fh->afs_Date, XAD_GETDATEXADDATE, &fi->xfi_Date, TAG_DONE);
              fi->xfi_Size = fi->xfi_CrunchSize = EndGetM32(fh->afs_Size);
              fi->xfi_Protection = EndGetM32(fh->afs_Protection);
              fi->xfi_OwnerUID = EndGetM32(fh->afs_OwnerInfo) >> 16;
              fi->xfi_OwnerGID = EndGetM32(fh->afs_OwnerInfo) & 0xFFFF;
              err = xadAddFileEntry(XADM fi, parse->ai, XAD_INSERTDIRSFIRST, XADTRUE, TAG_DONE);
            }
            else
              err = XADERR_NOMEMORY;
            break;
          case ST_USERDIR:
            if((str = (xadSTRPTR) xadAllocVec(XADM ii->xii_SectorSize, XADMEMF_PUBLIC)))
            {
              if((fi = (struct xadFileInfo *) xadAllocObject(XADM XADOBJ_FILEINFO, XAD_OBJNAMESIZE,
              fh->afs_Name[0] + curnamesize+1+1, fh->afs_Comment[0] ? XAD_OBJCOMMENTSIZE :
              TAG_DONE, fh->afs_Comment[0]+1, TAG_DONE)))
              {
                fi->xfi_PrivateInfo = str;
                xadCopyMem(XADM (xadPTR) fh, str, ii->xii_SectorSize); /* store the dir block */
                fi->xfi_Next = parse->DirList;
                parse->DirList = fi;
                str = fi->xfi_FileName;
                if(curnamesize)
                {
                  xadCopyMem(XADM curdirname, str, curnamesize);
                  str += curnamesize;
                  *(str++) = '/';
                }
                xadCopyMem(XADM fh->afs_Name+1,str,fh->afs_Name[0]);
                if(fh->afs_Comment[0])
                  xadCopyMem(XADM fh->afs_Comment+1,fi->xfi_Comment,fh->afs_Comment[0]);
                xadConvertDates(XADM XAD_DATEDATESTAMP, &fh->afs_Date, XAD_GETDATEXADDATE, &fi->xfi_Date, TAG_DONE);
                fi->xfi_Size = fi->xfi_CrunchSize = EndGetM32(fh->afs_Size);
                fi->xfi_Flags |= XADFIF_DIRECTORY;
                fi->xfi_Protection = EndGetM32(fh->afs_Protection);
                fi->xfi_OwnerUID = EndGetM32(fh->afs_OwnerInfo) >>16;
                fi->xfi_OwnerGID = EndGetM32(fh->afs_OwnerInfo) & 0xFFFF;
              }
              else
              {
                err = XADERR_NOMEMORY;
                xadFreeObjectA(XADM str, 0);
              }
            }
            else
              err = XADERR_NOMEMORY;
            break;
          case ST_FILE:
            if((fi = (struct xadFileInfo *) xadAllocObject(XADM  XADOBJ_FILEINFO, XAD_OBJNAMESIZE,
            fh->afs_Name[0] + curnamesize+1+1, fh->afs_Comment[0] ? XAD_OBJCOMMENTSIZE :
            TAG_DONE, fh->afs_Comment[0]+1, TAG_DONE)))
            {
              fi->xfi_PrivateInfo = (xadPTR)(uintptr_t) j; /* store block number */
              str = fi->xfi_FileName;
              if(curnamesize)
              {
                xadCopyMem(XADM curdirname, str, curnamesize);
                str += curnamesize;
                *(str++) = '/';
              }
              xadCopyMem(XADM fh->afs_Name+1,str,fh->afs_Name[0]);
              if(fh->afs_Comment[0])
                xadCopyMem(XADM fh->afs_Comment+1,fi->xfi_Comment,fh->afs_Comment[0]);
              xadConvertDates(XADM XAD_DATEDATESTAMP, &fh->afs_Date, XAD_GETDATEXADDATE, &fi->xfi_Date, TAG_DONE);
              fi->xfi_Size = fi->xfi_CrunchSize = EndGetM32(fh->afs_Size);
              fi->xfi_Protection = EndGetM32(fh->afs_Protection);
              fi->xfi_OwnerUID = EndGetM32(fh->afs_OwnerInfo) >> 16;
              fi->xfi_OwnerGID = EndGetM32(fh->afs_OwnerInfo) & 0xFFFF;
              fi->xfi_Flags |= XADFIF_EXTRACTONBUILD;
              err = xadAddFileEntryA(XADM fi, parse->ai, 0);
            }
            else
              err = XADERR_NOMEMORY;
            break;
          default: ++c;
          }
        }
        else
          ++c;
      } while(!c && !err && (j = EndGetM32(fh->afs_HashList)));
      parse->Corrupt += c;
    }
  }
  return err;
}

XADGETINFO(FSAmiga)
{
  xadINT32 err = 0;
  struct xadImageInfo *ii;
  xadSTRPTR b1;
  xadUINT32 i;

  if(ai->xai_InSize < 2048)
    return XADERR_FILESYSTEM;

  ii = ai->xai_ImageInfo;

  if((b1 = (xadSTRPTR) xadAllocVec(XADM (ii->xii_SectorSize*2)+((ii->xii_NumSectors+7)>>3),XADMEMF_PUBLIC|XADMEMF_CLEAR)))
  {
    if(!ii->xii_FirstSector)
    {
      if(!(err = getdiskblock(xadMasterBase, ai, 0, b1, 0)))
      {
        if(b1[0] != 'D' || b1[1] != 'O' || b1[2] != 'S' || b1[3] > 5)
          err = XADERR_FILESYSTEM;
        else if(b1[3] & 1)
        ai->xai_PrivateClient = (xadPTR) 1; /* is FFS filesystem */
      }
    }
    if(!err)
    {
      i = ii->xii_TotalSectors/2;
      if(i < ii->xii_FirstSector || i > ii->xii_FirstSector+ii->xii_NumSectors-1)
        err = XADERR_FILESYSTEM;
      else
      {
        if(!(err = getdiskblock(xadMasterBase, ai, i, b1, 1)))
        {
          if(EndGetM32(((struct RootBlock *) b1)->afs_Type) != T_SHORT
          || EndGetM32(((struct RootBlock *) b1)->afs_SecondaryType) != ST_ROOT)
          {
            i = 880; /* standard floppy, useful for wrong-size-images */
            if(i < ii->xii_FirstSector || i > ii->xii_FirstSector+ii->xii_NumSectors-1)
              err = XADERR_FILESYSTEM;
            else if(!(err = getdiskblock(xadMasterBase, ai, i, b1, 1)))
            {
              if(EndGetM32(((struct RootBlock *) b1)->afs_Type) != T_SHORT
              || EndGetM32(((struct RootBlock *) b1)->afs_SecondaryType) != ST_ROOT)
                err = XADERR_FILESYSTEM;
            }
          }

          if(!err)
          {
            struct DiskParseData pd;
            struct xadFileInfo *fi, *ofi, *par;
            struct xadLink *l;

            pd.Corrupt      = 0;
            pd.ai           = ai;
            pd.fh1          = (struct FileHeaderBlock *) b1;
            pd.fh2          = (struct FileHeaderBlock *) (b1+ii->xii_SectorSize);
            pd.BList        = b1+(ii->xii_SectorSize<<1);
            pd.BList[i>>3] |= (1<<(i&7));
            pd.DirList      = pd.CurDir = 0;
            pd.LinkList     = 0;
            pd.MasterBase   = xadMasterBase;

            if(!(err = parsedir(&pd))) /* parse root block */
            {
              while(!err && pd.DirList)
              {
                pd.CurDir = fi = pd.DirList;
                pd.DirList = fi->xfi_Next;
                xadCopyMem(XADM fi->xfi_PrivateInfo, b1, ii->xii_SectorSize);
                xadFreeObjectA(XADM fi->xfi_PrivateInfo, 0); /* free stored block */
                fi->xfi_PrivateInfo = (xadPTR)(uintptr_t) EndGetM32(pd.fh1->afs_HeaderKey); /* set blocknum */
                if(!(err = xadAddFileEntry(XADM fi, ai, XAD_INSERTDIRSFIRST, XADTRUE, TAG_DONE)))
                  err = parsedir(&pd);
              }
              while(!err && pd.LinkList)
              {
                l = pd.LinkList;
                pd.LinkList = l->xl_Next;
                xadCopyMem(XADM (xadPTR) &l->xl_Header, b1, ii->xii_SectorSize);
                par = l->xl_Parent;
                xadFreeObjectA(XADM l, 0);

                for(ofi = ai->xai_FileInfo; ofi; ofi=ofi->xfi_Next)
                {
                  if((xadUINT32)(uintptr_t) ofi->xfi_PrivateInfo == EndGetM32(pd.fh1->afs_LinkOriginal))
                    break;
                }
                if(ofi)
                {
                  xadUINT32 j, k;
                  xadSTRPTR str;

                  j = strlen((const char *)ofi->xfi_FileName)+1;
                  k = par ? strlen((const char *)par->xfi_FileName)+1 : 0;
                  if((fi = (struct xadFileInfo *) xadAllocObject(XADM  XADOBJ_FILEINFO,
                  XAD_OBJNAMESIZE, pd.fh1->afs_Name[0] + k+j+1, pd.fh1->afs_Comment[0] ?
                  XAD_OBJCOMMENTSIZE : TAG_DONE, pd.fh1->afs_Comment[0]+1, TAG_DONE)))
                  {
                    str = fi->xfi_FileName;
                    if(par)
                    {
                      xadCopyMem(XADM par->xfi_FileName, str, k);
                      str += k;
                      str[-1] = '/';
                    }
                    xadCopyMem(XADM pd.fh1->afs_Name+1,str,pd.fh1->afs_Name[0]);
                    fi->xfi_LinkName = str + pd.fh1->afs_Name[0] +1;
                    if(EndGetM32(pd.fh1->afs_SecondaryType) == ST_LINKDIR)
                      fi->xfi_Flags |= XADFIF_DIRECTORY;
                    fi->xfi_Flags |= XADFIF_LINK;
                    xadCopyMem(XADM ofi->xfi_FileName,fi->xfi_LinkName,j);
                    if(pd.fh1->afs_Comment[0])
                      xadCopyMem(XADM pd.fh1->afs_Comment+1,fi->xfi_Comment,pd.fh1->afs_Comment[0]);
                    xadConvertDates(XADM XAD_DATEDATESTAMP, &pd.fh1->afs_Date, XAD_GETDATEXADDATE, &fi->xfi_Date, TAG_DONE);
                    fi->xfi_Size = fi->xfi_CrunchSize = ofi->xfi_Size;
                    fi->xfi_Protection = EndGetM32(pd.fh1->afs_Protection);
                    fi->xfi_OwnerUID = EndGetM32(pd.fh1->afs_OwnerInfo) >> 16;
                    fi->xfi_OwnerGID = EndGetM32(pd.fh1->afs_OwnerInfo) & 0xFFFF;
                    err = xadAddFileEntry(XADM fi, ai, XAD_INSERTDIRSFIRST, XADTRUE, TAG_DONE);
                  }
                  else
                    err = XADERR_NOMEMORY;
                }
              }
            }

            while(pd.LinkList) /* free mem in case of error */
            {
              l = pd.LinkList;
              pd.LinkList = l->xl_Next;
              xadFreeObjectA(XADM l, 0);
            }
            while(pd.DirList) /* free mem in case of error */
            {
              fi = pd.DirList;
              pd.DirList = fi->xfi_Next;
              xadFreeObjectA(XADM fi->xfi_PrivateInfo, 0);
              xadFreeObjectA(XADM fi, 0);
            }

            if(pd.Corrupt)
              ai->xai_Flags |= XADAIF_FILECORRUPT;
          }
        }
      }
    }
    xadFreeObjectA(XADM b1,0);
  }
  else
    err = XADERR_NOMEMORY;

  if(err == XADERR_FILESYSTEM)
    ai->xai_PrivateClient = 0;

  return err;
}

XADUNARCHIVE(FSAmiga)
{
  xadINT32 err;
  xadSTRPTR buf;
  xadSize size, blksize;
  xadUINT32 blk;

  blksize = ai->xai_ImageInfo->xii_SectorSize;
  size = ai->xai_CurFile->xfi_Size;

  if((buf = (xadSTRPTR) xadAllocVec(XADM blksize*11, XADMEMF_PUBLIC)))
  {
    if(!(err = getdiskblock(xadMasterBase, ai, (xadUINT32)(uintptr_t) ai->xai_CurFile->xfi_PrivateInfo, buf, 1)))
    {
      struct FileExtensionBlock *fe;
      xadUINT8 *data;
      xadUINT32 usedblk = 0, blkcnt;

      fe = (struct FileExtensionBlock *) buf;
      data = (xadUINT8 *) buf+blksize; /* 10 blocks */
      blk = blkcnt = (xadUINT32)(blksize>>2)-56;

      if(!ai->xai_PrivateClient) /* OFS filesystem */
        blksize -= 6*4;
      do
      {
        if(usedblk == 10)
        {
          err = xadHookAccess(XADM XADAC_WRITE, 10*blksize, data, ai);
          size -= 10*blksize;
          usedblk = 0;
        }
        if(!blkcnt && !err)
        {
          if(!(err = getdiskblock(xadMasterBase, ai, EndGetM32(fe->afs_Extension), buf, 1)))
          {
            if(EndGetM32(fe->afs_Type) != T_LIST || EndGetM32(fe->afs_SecondaryType) != ST_FILE)
              err = XADERR_ILLEGALDATA;
          }
          blkcnt = blk;
        }
        if(!err)
        {
          xadSTRPTR a;
          a = (xadSTRPTR) data+(blksize*(usedblk++));
          --blkcnt;
          err = getdiskblock(xadMasterBase, ai, EndGetM32(fe->afs_BlockList[blkcnt]), a, 0);
          if(!ai->xai_PrivateClient && !err) /* OFS filesystem */
          {
            xadUINT32 i, j;
            for(i = j = 0; i < blksize+(4*6); i += 4)
              j += EndGetM32(a+i);
            if(j)
              err = XADERR_CHECKSUM;
            else /* copy buffer back */
              xadCopyMem(XADM a+6*4, a, blksize);
          }
        }
      } while(!err && size > blksize*usedblk);
      if(!err && size)
        err = xadHookAccess(XADM XADAC_WRITE, size, data, ai);
    }
    xadFreeObjectA(XADM buf, 0);
  }
  else
    err = XADERR_NOMEMORY;

  return err;
}

XADFREE(FSAmiga)
{
  ai->xai_PrivateClient = 0; /* clear entry buffer */
}

XADFIRSTCLIENT(FSAmiga) {
  XADNEXTCLIENT,
  XADCLIENT_VERSION,
  XADMASTERVERSION,
  FSAMIGA_VERSION,
  FSAMIGA_REVISION,
  0,
  XADCF_FILESYSTEM|XADCF_FREEFILEINFO,
  XADCID_FSAMIGA,
  "Amiga Standard FS",
  NULL,
  XADGETINFOP(FSAmiga),
  XADUNARCHIVEP(FSAmiga),
  XADFREEP(FSAmiga)
};

#undef XADNEXTCLIENT
#define XADNEXTCLIENT XADNEXTCLIENTNAME(FSAmiga)

#endif  /* XADMASTER_FS_AMIGA_C */
