/*
 *
 *    zooarc.c ©1995 by Matthias Meixner
 *
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos/dos.h>
#include <proto/dos.h>
#include <stddef.h>
#include <ios1.h>
#include <setjmp.h>
#include <math.h>

#define NO_XARS_PRAGMAS

#include <libraries/xpkarchive.h>
#include <libraries/xpkarchivesub.h>

/* I define this before turning on watchmem */

extern FILE *__firstfile;
FILE *fdopen(int fd,const char *mode)
{
   FILE *fp,**p;

   if(fp=malloc(sizeof(FILE))) {
      memset(fp,0,sizeof(FILE));

      fp->_file=fd;
      fp->_flag=_IORW | _IOFBF;

      for(p=&__firstfile;*p;p=&(*p)->_next);
      *p=fp;
   }
   return fp;
}
FILE *FHopen(BPTR fh)
{
   struct UFB *ufb=NULL,*n;
   FILE *fp;
   int indx=0;
   int mode;

   mode=UFB_RA|UFB_WA|UFB_NC;


   if(__ufbs) {
      ufb=__ufbs;

      while(ufb->ufbflg && ufb->ufbnxt) {
         ufb=ufb->ufbnxt;
         indx++;
      }
   }

   if(ufb && ufb->ufbflg==0) { /* found empty entry */
      ufb->ufbflg=mode;
      ufb->ufbfh=fh;
      ufb->ufbfn=NULL;

      if(fp=fdopen(indx,"r+")) {
         return fp;
      } else {
         ufb->ufbflg=0;
         return NULL;
      }
   } else {

      if(!(n=malloc(sizeof(struct UFB)))) return NULL;

      n->ufbnxt=NULL;
      n->ufbflg=mode;
      n->ufbfh=fh;
      n->ufbfn=NULL;

      if(!ufb) {
         __ufbs=n;
         __nufbs=1;
         if(fp=fdopen(0,"r+")) {
            return fp;
         } else {
            __ufbs=NULL;
            __nufbs=0;
            free(n);
            return NULL;
         }

      } else {
         ufb->ufbnxt=n;
         __nufbs++;
         if(fp=fdopen(indx+1,"r+")) {
            return fp;
         } else {
            ufb->ufbnxt=NULL;
            __nufbs--;
            free(n);
            return NULL;
         }
      }
   }
   return NULL;
}



#include "zooarc.h"
#include "crc.h"
#include "ar.h"
#include "lzh.h"
#include "zoomem.h"

#if MWDEBUG
char *__MWLogName = "KCON:0/0/639/199/MemLib/AUTO/CLOSE/WAIT";
#endif

char *out_buf_adr,*in_buf_adr;

#if 0 /* You must not declare these libs here, or the    */
      /* autoinitializing mechanism of SAS/C will break  */
      /* (constructors use them, before UserLibInit gets */
      /*  called and therefore constructors use          */
      /*  variables, that are not initialized)           */
struct ExecBase *SysBase;
struct DosLibrary *DOSBase;
#endif

struct Library *XpkArchiveBase;

void unpack_struct(char *to,const char *src,const char *conv)
{
   int a;

   for(;*conv;conv++) {
      if(*conv>0) {
         for(a=*conv;a;a--) *to++=*src++;
      } else {
         if((long)to & 1) *to++=0;

         a=-*conv; /* Padding to size of short,long,double */
         if(a==3) *to++=0;
         else if(a==5) {*to++=0;*to++=0;*to++=0;}
         else if(a==6) {*to++=0;*to++=0;}
         else if(a==7) *to++=0;

         while(a) to[--a]=*src++;
         to-=*conv;
      }
   }
}
void pack_struct(char *to,const char *src,const char *conv)
{
   int a;

   for(;*conv;conv++) {
      if(*conv>0) {
         for(a=*conv;a;a--) *to++=*src++;
      } else {
         if((long)src & 1) src++;

         a=-*conv; /* Padding to size of short,long,double */
         if(a==3) src++;
         else if(a==5) src+=3;
         else if(a==6) src+=2;
         else if(a==7) src++;

         while(a) to[--a]=*src++;
         to-=*conv;
      }
   }
}

char buffer[MAXDIRENT_SIZE];
int  bufsize;
struct zoo_header zoohead,newhead={"ZOO 2.10 Archive.",ZOO_TAG,42,-42,2,0,1,0,0,0x80};
struct direntry dir,nulldir={ZOO_TAG,2};

HEADER_CONVTAB(convhead);
DIRENT_CONVTAB1(convdir1);
DIRENT_CONVTAB2(convdir2);

char conv_short[]={-2,0};
char conv_long[]={-4,0};

char legal[] =
"tabcdefghijklmnopqrs_uvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ@^`{}~!#$%&'()-";

FILE *arcfile=NULL;

jmp_buf catch_pos;

long nullentry;
#if 0
long lastentry;
#endif

#define ReadBuffer(size) ((bufsize=(size)),!fread(buffer,(size),1,arcfile))
LONG WriteBuffer(void)
{
   short crc;

   buffer[DirOffset_dir_crc]=0;
   buffer[DirOffset_dir_crc+1]=0;

   crccode=0;
   addbfcrc(buffer,bufsize);

   crc=crccode;

   pack_struct(buffer+DirOffset_dir_crc,(char *)&crc,conv_short);

   return !fwrite(buffer,bufsize,1,arcfile);
}

LONG ReadHeader(struct zoo_header *head)
{
   LONG r;
   r=fread(buffer,1,HEADER_SIZE,arcfile);
   if(r<SMALLHEADER_SIZE) return -1;

   unpack_struct((char *)head,buffer,convhead);
   return r;
}
LONG ReadDirent(FILE *arcfile,LONG pos,struct direntry *dir)
{
   LONG r;
   char *p;
   int a;

   if(fseek(arcfile,pos,SEEK_SET)) return -1;

   r=fread(buffer,1,MAXDIRENT_SIZE,arcfile);
   if(r<DIRENTSIZE_TYPE1) return -1;

   memset(&dir->var_dir_len,0,sizeof(struct direntry)-offsetof(struct direntry,var_dir_len));
   unpack_struct((char *)dir,buffer,convdir1);

   if(dir->type==2) {
      if(r<DIRENTSIZE_TYPE2+dir->var_dir_len) return -1;

      p=buffer+DirOffset_lfname;

      for(a=0;a<dir->namlen;a++) {
         dir->lfname[a]=*p++;
      }

      for(a=0;a<dir->dirlen;a++) {
         dir->dirname[a]=*p++;
      }
      unpack_struct((char *)&dir->system_id,p,convdir2);

      buffer[DirOffset_dir_crc]=buffer[DirOffset_dir_crc+1]=0;

      crccode=0;
      addbfcrc(buffer,DIRENTSIZE_TYPE2+dir->var_dir_len);
      dir->dir_crc-=crccode;

      if(dir->var_dir_len==0) dir->namlen=0;
      if(dir->var_dir_len<=1) dir->dirlen=0;
      if(dir->var_dir_len<=dir->namlen+dir->dirlen+2) dir->system_id=0;
      if(dir->var_dir_len<=dir->namlen+dir->dirlen+4) dir->fattr=0;
      if(dir->var_dir_len<=dir->namlen+dir->dirlen+7) dir->vflag=dir->version_no=0;

   }

   return r;
}
LONG WriteDirent(struct direntry *dir)
{
   char *p;
   short crc;

   dir->dir_crc=0;

   pack_struct(buffer,(char *)dir,convdir1);

   p=buffer+DirOffset_lfname;
   if(dir->namlen) {
      memcpy(p,dir->lfname,dir->namlen);
      p+=dir->namlen;
   }

   if(dir->dirlen) {
      memcpy(p,dir->dirname,dir->dirlen);
      p+=dir->dirlen;
   }

   pack_struct(p,(char *)&dir->system_id,convdir2);

   bufsize=DIRENTSIZE_TYPE2+dir->var_dir_len;

   crccode=0;
   addbfcrc(buffer,bufsize);

   crc=crccode;

   pack_struct(buffer+DirOffset_dir_crc,(char *)&crc,conv_short);

   if(!fwrite(buffer,bufsize,1,arcfile)) return -1;
   return 0;
}

long Amiga2IXMode(long mode)
{
   mode^=0xf;
   mode&=0xe;
   return mode<<5;
}
long IX2AmigaMode(long mode)
{
   mode&=0x1FF;
   mode=((mode & 7) | ((mode>>3) & 7) | ((mode>>6) & 7))<<1;

   if(mode & 4) mode|=1;

   mode^=0xf;
   return mode & 0xff;
}

struct ZooLock *Entry2Lock(struct direntry *dir,long pos)
{
   struct ZooLock *lock;
   struct XarFileData *data;
   char *p;
   int len;

   if(!(lock=calloc(1,sizeof(struct ZooLock)))) return NULL;

   len=(dir->namlen?dir->namlen-1:strlen(dir->fname))+1;
   if(dir->dirlen) len+=dir->dirlen;

   if(!(p=malloc(len))) return NULL;

   *p=0;

   if(dir->dirlen) {
      strcpy(p,dir->dirname);
      strcat(p,"/");
   }

   if(dir->namlen) strcat(p,dir->lfname); else strcat(p,dir->fname);
   lock->Lock.FileName=p;

   if(dir->comment) {
      if(!(p=malloc(dir->cmt_size))) return NULL;
      if(fseek(arcfile,dir->comment,SEEK_SET)) {
         free(p);
         p=NULL;
      }
      if(p && fread(p,dir->cmt_size,1,arcfile)==1) {
         lock->Lock.FileNote=p;
      } else free(p);
   }

   data=&lock->Lock.FileData;
   data->Flags=dir->deleted?XARFF_Deleted:0;
   data->Generation=dir->version_no;

   switch(dir->system_id) {
      case SYSID_NIX:      data->SysID=SYS_UNIX;
                           break;
      case SYSID_MS:       data->SysID=SYS_MS_DOS;
                           break;
      case SYSID_PORTABLE: data->SysID=SYS_UNIX;
                           break;
   }

   data->Filesize=dir->org_size;
   data->Time.Year=(dir->date>>9)+80;
   data->Time.Month=(dir->date>>5)&15;
   data->Time.Day=dir->date&31;
   data->Time.Hour=dir->time>>11;
   data->Time.Min=(dir->time>>5)&63;
   data->Time.Sec=2*(dir->time & 31);
   data->CRC=dir->file_crc;

   data->Protection=IX2AmigaMode(dir->fattr);

   data->ValidMask=XAR_ValidGeneration|XAR_ValidSysID|XAR_ValidTime
                   |XAR_ValidCRC|XAR_ValidProtect;

   data->CompressedSize=dir->size_now;
   data->Type=XAR_TypeFile;

   lock->DirentPos=pos;
   lock->DirentSize=dir->type==2?DIRENTSIZE_TYPE2+dir->var_dir_len:DIRENTSIZE_TYPE1;

   lock->DataPos=dir->offset;
   lock->Method=dir->packing_method;
   lock->GFlag=dir->vflag;

   return lock;
}

#if 0
LONG UpdateNext(struct ZooLock *lock,long next)
{
   short crc;

   if(fseek(arcfile,lock->DirentPos,SEEK_SET)
      || !fread(buffer,lock->DirentSize,1,arcfile)) return -1;

   pack_struct(buffer+DirOffset_next,&next,conv_long);

   crccode=0;
   addbfcrc(buffer,lock->DirentSize);

   crc=crccode;
   pack_struct(buffer+DirOffset_dir_crc,&crc,conv_short);

   if(!fwrite(buffer,lock->DirentPos,SEEK_SET)) return -1;
}
#endif

int ToUpper(int c)
{
   if('a'<=c && c<='z') return c+'A'-'a';
   if(224<=c && c<=254) return c+192-224;
   return c;
}
int StringICmp(const UBYTE *a,const UBYTE *b)
{
   while(*a && ToUpper(*a)==ToUpper(*b)) {a++;b++;}

   return ToUpper(*a)-ToUpper(*b);
}

void SetFilename(struct direntry *dir,char *filename)
{
   char *dirname,*p;
   int len,i;

   /* split path into filename and directory */

   dirname=filename;
   filename=strrchr(dirname,'/');
   if(filename) filename++; else filename=dirname;
   len=filename-dirname;
   for(p=dirname;*p=='/';p++);
   if(p!=filename) len--;

   if(len>255) len=255;
   if(len) stccpy(dir->dirname,dirname,len+1); else dir->dirname[0]=0;
   stccpy(dir->lfname,filename,256);

   dir->namlen=strlen(dir->lfname);
   if(dir->namlen) dir->namlen++;

   dir->dirlen=strlen(dir->dirname);
   if(dir->dirlen) dir->dirlen++;


   /* Make a MS-DOS compatible filename */

   p=strrchr(filename,'.');

   len=p?p-filename:strlen(filename);

   if(len>8) len=8;
   stccpy(dir->fname,filename,len+1);
   if(p) strncat(dir->fname,p,4);

   /* limit to legal characters */

   i=0;
   for(p=dir->fname+strlen(dir->fname)-1;p>=dir->fname;p--) {
      if(strchr(legal,*p)==NULL && (*p!='.' || i)) {
         *p=legal[((unsigned char)*p) % sizeof(legal)];
      }
      if(*p=='.') i=1;
   }

   /* long filename only if needed */

   if(!strcmp(dir->fname,dir->lfname)) {
      dir->lfname[0]=0;
      dir->namlen=0;
   }

   dir->var_dir_len=10+dir->namlen+dir->dirlen;
}

long CopyData(FILE * from, FILE * to,long size)
{
   UBYTE *mem;
   ULONG memsize;
   long err=XARERR_NO_ERROR;

   if(!size) return XARERR_NO_ERROR;

   memsize=size;

   while(memsize && (mem=malloc(memsize))==NULL) memsize<<=1;

   if(!memsize) return XARERR_NO_MEMORY;


   while(size>=memsize) {
      if(!fread(mem,memsize,1,from)) {err=XARERR_READ_ERROR;break;}
      if(!fwrite(mem,memsize,1,to)) {err=XARERR_WRITE_ERROR;break;}
      size-=memsize;
   }

   if(size && size<memsize) {
      if(!fread(mem,size,1,from)) err=XARERR_READ_ERROR;
      if(!fwrite(mem,size,1,to)) err=XARERR_WRITE_ERROR;
   }

   free(mem);
   return err;
}

#if 0 /* Nothing to initialize */
int __saveds __asm __UserLibInit(register __a6 struct Library *thislib)
{
   SysBase=(struct ExecBase *)*(ULONG *)4;
   DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",0);
   if(!DOSBase) return 1;
   return 0;
}
void __saveds __asm __UserLibCleanup(register __a6 struct Library *thislib)
{
   CloseLibrary((struct Library *)DOSBase);
}
#endif

struct XarSubInfo __saveds * XarsArchiverInfo(void)
{
#define A3000 XPKMF_A3000SPEED

   static XMINFO DummyMode2 = {
      NULL,   /* next */
      100,    /* upto */
      A3000,  /* flags */
      0,      /* packmem */
      0,      /* unpackmem */
      500,    /* packspeed,   K/sec */
      500,    /* unpackspeed, K/sec */
      0,      /* ratio,      *0.1% */
      0,      /* reserved */
      "lh5" /* description */
   };

   static XMINFO DummyMode1 = {
      &DummyMode2,   /* next */
      66,    /* upto */
      A3000,  /* flags */
      0,      /* packmem */
      0,      /* unpackmem */
      500,    /* packspeed,   K/sec */
      500,    /* unpackspeed, K/sec */
      0,      /* ratio,      *0.1% */
      0,      /* reserved */
      "lzw" /* description */
   };

   static XMINFO DummyMode0 = {
      &DummyMode1,   /* next */
      33,    /* upto */
      A3000,  /* flags */
      0,      /* packmem */
      0,      /* unpackmem */
      500,    /* packspeed,   K/sec */
      500,    /* unpackspeed, K/sec */
      0,      /* ratio,      *0.1% */
      0,      /* reserved */
      "COPY" /* description */
   };

   static struct XarSubInfo dummyInfo=
   {
      sizeof(struct XarSubInfo),       /* sizeof()                      */
      1,0,                             /* Version+Revision              */
      0,                               /* Version of xpkarchive.library */
      {
         "Zoo","Zoo 1.0","Zoo archiver version 1.0",
         "Zoo",                         /* ArchiveType                   */
         "ZOO",                         /* ID                            */
         100,                             /* Default mode                  */
         "zoo",                         /* extension                     */

         XARIF_PK_MEM | XARIF_UP_MEM
            | XARIF_CHECKING | XARIF_MODES
            | XARIF_USES_GENERATIONS        /* Flags                         */
      },
      &DummyMode0,                      /* Modes                         */
      {"????????????????????\334\247\304\375",NULL},                /* Identify                      */
   };

   return &dummyInfo;
}

LONG __saveds __asm XarsCreateArchive(REG __a0 struct XarSubHandle *arc)
/* Create an empty archive */
{
   XpkArchiveBase=arc->MasterLib;

   if(!(out_buf_adr=malloc(MEM_BLOCK_SIZE))) return XARERR_NO_MEMORY;
   in_buf_adr = out_buf_adr + OUT_BUF_SIZE + (FUDGE/2);

   if(!(arcfile=FHopen(arc->ArcFile))) return XARERR_NO_MEMORY;

   zoohead=newhead;

   pack_struct(buffer,(char *)&newhead,convhead);
   nulldir.var_dir_len=0;

   if(!fwrite(buffer,HEADER_SIZE,1,arcfile) || WriteDirent(&nulldir))
      return XARERR_WRITE_ERROR;

   nullentry=HEADER_SIZE;
#if 0
   lastentry=0;
#endif

   /* Important: Flush all internal buffers, or you might get problems
      with XarsCopyFile. If you try to copy a file, that was just
      added, it could happen that parts of the informations are still in
      the internal buffer, and since XarsCopyFile just copies the
      contents of the file, it could copy invalid data. The reason for
      this behaviour is, that it has no access to the internal buffers
      of the 'arcfile' of the src-archive !!! */

   if(fflush(arcfile)==EOF) return XARERR_WRITE_ERROR;

   return XARERR_NO_ERROR;
}
LONG __saveds __asm XarsOpenArchive(REG __a0 struct XarSubHandle *arc)
{
   long next;

   struct ZooLock *lock,*l;

   XpkArchiveBase=arc->MasterLib;

   if(!(out_buf_adr=malloc(MEM_BLOCK_SIZE))) return XARERR_NO_MEMORY;
   in_buf_adr = out_buf_adr + OUT_BUF_SIZE + (FUDGE/2);

   if(!(arcfile=FHopen(arc->ArcFile))) return XARERR_NO_MEMORY;

   if(ReadHeader(&zoohead)<0) return XARERR_UNKNOWN_TYPE;

   if(zoohead.zoo_tag!=ZOO_TAG
      || zoohead.zoo_start!=-zoohead.zoo_minus)
                      return XARERR_UNKNOWN_TYPE;

   for(next=zoohead.zoo_start;next;next=dir.next) {

      if(ReadDirent(arcfile,next,&dir)<0) {
         arc->Flags|=XARAF_Corrupt;
         return XARWARN_ARCHIVE_CORRUPT;
      }

      if(dir.next) {
#if 0
         lastentry=next;     /* Position of last valid direnty */
#endif
         nullentry=dir.next; /* Position of null-direntry */

         lock=Entry2Lock(&dir,next);
         lock->Lock.Arc=arc;

         for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;
               l->Lock.Node.mln_Succ;
               l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

            if(l->DirentPos==lock->DirentPos) {
               arc->Flags|=XARAF_Corrupt;
               free(lock);
               return XARWARN_ARCHIVE_CORRUPT;
            }
         }

         AddTail((struct List *)&arc->ArcLocks,(struct Node *)lock);
      }
   }

   return XARERR_NO_ERROR;
}
LONG __saveds __asm XarsCloseArchive(REG __a0 struct XarSubHandle *arc)
{

   struct ZooLock *lock;

   while(lock=(struct ZooLock *)RemHead((struct List *)&arc->ArcLocks)) {
      if(lock->Lock.FileName) free(lock->Lock.FileName);
      if(lock->Lock.FileNote) free(lock->Lock.FileNote);

      free(lock);
   }

   free(out_buf_adr);
   free_encode();
   free_huf();
   free_lzc();
   free_lzd();

   /* fflush() should not be needed, since all functions that modify
      the archive do their fflush() themselves */

   if(fflush(arcfile)==EOF) return XARERR_WRITE_ERROR;
   return XARERR_NO_ERROR;
}

LONG __saveds __asm XarsAddFile(REG __a0 struct XarSubHandle *arc,
   REG __a1 struct XarSubIO *xio,
   REG __a2 struct XarSubLock **lock)
{
   long err;
   struct XarFileData *data;
   struct ZooLock *newlock=NULL,*l;
   long newnull;
   struct XarProgress xprog={"Zoo","Zoo V2.1","adding","added"};

   if(!xio->IOMem) return XARERR_INPUT_NOT_SUPPORTED;

   if(err=setjmp(catch_pos)) {
      if(newlock) {
         if(newlock->Lock.FileName) free(newlock->Lock.FileName);
         if(newlock->Lock.FileNote) free(newlock->Lock.FileNote);
         free(newlock);
      }
      return err; /* Mark position for error Handling   */
                  /* If err !=0, then an error happened */
   }

   newlock=malloc(sizeof(struct ZooLock));

   *newlock=**(struct ZooLock **)lock;

   newlock->Lock.FileName=strdup((*lock)->FileName);
   newlock->Lock.FileNote=(*lock)->FileNote?strdup((*lock)->FileNote):NULL;

   if(!newlock->Lock.FileName || ((*lock)->FileNote && !newlock->Lock.FileNote)) {
      if(newlock->Lock.FileNote) free(newlock->Lock.FileNote);
      if(newlock->Lock.FileName) free(newlock->Lock.FileName);
      free(newlock);
      return XARERR_NO_MEMORY;
   }

   SetFilename(&dir,newlock->Lock.FileName);

   /* write a nullentry of the size of the new directory-entry.
      This keeps the archive valid, if an error occurs and
      reserves enough memory for the real directory-entre */

   if(fseek(arcfile,nullentry,SEEK_SET)) Error(XARERR_WRITE_ERROR);

   nulldir.var_dir_len=dir.var_dir_len;
   if(WriteDirent(&nulldir)) Error(XARERR_WRITE_ERROR);

   if(xio->PackMode<=33) dir.packing_method=0;
   else if(xio->PackMode<=66) dir.packing_method=1;
   else dir.packing_method=2;

   if(newlock->Lock.FileNote) {
      dir.comment=ftell(arcfile);
      dir.cmt_size=strlen(newlock->Lock.FileNote)+1;

      if(!fwrite(newlock->Lock.FileNote,dir.cmt_size,1,arcfile)) {
            Error(XARERR_WRITE_ERROR);
      }
   } else {
      dir.comment=dir.cmt_size=0;
   }

   dir.offset=ftell(arcfile)+5;

   if(dir.packing_method==0) {
      XarProgress(XARPROG_START,xio,&xprog,0,0);
      if(!fwrite("@)#(",5,1,arcfile) || !fwrite(xio->IOMem,xio->Size,1,arcfile)) {
         XarProgress(XARPROG_END,xio,&xprog,0,0);
         Error(XARERR_WRITE_ERROR);
      }
      XarProgress(XARPROG_END,xio,&xprog,xio->Size,xio->Size);

      dir.org_size=dir.size_now=xio->Size;
      dir.major_ver=1;
      dir.minor_ver=0;
   } else if(dir.packing_method==1) {
      XarProgress(XARPROG_START,xio,&xprog,0,0);
      if(!fwrite("@)#(",5,1,arcfile) || lzc(xio)) {
         XarProgress(XARPROG_END,xio,&xprog,0,0);
         Error(XARERR_WRITE_ERROR);
      }

      dir.org_size=xio->Size;
      dir.size_now=ftell(arcfile)-dir.offset;

      if(dir.size_now>dir.org_size) {
         dir.packing_method=0;
         if(fseek(arcfile,dir.offset-5,SEEK_SET)) Error(XARERR_WRITE_ERROR);
         if(!fwrite("@)#(",5,1,arcfile) || !fwrite(xio->IOMem,xio->Size,1,arcfile)) {
            XarProgress(XARPROG_END,xio,&xprog,0,0);
            Error(XARERR_WRITE_ERROR);
         }
         XarProgress(XARPROG_END,xio,&xprog,xio->Size,xio->Size);

         dir.org_size=dir.size_now=xio->Size;
         dir.major_ver=1;
         dir.minor_ver=0;
      } else {
         XarProgress(XARPROG_END,xio,&xprog,dir.org_size,dir.size_now);
         dir.major_ver=1;
         dir.minor_ver=0;
      }
   } else if(dir.packing_method==2) {
      XarProgress(XARPROG_START,xio,&xprog,0,0);
      if(!fwrite("@)#(",5,1,arcfile)) {
         XarProgress(XARPROG_END,xio,&xprog,0,0);
         Error(XARERR_WRITE_ERROR);
      }

      encode(xio);
      if(ferror(arcfile)) Error(XARERR_WRITE_ERROR);

      dir.org_size=xio->Size;
      dir.size_now=ftell(arcfile)-dir.offset;

      if(dir.size_now>dir.org_size) {
         dir.packing_method=0;
         if(fseek(arcfile,dir.offset-5,SEEK_SET)) Error(XARERR_WRITE_ERROR);
         if(!fwrite("@)#(",5,1,arcfile) || !fwrite(xio->IOMem,xio->Size,1,arcfile)) {
            XarProgress(XARPROG_END,xio,&xprog,0,0);
            Error(XARERR_WRITE_ERROR);
         }
         XarProgress(XARPROG_END,xio,&xprog,xio->Size,xio->Size);

         dir.org_size=dir.size_now=xio->Size;
         dir.major_ver=1;
         dir.minor_ver=0;
      } else {
         XarProgress(XARPROG_END,xio,&xprog,dir.org_size,dir.size_now);
         dir.major_ver=2;
         dir.minor_ver=1;
      }
   }

   newnull=ftell(arcfile);

   /* New null-entry */

   nulldir.var_dir_len=0;
   if(WriteDirent(&nulldir)) Error(XARERR_WRITE_ERROR);

   data=&(*lock)->FileData;
   dir.zoo_tag=ZOO_TAG;
   dir.type=2;
   dir.next=newnull;
   dir.date=(data->Time.Year-80<<9)+(data->Time.Month<<5)+data->Time.Day;
   dir.time=(data->Time.Hour<<11)+(data->Time.Min<<5)+data->Time.Sec/2;

   crccode=0;
   addbfcrc(xio->IOMem,xio->Size);
   dir.file_crc=crccode;

   dir.deleted=0;
   dir.struc=0;

   dir.tz=NO_TZ;
   dir.system_id=SYSID_NIX;
   dir.fattr=(1<<22)+Amiga2IXMode(newlock->Lock.FileData.Protection);

   dir.vflag=zoohead.vdata;

   for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;l->Lock.Node.mln_Succ;
         l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

      if(!XarsCmpFilename(l->Lock.FileName,newlock->Lock.FileName)) {
         dir.vflag=l->GFlag;
         if(dir.vflag & VFL_LAST) break;
      }
   }

   if(dir.vflag & VFL_ON) {
      dir.vflag|=VFL_LAST;
      dir.version_no=newlock->Lock.FileData.Generation;
   } else {
      dir.version_no=0;
      newlock->Lock.FileData.Generation=0;
   }

   /* WriteDirent initializes dir.dir_crc ! */

   newlock->DirentPos=nullentry;
   newlock->DirentSize=DIRENTSIZE_TYPE2+dir.var_dir_len;
   newlock->DataPos=dir.offset;
   newlock->Method=dir.packing_method;
   newlock->GFlag=dir.vflag;
   newlock->Lock.FileData.CompressedSize=dir.size_now;

   if(fseek(arcfile,nullentry,SEEK_SET) || WriteDirent(&dir)) return XARERR_WRITE_ERROR;

   AddTail((struct List *)&arc->ArcLocks,(struct Node *)newlock);
   *lock=(struct XarSubLock *)newlock;

#if 0
   lastentry=nullentry;
#endif
   nullentry=newnull;


   /* Update generations and handle generation-limits */

   for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;l->Lock.Node.mln_Succ;
         l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

      /* Find files with same filename */

      if(l!=newlock && !XarsCmpFilename(l->Lock.FileName,newlock->Lock.FileName)) {
         UBYTE genlim;

         /* does this file have generations ? */

         if(newlock->GFlag & VFL_ON) {

            if(zoohead.vdata & VFL_ON) genlim=min(l->GFlag & VFL_GEN,zoohead.vdata & VFL_GEN);
            genlim=l->GFlag & VFL_GEN;

            if(genlim && newlock->Lock.FileData.Generation-l->Lock.FileData.Generation
                  >=genlim) {

               /* Delete too old files */

               if(fseek(arcfile,l->DirentPos,SEEK_SET)
                  || ReadBuffer(l->DirentSize)) return XARERR_WRITE_ERROR;

               buffer[DirOffset_deleted]=1;

               if(fseek(arcfile,l->DirentPos,SEEK_SET)
                  || WriteBuffer()) return XARERR_WRITE_ERROR;
               l->Lock.FileData.Flags|=XARFF_Deleted;
            } else {
               int FlagPos;

               /* Remove "last-flag" */

               if(fseek(arcfile,l->DirentPos,SEEK_SET)
                  || ReadBuffer(l->DirentSize)) return XARERR_WRITE_ERROR;

               if(buffer[DirOffset_type]==2) {
                  FlagPos=DIRENTSIZE_TYPE2+buffer[DirOffset_dirlen]+buffer[DirOffset_namlen]+7;

                  buffer[FlagPos]&=~VFL_LAST;

                  if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || WriteBuffer()) return XARERR_WRITE_ERROR;
               }
            }
         } else {

            /* No generations: delete all other files with the same filename */

            if(fseek(arcfile,l->DirentPos,SEEK_SET)
                  || ReadBuffer(l->DirentSize)) return XARERR_WRITE_ERROR;

            buffer[DirOffset_deleted]=1;

            if(fseek(arcfile,l->DirentPos,SEEK_SET)
                  || WriteBuffer()) return XARERR_WRITE_ERROR;
            l->Lock.FileData.Flags|=XARFF_Deleted;
         }
      }
   }

   /* Important: Flush all internal buffers, or you might get problems
      with XarsCopyFile. If you try to copy a file, that was just
      added, it could happen that parts of the informations are still in
      the internal buffer, and since XarsCopyFile just copies the
      contents of the file, it could copy invalid data. The reason for
      this behaviour is, that it has no access to the internal buffers
      of the 'arcfile' of the src-archive !!! */

   if(fflush(arcfile)==EOF) return XARERR_WRITE_ERROR;

   return XARERR_NO_ERROR;
}
LONG __saveds __asm XarsAddDir(REG __a0 struct XarSubHandle *arc,
   REG __a1 struct XarSubIO *xio,
   REG __a2 struct XarSubLock **lock)
{
   return XARERR_OPERATION_NOT_SUPPORTED;
}
LONG __saveds __asm XarsExtractFile(REG __a0 struct XarSubLock *lock,
   REG __a1 struct XarSubIO *xio)
{
   long spos;
   long bytes_decoded=0;
   int n;
   long err;
   char buf[5];
   struct XarProgress xprog={"Zoo","Zoo V2.1","extracting","extracted"};

   if(err=setjmp(catch_pos)) return err; /* Mark position for error Handling   */
                                         /* If err !=0, then an error happened */

   if(xio->IOMem==0) return XARERR_OUTPUT_NOT_SUPPORTED;

   if(fseek(arcfile,((struct ZooLock *)lock)->DataPos-5,SEEK_SET))
                     return XARERR_READ_ERROR;

   if(!fread(buf,5,1,arcfile)) return XARERR_READ_ERROR;

   if(strcmp(buf,"@)#(")) {
      lock->Arc->Flags|=XARAF_Corrupt;
      return XARERR_ARCHIVE_CORRUPT;
   }

   spos=ftell(arcfile);

   if(((struct ZooLock *)lock)->Method==0) {
      int n;

      XarProgress(XARPROG_START,xio,&xprog,0,0);
      n=fread(xio->IOMem,lock->FileData.Filesize,1,arcfile);
      XarProgress(XARPROG_END,xio,&xprog,lock->FileData.Filesize,lock->FileData.Filesize);
      if(!n) return XARERR_READ_ERROR;
   } else if(((struct ZooLock *)lock)->Method==1) {

      XarProgress(XARPROG_START,xio,&xprog,0,0);
      err=lzd(xio);
      XarProgress(XARPROG_END,xio,&xprog,lock->FileData.Filesize,lock->FileData.Filesize);

      if(err>=XARERROR_LEVEL) return err;

   } else if(((struct ZooLock *)lock)->Method==2) {
      LONG size;
      extern int decoded;

      size=xio->Size;

      XarProgress(XARPROG_START,xio,&xprog,0,0);

      decode_start();
      while (!decoded && size>0) {

         n = decode((uint) DICSIZ, (uchar *)out_buf_adr); /* n = count of chars decoded */
         size-=n;
         if(size>=0) memcpy(xio->IOMem+bytes_decoded,out_buf_adr,n);
         bytes_decoded += n;

         XarProgress(XARPROG_MID,xio,&xprog,bytes_decoded,ftell(arcfile)-spos);
      }
      XarProgress(XARPROG_END,xio,&xprog,bytes_decoded,ftell(arcfile));
      if(!decoded) return XARERR_READ_ERROR;
   }
   crccode=0;
   addbfcrc(xio->IOMem,xio->Size);
   if(crccode!=lock->FileData.CRC) return XARWARN_CRC_FAILED;

   return XARERR_NO_ERROR;
}
LONG __saveds __asm XarsDeleteFile(REG __a0 struct XarSubLock *lock)
{

   if(fseek(arcfile,((struct ZooLock *)lock)->DirentPos,SEEK_SET)
      || ReadBuffer(((struct ZooLock *)lock)->DirentSize)) return XARERR_WRITE_ERROR;

   buffer[DirOffset_deleted]=1;

   if(fseek(arcfile,((struct ZooLock *)lock)->DirentPos,SEEK_SET)
      || WriteBuffer()) return XARERR_WRITE_ERROR;

   /* Important: Flush all internal buffers, or you might get problems
      with XarsCopyFile. If you try to copy a file, that was just
      added, it could happen that parts of the informations are still in
      the internal buffer, and since XarsCopyFile just copies the
      contents of the file, it could copy invalid data. The reason for
      this behaviour is, that it has no access to the internal buffers
      of the 'arcfile' of the src-archive !!! */

   if(fflush(arcfile)==EOF) return XARERR_WRITE_ERROR;

   lock->FileData.Flags|=XARFF_Deleted;

   return XARERR_NO_ERROR;
}
LONG __saveds __asm XarsUndeleteFile(REG __a0 struct XarSubLock *lock)
{

   if(fseek(arcfile,((struct ZooLock *)lock)->DirentPos,SEEK_SET)
      || ReadBuffer(((struct ZooLock *)lock)->DirentSize)) return XARERR_WRITE_ERROR;

   buffer[DirOffset_deleted]=0;

   if(fseek(arcfile,((struct ZooLock *)lock)->DirentPos,SEEK_SET)
      || WriteBuffer()) return XARERR_WRITE_ERROR;

   /* Important: Flush all internal buffers, or you might get problems
      with XarsCopyFile. If you try to copy a file, that was just
      added, it could happen that parts of the informations are still in
      the internal buffer, and since XarsCopyFile just copies the
      contents of the file, it could copy invalid data. The reason for
      this behaviour is, that it has no access to the internal buffers
      of the 'arcfile' of the src-archive !!! */

   if(fflush(arcfile)==EOF) return XARERR_WRITE_ERROR;

   lock->FileData.Flags&=~XARFF_Deleted;

   return XARERR_NO_ERROR;
}

LONG __saveds __asm XarsCopyFile(REG __a0 struct XarSubLock **lock,REG __a1 struct XarSubHandle *arc)
{
   FILE *srcfile;
   struct ZooLock *newlock,*l;
   long err=XARERR_NO_ERROR;
   int gen;

   if(!(srcfile=FHopen((*lock)->Arc->ArcFile))) {
      err=XARERR_NO_MEMORY;
      goto err0;
   }

   if(!(newlock=malloc(sizeof(struct ZooLock)))) {
      err=XARERR_NO_MEMORY;
      goto err1;
   }

   *newlock=*(struct ZooLock *)*lock;
   newlock->Lock.Arc=arc;

   newlock->Lock.FileName=strdup((*lock)->FileName);
   newlock->Lock.FileNote=(*lock)->FileNote?strdup((*lock)->FileNote):NULL;

   if(!newlock->Lock.FileName || ((*lock)->FileNote && !newlock->Lock.FileNote)) {
      err=XARERR_NO_MEMORY;
      goto err2;
   }

   if(ReadDirent(srcfile,newlock->DirentPos,&dir)<0) {
      err=XARERR_READ_ERROR;
      goto err2;
   }

   dir.type=2;
   SetFilename(&dir,newlock->Lock.FileName);

   nulldir.var_dir_len=dir.var_dir_len;

   newlock->DirentPos=nullentry;
   newlock->DirentSize=DIRENTSIZE_TYPE2+dir.var_dir_len;

   dir.vflag=zoohead.vdata;

   gen=0;
   for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;l->Lock.Node.mln_Succ;
         l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

      if(!XarsCmpFilename(l->Lock.FileName,newlock->Lock.FileName)) {
         dir.vflag=l->GFlag;
         if(l->Lock.FileData.Generation>gen) gen=l->Lock.FileData.Generation>gen;
         if(dir.vflag & VFL_LAST) break;
      }
   }

   if(dir.vflag & VFL_ON) {
      if(gen<=newlock->Lock.FileData.Generation) dir.vflag|=VFL_LAST;
      dir.version_no=newlock->Lock.FileData.Generation;
   } else {
      dir.version_no=0;
      newlock->Lock.FileData.Generation=0;
   }

   newlock->GFlag=dir.vflag;

   if(fseek(arcfile,nullentry,SEEK_SET) || WriteDirent(&nulldir)) {
      err=XARERR_WRITE_ERROR;
      goto err2;
   }

   if(newlock->Lock.FileNote && newlock->Lock.FileNote[0]) {
      dir.comment=ftell(arcfile);
      dir.cmt_size=strlen(newlock->Lock.FileNote)+1;

      if(!fwrite(newlock->Lock.FileNote,dir.cmt_size,1,arcfile)) {
         err=XARERR_WRITE_ERROR;
         goto err2;
      }
   } else {
      dir.comment=0;
      dir.cmt_size=0;
   }

   newlock->DataPos=dir.offset=ftell(arcfile)+5;

   if(fseek(srcfile,((struct ZooLock *)(*lock))->DataPos-5,SEEK_SET)
      || (err=CopyData(srcfile,arcfile,newlock->Lock.FileData.CompressedSize+5)))
   {
      if(err==XARERR_NO_ERROR) err=XARERR_WRITE_ERROR;
      goto err2;
   }

   dir.next=ftell(arcfile);

   if(WriteDirent(&nulldir) || fseek(arcfile,nullentry,SEEK_SET)
         || WriteDirent(&dir)) {
      err=XARERR_WRITE_ERROR;
      goto err2;
   }

#if 0
   lastentry=nullentry;
#endif
   nullentry=dir.next;

   AddTail((struct List *)&arc->ArcLocks,(struct Node *)newlock);
   *lock=(struct XarSubLock *)newlock;

   /* Update generations and handle generation-limits */
   /* for non-deleted files only                      */

   if(!dir.deleted) {

      for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;l->Lock.Node.mln_Succ;
            l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

         /* Find files with same filename */

         if(l!=newlock && !XarsCmpFilename(l->Lock.FileName,newlock->Lock.FileName)) {
            UBYTE genlim;

            /* does this file have generations ? */

            if(newlock->GFlag & VFL_ON) {
               if(zoohead.vdata & VFL_ON) genlim=min(l->GFlag & VFL_GEN,zoohead.vdata & VFL_GEN);
               genlim=l->GFlag & VFL_GEN;

               if(genlim && newlock->Lock.FileData.Generation-l->Lock.FileData.Generation
                     >=genlim) {

                  /* Delete too old files */

                  if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || ReadBuffer(l->DirentSize)) return XARERR_WRITE_ERROR;

                  buffer[DirOffset_deleted]=1;

                  if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || WriteBuffer()) return XARERR_WRITE_ERROR;
                  l->Lock.FileData.Flags|=XARFF_Deleted;
               } else {
                  int FlagPos;

                  /* Remove "last-flag" */

                  if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || ReadBuffer(l->DirentSize)) return XARERR_WRITE_ERROR;

                  if(buffer[DirOffset_type]==2) {
                     FlagPos=DIRENTSIZE_TYPE2+buffer[DirOffset_dirlen]+buffer[DirOffset_namlen]+7;

                     buffer[FlagPos]&=~VFL_LAST;

                     if(fseek(arcfile,l->DirentPos,SEEK_SET)
                        || WriteBuffer()) return XARERR_WRITE_ERROR;
                  }
               }
            } else {

               /* No generations: delete all other files with the same filename */

               if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || ReadBuffer(l->DirentSize)) return XARERR_WRITE_ERROR;

               buffer[DirOffset_deleted]=1;

               if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || WriteBuffer()) return XARERR_WRITE_ERROR;
               l->Lock.FileData.Flags|=XARFF_Deleted;
            }
         }
      }
   }

   /* Important: Flush all internal buffers, or you might get problems
      with XarsCopyFile. If you try to copy a file, that was just
      added, it could happen that parts of the informations are still in
      the internal buffer, and since XarsCopyFile just copies the
      contents of the file, it could copy invalid data. The reason for
      this behaviour is, that it has no access to the internal buffers
      of the 'arcfile' of the src-archive !!! */

   fclose(srcfile); /* this one only frees the allocated memory, the
                       file itself does not get closed */

   if(fflush(arcfile)==EOF) return XARERR_WRITE_ERROR;

   return XARERR_NO_ERROR;

err2:
   if(newlock->Lock.FileNote) free(newlock->Lock.FileNote);
   if(newlock->Lock.FileName) free(newlock->Lock.FileName);
   free(newlock);
err1:
   fclose(srcfile); /* this one only frees the allocated memory, the
                       file itself does not get closed */
err0:
   return err;
}
LONG __saveds __asm XarsPackArchive(REG __a0 struct XarSubHandle *from, REG __a1 struct XarSubHandle *to)
{
   /* Use the default action, that is built in the xpkarchive.library */
   return XARERR_OPERATION_NOT_SUPPORTED;
}
LONG __saveds __asm XarsModifyFileData(REG __a0 struct XarSubLock *lock,REG __a1 struct XarFileData *FData)
{
   struct ZooLock *l;
   struct XarFileData *d;
   int pos;

   l=(struct ZooLock *)lock;
   d=&l->Lock.FileData;

   if(fseek(arcfile,l->DirentPos,SEEK_SET)
         || ReadBuffer(l->DirentSize))
   {
      return XARERR_WRITE_ERROR;
   }

   if(buffer[DirOffset_type]==2) {
      pos=buffer[DirOffset_dirlen]+buffer[DirOffset_namlen]+DIRENTSIZE_TYPE2;
   } else {
      pos=DIRENTSIZE_TYPE2;
   }

   if(d->Generation!=FData->Generation && (l->GFlag & VFL_ON)) {
      pack_struct(buffer+DirOffset_lfname+pos+6,(char *)&FData->Generation,conv_short);
      d->Generation=FData->Generation;
   }

   if(d->Time.Day!=FData->Time.Day || d->Time.Month!=FData->Time.Month
                                   || d->Time.Year!=FData->Time.Year) {
      short sh;
      sh=(FData->Time.Year-80<<9)+(FData->Time.Month<<5)+FData->Time.Day;
      pack_struct(buffer+DirOffset_date,(char *)&sh,conv_short);

   }

   if(d->Time.Hour!=FData->Time.Hour || d->Time.Min!=FData->Time.Min
                                     || d->Time.Sec!=FData->Time.Sec) {
      short sh;
      sh=(FData->Time.Hour<<11)+(FData->Time.Min<<5)+FData->Time.Sec/2;
      pack_struct(buffer+DirOffset_time,(char *)&sh,conv_short);
   }

   d->Time=FData->Time;

   if(Amiga2IXMode(d->Protection)!=Amiga2IXMode(FData->Protection)) {
      long fattr;
      char conv_3[]={-3,0};

      fattr=Amiga2IXMode(FData->Protection) | (1<<22);
      pack_struct(buffer+DirOffset_lfname+pos+2,(char *)&fattr,conv_3);
      d->Protection=IX2AmigaMode(fattr);

   }

   if(fseek(arcfile,l->DirentPos,SEEK_SET)
         || WriteBuffer())
   {
      return XARERR_WRITE_ERROR;
   }

   return XARERR_NO_ERROR;
}
LONG __saveds __asm XarsSetFilenote(REG __a0 struct XarSubLock *lock,REG __a1 const UBYTE *FileNote)
{
   short len;
   long cpos,newnull;
   char buf[6]={0};
   struct ZooLock *last;

   len=strlen(FileNote);

   if(!len) {
      cpos=0;
      if(fseek(arcfile,((struct ZooLock *)lock)->DirentPos,SEEK_SET)
                  || ReadBuffer(((struct ZooLock *)lock)->DirentSize)) return XARERR_WRITE_ERROR;

      pack_struct(buffer+DirOffset_cmt_size,(char *)&len,conv_short);
      pack_struct(buffer+DirOffset_comment,(char *)&cpos,conv_long);

      if(fseek(arcfile,((struct ZooLock *)lock)->DirentPos,SEEK_SET)
                  || WriteBuffer()) return XARERR_WRITE_ERROR;

   } else {
      len++;

      last=(struct ZooLock *)lock->Arc->ArcLocks.mlh_TailPred;

      if(fseek(arcfile,nullentry,SEEK_SET)) return XARERR_WRITE_ERROR;
      cpos=nullentry;

      if(len>6) {
         if(!fwrite(buf,6,1,arcfile) || !fwrite(buf+6,len-6,1,arcfile)) return XARERR_WRITE_ERROR;
      } else {
         if(!fwrite(buf,len,1,arcfile)) return XARERR_WRITE_ERROR;
      }

      newnull=ftell(arcfile);

      nulldir.var_dir_len=0;

      if(WriteDirent(&nulldir)) return XARERR_WRITE_ERROR;

      if(fseek(arcfile,last->DirentPos,SEEK_SET)
                  || ReadBuffer(last->DirentSize)) return XARERR_WRITE_ERROR;

      pack_struct(buffer+DirOffset_next,(char *)&newnull,conv_long);

      if(fseek(arcfile,((struct ZooLock *)last)->DirentPos,SEEK_SET)
                  || WriteBuffer()) return XARERR_WRITE_ERROR;

      nullentry=newnull;

      if(fseek(arcfile,((struct ZooLock *)lock)->DirentPos,SEEK_SET)
                  || ReadBuffer(((struct ZooLock *)lock)->DirentSize)) return XARERR_WRITE_ERROR;

      pack_struct(buffer+DirOffset_cmt_size,(char *)&len,conv_short);
      pack_struct(buffer+DirOffset_comment,(char *)&cpos,conv_long);

      if(fseek(arcfile,((struct ZooLock *)lock)->DirentPos,SEEK_SET)
                  || WriteBuffer()) return XARERR_WRITE_ERROR;


      if(fseek(arcfile,cpos,SEEK_SET)) return XARERR_WRITE_ERROR;
      if(len>6) len=6;

      if(!fwrite(buf,len,1,arcfile)) return XARERR_WRITE_ERROR;
   }
   if(fflush(arcfile)) return XARERR_WRITE_ERROR;

   if(lock->FileNote) free(lock->FileNote);

   if(!len) {
      lock->FileNote=NULL;
   } else {
      if(!(lock->FileNote=strdup(FileNote))) return XARERR_NO_MEMORY;
   }

   return XARERR_NO_ERROR;
}

LONG __saveds __asm XarsRenameFile(REG __a0 struct XarSubLock *lock,REG __a1 const UBYTE *NewName, REG __d0 long NewGeneration)
{
   char *name;
   struct ZooLock *l,*last;
   long newnull,dirent_pos;
   struct XarSubHandle *arc;

   arc=lock->Arc;

   name=strdup(NewName);
   if(!name) return XARERR_NO_MEMORY;

   if(ReadDirent(arcfile,((struct ZooLock *)lock)->DirentPos,&dir)<0) {
         free(name);
         return XARERR_WRITE_ERROR;
   }

   SetFilename(&dir,name);

   dir.vflag=zoohead.vdata;
   dir.type=2;

   for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;l->Lock.Node.mln_Succ;
         l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

      if(!XarsCmpFilename(l->Lock.FileName,name)) {
         dir.vflag=l->GFlag;
         if(dir.vflag & VFL_LAST) break;
      }
   }

   if(dir.vflag & VFL_ON) {
      dir.vflag|=VFL_LAST;
      dir.version_no=NewGeneration;
   } else {
      dir.version_no=0;
   }

   nulldir.var_dir_len=dir.var_dir_len;

   if(fseek(arcfile,nullentry,SEEK_SET)
       || WriteDirent(&nulldir)
       || (newnull=ftell(arcfile),nulldir.var_dir_len=0,WriteDirent(&nulldir)))
   {
      free(name);
      return XARERR_WRITE_ERROR;
   }

   last=(struct ZooLock *)lock->Arc->ArcLocks.mlh_TailPred;

   if(fseek(arcfile,last->DirentPos,SEEK_SET)
                  || ReadBuffer(last->DirentSize))
   {
      free(name);
      return XARERR_WRITE_ERROR;
   }

   pack_struct(buffer+DirOffset_next,(char *)&newnull,conv_long);

   if(fseek(arcfile,last->DirentPos,SEEK_SET)
                  || WriteBuffer())
   {
      free(name);
      return XARERR_WRITE_ERROR;
   }

   if(last==(struct ZooLock *)lock) {
      dir.next=newnull;
   }

   if(fseek(arcfile,nullentry,SEEK_SET) || WriteDirent(&dir)) {
      free(name);
      return XARERR_WRITE_ERROR;
   }

   dirent_pos=nullentry;
   nullentry=newnull;

   l=(struct ZooLock *)lock->Node.mln_Pred;

   if(l->Lock.Node.mln_Pred) {
      if(fseek(arcfile,l->DirentPos,SEEK_SET)
                  || ReadBuffer(l->DirentSize))
      {
         free(name);
         return XARERR_WRITE_ERROR;
      }

      pack_struct(buffer+DirOffset_next,(char *)&dirent_pos,conv_long);

      if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || WriteBuffer())
      {
         free(name);
         return XARERR_WRITE_ERROR;
      }
   } else {
      char buf[8];

      pack_struct(buf,(char *)&dirent_pos,conv_long);
      dirent_pos=-dirent_pos;
      pack_struct(buf+4,(char *)&dirent_pos,conv_long);
      dirent_pos=-dirent_pos;

      if(fseek(arcfile,HeadOffset_zoo_start,SEEK_SET)
            || !fwrite(buf,8,1,arcfile))
      {
         free(name);
         return XARERR_WRITE_ERROR;
      }
   }

   ((struct ZooLock *)lock)->DirentPos=dirent_pos;
   ((struct ZooLock *)lock)->DirentSize=dir.var_dir_len+DIRENTSIZE_TYPE2;
   free(lock->FileName);
   lock->FileName=name;
   lock->FileData.Generation=NewGeneration;
   ((struct ZooLock *)lock)->GFlag=dir.vflag;

   if(!dir.deleted) {

      for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;l->Lock.Node.mln_Succ;
            l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

         /* Find files with same filename */

         if(l!=(struct ZooLock *)lock && !XarsCmpFilename(l->Lock.FileName,((struct ZooLock *)lock)->Lock.FileName)) {
            UBYTE genlim;

            /* does this file have generations ? */

            if(((struct ZooLock *)lock)->GFlag & VFL_ON) {
               if(zoohead.vdata & VFL_ON) genlim=min(l->GFlag & VFL_GEN,zoohead.vdata & VFL_GEN);
               genlim=l->GFlag & VFL_GEN;

               if(genlim && ((struct ZooLock *)lock)->Lock.FileData.Generation-l->Lock.FileData.Generation
                     >=genlim) {

                  /* Delete too old files */

                  if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || ReadBuffer(l->DirentSize)) return XARERR_WRITE_ERROR;

                  buffer[DirOffset_deleted]=1;

                  if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || WriteBuffer()) return XARERR_WRITE_ERROR;
               } else {
                  int FlagPos;

                  /* Remove "last-flag" */

                  if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || ReadBuffer(l->DirentSize)) return XARERR_WRITE_ERROR;

                  if(buffer[DirOffset_type]==2) {
                     FlagPos=DIRENTSIZE_TYPE2+buffer[DirOffset_dirlen]+buffer[DirOffset_namlen]+7;

                     buffer[FlagPos]&=~VFL_LAST;

                     if(fseek(arcfile,l->DirentPos,SEEK_SET)
                        || WriteBuffer()) return XARERR_WRITE_ERROR;
                  }
               }
            } else {

               /* No generations: delete all other files with the same filename */

               if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || ReadBuffer(l->DirentSize)) return XARERR_WRITE_ERROR;

               buffer[DirOffset_deleted]=1;

               if(fseek(arcfile,l->DirentPos,SEEK_SET)
                     || WriteBuffer()) return XARERR_WRITE_ERROR;
            }
         }
      }
   }

   /* Important: Flush all internal buffers, or you might get problems
      with XarsCopyFile. If you try to copy a file, that was just
      added, it could happen that parts of the informations are still in
      the internal buffer, and since XarsCopyFile just copies the
      contents of the file, it could copy invalid data. The reason for
      this behaviour is, that it has no access to the internal buffers
      of the 'arcfile' of the src-archive !!! */

   if(fflush(arcfile)==EOF) return XARERR_WRITE_ERROR;

   return XARERR_NO_ERROR;
}
LONG __saveds __asm XarsCmpFilename(REG __a0 const UBYTE *s1,REG __a1 const UBYTE *s2)
{
   return StringICmp(s1,s2);
}

char *globalerror="";
char message[80];

UBYTE * __saveds __asm XarsWhy(register __a0 struct XarSubHandle *arc, register __d0 long ErrorCode)
{
   return globalerror;
}
LONG __saveds __asm XarsDirect(register __a0 struct XarSubHandle *arc, register __a1 const UBYTE *command)
{
   const UBYTE *file;

   int cmd=0;
   int gen,maxgen=-1;
   struct ZooLock *l;
   int  FlagPos;

   /* switching generations on and off */

   if(!stricmp(command,"gA+")) {
      zoohead.vdata |= VFL_ON;
      cmd=1;
   } else if(!stricmp(command,"gA-")) {
      zoohead.vdata &= ~VFL_ON;
      cmd=1;
   }

   if(cmd) {
      if(fseek(arcfile,0,SEEK_SET)) return XARERR_WRITE_ERROR;
      pack_struct(buffer,(char *)&zoohead,convhead);

      if(!fwrite(buffer,HEADER_SIZE,1,arcfile)) return XARERR_WRITE_ERROR;

      sprintf(message,"generations activated");
      globalerror=message;
      return XARCUSTOM_LEVEL;
   }


   /* setting the global generationlimit */

   if(!strnicmp(command,"glA=",4)) {
      if(!sscanf(command+4,"%d",&gen)) {
         globalerror="invalid generationlimit";
         return XARCUSTOM_LEVEL;
      }

      cmd=1;
   } else if(!strnicmp(command,"glA+",4)) {
      if(!sscanf(command+4,"%d",&gen)) {
         globalerror="invalid generationlimit";
         return XARCUSTOM_LEVEL;
      }

      gen+=zoohead.vdata & VFL_GEN;
      cmd=1;
   } else if(!strnicmp(command,"glA-",4)) {
      if(!sscanf(command+4,"%d",&gen)) {
         globalerror="invalid generationlimit";
         return XARCUSTOM_LEVEL;
      }

      gen=(zoohead.vdata & VFL_GEN)-gen;

      zoohead.vdata = (zoohead.vdata & ~VFL_GEN) | (gen & VFL_GEN);
      cmd=1;
   }

   if(cmd) {
      zoohead.vdata = (zoohead.vdata & ~VFL_GEN) | (gen & VFL_GEN);

      if(fseek(arcfile,0,SEEK_SET)) return XARERR_WRITE_ERROR;
      pack_struct(buffer,(char *)&zoohead,convhead);

      if(!fwrite(buffer,HEADER_SIZE,1,arcfile)) return XARERR_WRITE_ERROR;

      sprintf(message,"the generationlimit of the archive is now %d",zoohead.vdata & VFL_GEN);
      globalerror=message;
      return XARCUSTOM_LEVEL;
   }



   /* setting the generationlimit for single files */

   file=strchr(command,' ');
   if(!file) {
      globalerror="Unknown command";
      return XARCUSTOM_LEVEL;
   }

   file++;

   if(!strnicmp(command,"gl=",3)) {
      if(!sscanf(command+3,"%d",&gen)) {
         globalerror="invalid generationlimit";
         return XARCUSTOM_LEVEL;
      }

      for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;l->Lock.Node.mln_Succ;
                  l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

         if(!XarsCmpFilename(l->Lock.FileName,file)
              && l->Lock.FileData.Generation>maxgen) {

            maxgen=l->Lock.FileData.Generation;
         }
      }
      cmd=1;
   } else if(!strnicmp(command,"gl+",3)) {
      if(!sscanf(command+3,"%d",&gen)) {
         globalerror="invalid generationlimit";
         return XARCUSTOM_LEVEL;
      }

      for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;l->Lock.Node.mln_Succ;
                  l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

         if(!XarsCmpFilename(l->Lock.FileName,file)
              && l->Lock.FileData.Generation>maxgen) {

            if(maxgen==-1) {
               gen+=l->GFlag & VFL_GEN;
            }
            maxgen=l->Lock.FileData.Generation;
         }
      }
      cmd=1;
   } else if(!strnicmp(command,"gl-",3)) {
      if(!sscanf(command+3,"%d",&gen)) {
         globalerror="invalid generationlimit";
         return XARCUSTOM_LEVEL;
      }

      for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;l->Lock.Node.mln_Succ;
                  l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

         if(!XarsCmpFilename(l->Lock.FileName,file)
              && l->Lock.FileData.Generation>maxgen) {

            if(maxgen==-1) {
               gen=(l->GFlag & VFL_GEN)-gen;
            }
            maxgen=l->Lock.FileData.Generation;
         }
      }
      cmd=1;
   }

   if(cmd) {
      gen &=VFL_GEN;

      if(maxgen==-1) {
         sprintf(message,"file %s not found",file);
         globalerror=message;
         return XARCUSTOM_LEVEL;
      }

      for(l=(struct ZooLock *)arc->ArcLocks.mlh_Head;l->Lock.Node.mln_Succ;
                  l=(struct ZooLock *)l->Lock.Node.mln_Succ) {

         if(!XarsCmpFilename(l->Lock.FileName,file)) {

            if(fseek(arcfile,l->DirentPos,SEEK_SET)
               || ReadBuffer(l->DirentSize)) return XARERR_WRITE_ERROR;

            if(buffer[DirOffset_type]==2) {
               FlagPos=DIRENTSIZE_TYPE2+buffer[DirOffset_dirlen]+buffer[DirOffset_namlen]+7;

            buffer[FlagPos]=(buffer[FlagPos] & ~VFL_GEN) | gen;

            if(gen && maxgen-l->Lock.FileData.Generation>=gen) {
               buffer[DirOffset_deleted]=1;
            }

            if(fseek(arcfile,l->DirentPos,SEEK_SET)
               || WriteBuffer()) return XARERR_WRITE_ERROR;
            }

            if(gen && maxgen-l->Lock.FileData.Generation>=gen) {
               l->Lock.FileData.Flags|=XARFF_Deleted;
            }
            l->GFlag=buffer[FlagPos];
         }
      }
      sprintf(message,"the generationlimit of %s is now %d",file,gen);
      globalerror=message;
      return XARCUSTOM_LEVEL;
   }

   globalerror="Unknown command";
   return XARCUSTOM_LEVEL;
}

void Error(long err)
{
   longjmp(catch_pos,err);
}

long __saveds __asm XarsGetPackMode(register __a0 struct XarSubLock *lock, register __a1 struct XarPackMode *mode)
{
   static char *modes[]={"copy","lzw","lh5"};

   strcpy(mode->ID,"ZOO");
   strcpy(mode->Description,modes[((struct ZooLock *)lock)->Method]);
   mode->Mode=((struct ZooLock *)lock)->Method*50;
   return XARERR_NO_ERROR;
}

