
/*
 *
 *  TODO: Progress-Report für Directories
 *
 */


#define NO_XARS_PRAGMAS

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

#include <stddef.h>

void * __asm AsmAllocPooled(register __a0 void *,
                            register __d0 ULONG,
                            register __a6 struct ExecBase *);

void * __asm AsmCreatePool(register __d0 ULONG,
                           register __d1 ULONG,
                           register __d2 ULONG,
                           register __a6 struct ExecBase *);


void __asm AsmDeletePool(register __a0 void *,
                         register __a6 struct ExecBase *);

void __asm AsmFreePooled(register __a0 void *,
                         register __a1 void *,
                         register __d0 ULONG,
                         register __a6 struct ExecBase *);

#define A3000 XPKMF_A3000SPEED

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

struct XarSubInfo dummyInfo=
{
   sizeof(struct XarSubInfo),       /* sizeof()                      */
   1,0,                             /* Version+Revision              */
   0,                               /* Version of xpkarchive.library */
   {
      "Dummy","Dummy 1.0","Dummy archiver version 1.0",
      "Dummy",                         /* ArchiveType                   */
      "DUMMY",                         /* ID                            */
      100,                             /* Default mode                  */
      "dummy",                         /* extension                     */
      XARIF_PK_MEM|XARIF_UP_MEM|XARIF_USES_GENERATIONS        /* Flags                         */
   },
   &DummyMode,                      /* Modes                         */
   {"Dummy*0",NULL},                /* Identify                      */
};

struct FileHeader
{
   UBYTE FileName[128];
   UBYTE Comment[80];
   ULONG Protection;
   struct XarTime Time;
   ULONG Size;
   USHORT Generation;
};

struct MyLock {
   struct XarSubLock Lock;
   struct FileHeader FHead;
   ULONG FileHeaderPos;
   ULONG DataPos;
};


struct ExecBase *SysBase;
struct DosLibrary *DOSBase;

#define MemPool Sub[0]
#define ArcSize Sub[1]

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);
}

void *XAllocMem(void* pool,ULONG size)
{
   ULONG *mem;
   size+=4;
   mem=(ULONG *)AsmAllocPooled(pool,size,SysBase);

   if(mem) *mem++=size;

   return (void *)mem;
}
void XFreeMem(void *pool,void *m)
{
   ULONG *mem;

   mem=(ULONG *)m;

   mem--;

   AsmFreePooled(pool,mem,*mem,SysBase);
}

long ScanArchive(struct XarSubHandle *arc)
{
   struct FileHeader hd;
   struct MyLock *lock;
   long len;

   ULONG pos;

   arc->ArcSize=6;

   while(len=Read(arc->ArcFile,&hd,sizeof(hd))) {

      if(len!=sizeof(hd)) {
         arc->Flags|=XARAF_Corrupt;
         return XARWARN_ARCHIVE_CORRUPT;
      }

      if((pos=Seek(arc->ArcFile,hd.Size,OFFSET_CURRENT))<0) {
         arc->Flags |= XARAF_Corrupt;
         return XARWARN_ARCHIVE_CORRUPT;
      }

      arc->ArcSize=pos+hd.Size;

      if(!(lock=XAllocMem((void *)arc->MemPool,sizeof(struct MyLock))))
            return XARERR_NO_MEMORY;

      lock->FHead=hd;
      lock->Lock.FileData.ValidMask=XAR_ValidTime|XAR_ValidProtect|XAR_ValidGeneration;

      if(hd.FileName[0]) {
         lock->Lock.FileName=lock->FHead.FileName;
      } else {
         lock->Lock.FileName=lock->FHead.FileName+1;
         lock->Lock.FileData.Flags|=XARFF_Deleted;
      }

      lock->Lock.FileNote=lock->FHead.Comment;
      lock->Lock.Arc=arc;
   /* lock->Lock.FileData.CHKSum=0;  allocated with MEMF_CLEAR  */
      lock->Lock.FileData.Filesize=hd.Size;
      lock->Lock.FileData.SysID=SYS_AMIGA;
      lock->Lock.FileData.Protection=hd.Protection;
      lock->Lock.FileData.Time=hd.Time;
      lock->Lock.FileData.CompressedSize=hd.Size;
      lock->Lock.FileData.Generation=hd.Generation;

   /* Check for directory-entry */

      if(lock->Lock.FileName[strlen(lock->Lock.FileName)-1]=='/') {
         lock->Lock.FileData.Type=XAR_TypeDir;
      } else {
         lock->Lock.FileData.Type=XAR_TypeFile;
      }

      lock->FileHeaderPos=pos-sizeof(hd);
      lock->DataPos=pos;

      lock->FHead=hd;

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

   return XARERR_NO_ERROR;
}
long FCopy(BPTR from, BPTR to,ULONG size)
{
   UBYTE *mem;
   ULONG memsize;
   long err=XARERR_NO_ERROR;

   if(!size) return XARERR_NO_ERROR;

   memsize=size;

   while(memsize && (mem=AllocMem(memsize,NULL))==NULL) memsize<<=1;

   if(!memsize) return XARERR_NO_MEMORY;


   while(size>=memsize) {
      if(Read(from,mem,memsize)!=memsize) {err=XARERR_READ_ERROR;break;}
      if(Write(to,mem,memsize)<0) {err=XARERR_WRITE_ERROR;break;}
      size-=memsize;
   }

   if(size && size<memsize) {
      if(Read(from,mem,size)!=size) err=XARERR_READ_ERROR;
      if(Write(to,mem,size)<0) err=XARERR_WRITE_ERROR;
   }

   FreeMem(mem,memsize);
   return err;
}

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 CheckName(UBYTE *s,int type)
/*
 * Check the filename after a possible truncation of the filename due to
 * a max. length of 126 characters.
 * Directories must terminate in '/', whereas files must not.
 *
 */
{
   int l;

   if(!s[0]) s++; /* Deleted files have the first character set to 0 */

   l=strlen(s)-1;
   if(l<0) return; /* This case should never occur */

   if(type==XAR_TypeDir) {
      s[l]='/';   /* Normally this will override '/', in the other case,
                     the filename is too long and will be truncated by one
                     character */
   } else {
      if(s[l]=='/') s[l]=0; /* Filenames must not terminate in '/' */
   }
}

/********************** Interface funtions *******************************/

struct XarSubInfo __saveds * XarsArchiverInfo(void)
{
   return &dummyInfo;
}

long __saveds __asm XarsCreateArchive(REG __a0 struct XarSubHandle *arc)
{
   if(!(arc->MemPool=(ULONG)AsmCreatePool(MEMF_CLEAR,4096,512,SysBase))) return XARERR_NO_MEMORY;
   Write(arc->ArcFile,"Dummy",6);

   arc->ArcSize=6;

   return XARERR_NO_ERROR;
}
long __saveds __asm XarsOpenArchive(REG __a0 struct XarSubHandle *arc)
{
   long err;
   UBYTE buffer[6];

   if(Read(arc->ArcFile,buffer,6)!=6
      || strcmp("Dummy",buffer)) return XARERR_UNKNOWN_TYPE;

   if(!(arc->MemPool=(ULONG)AsmCreatePool(MEMF_CLEAR,4096,512,SysBase))) return XARERR_NO_MEMORY;



   Seek(arc->ArcFile,6,OFFSET_BEGINNING);

   err=ScanArchive(arc);

   if(err>XARERROR_LEVEL) {
      AsmDeletePool((void *)arc->MemPool,SysBase);
   }
   return err;
}
long __saveds __asm XarsCloseArchive(REG __a0 struct XarSubHandle *arc)
{
   AsmDeletePool((void *)arc->MemPool,SysBase);

   return XARERR_NO_ERROR;
}

long __saveds __asm XarsAddFile(REG __a0 struct XarSubHandle *arc,
   REG __a1 struct XarSubIO *xio,
   REG __a2 struct XarSubLock **lock)
{
   struct MyLock *mlock;
   struct Library *XpkArchiveBase;
   struct XarProgress xprog={"Dummy","Dummy archiver","adding","added"};

   if(!xio->IOMem) return XARERR_INPUT_NOT_SUPPORTED;

   XpkArchiveBase=(*lock)->Arc->MasterLib;

   mlock=XAllocMem((void *)arc->MemPool,sizeof(struct MyLock));
   if(!mlock) return XARERR_NO_MEMORY;

   mlock->Lock=**lock; /* copy contents of lock */

   mlock->Lock.FileData.ValidMask=XAR_ValidTime|XAR_ValidProtect|XAR_ValidGeneration;
   mlock->Lock.Arc=arc;

   stccpy(mlock->FHead.FileName,(*lock)->FileName,127);
   mlock->Lock.FileName=mlock->FHead.FileName;
   CheckName(mlock->FHead.FileName,mlock->Lock.FileData.Type);
   if((*lock)->FileNote) stccpy(mlock->FHead.Comment,(*lock)->FileNote,80); else mlock->FHead.Comment[0]=0;
   mlock->Lock.FileNote=mlock->FHead.Comment;
   mlock->FHead.Time=mlock->Lock.FileData.Time;
   mlock->FHead.Size=xio->Size;
   mlock->FHead.Protection=mlock->Lock.FileData.Protection;
   mlock->FHead.Generation=mlock->Lock.FileData.Generation;

   mlock->FileHeaderPos=arc->ArcSize;
   mlock->DataPos=arc->ArcSize+sizeof(struct FileHeader);

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

   if(Seek(arc->ArcFile,arc->ArcSize,OFFSET_BEGINNING)<0) {
      XarProgress(XARPROG_END,xio,&xprog,0,0);
      return XARERR_READ_ERROR;
   }

   if(Write(arc->ArcFile,&mlock->FHead,sizeof(struct FileHeader))<0) {
      XarProgress(XARPROG_END,xio,&xprog,0,0);
      return XARERR_WRITE_ERROR;
   }

   if(Write(arc->ArcFile,xio->IOMem,xio->Size)<0) {
      XarProgress(XARPROG_END,xio,&xprog,0,0);
      return XARERR_WRITE_ERROR;
   }
   arc->ArcSize+=xio->Size+sizeof(struct FileHeader);

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

   XarProgress(XARPROG_END,xio,&xprog,xio->Size,xio->Size);

   return XARERR_NO_ERROR;
}
long __saveds __asm XarsAddDir(REG __a0 struct XarSubHandle *arc,
   REG __a1 struct XarSubIO *xio,
   REG __a2 struct XarSubLock **lock)
{
   struct MyLock *mlock;
   struct Library *XpkArchiveBase;
   struct XarProgress xprog={"Dummy","Dummy archiver",NULL,"added directory"};

   XpkArchiveBase=(*lock)->Arc->MasterLib;

   mlock=XAllocMem((void *)arc->MemPool,sizeof(struct MyLock));

   if(!mlock) return XARERR_NO_MEMORY;
   mlock->Lock.FileData.ValidMask=XAR_ValidTime|XAR_ValidProtect;

   mlock->Lock=**lock;
   mlock->Lock.Arc=arc;

   stccpy(mlock->FHead.FileName,(*lock)->FileName,127);
   mlock->Lock.FileName=mlock->FHead.FileName;

   CheckName(mlock->FHead.FileName,mlock->Lock.FileData.Type);

   if((*lock)->FileNote) stccpy(mlock->FHead.Comment,(*lock)->FileNote,80);
         else mlock->FHead.Comment[0]=0;

   mlock->Lock.FileNote=mlock->FHead.Comment;

   mlock->FHead.Time=(*lock)->FileData.Time;
   mlock->FHead.Size=0;

   mlock->FHead.Protection=(*lock)->FileData.Protection;

   mlock->FileHeaderPos=arc->ArcSize;
   mlock->DataPos=arc->ArcSize+sizeof(struct FileHeader);

   if(Seek(arc->ArcFile,arc->ArcSize,OFFSET_BEGINNING)<0) {
      return XARERR_READ_ERROR;
   }

   if(Write(arc->ArcFile,&mlock->FHead,sizeof(struct FileHeader))<0) {
      return XARERR_WRITE_ERROR;
   }

   arc->ArcSize+=sizeof(struct FileHeader);

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

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

   return XARERR_NO_ERROR;
}
long __saveds __asm XarsExtractFile(REG __a0 struct XarSubLock *lock,
   REG __a1 struct XarSubIO *xio)
{
   struct MyLock *mlock;
   struct XarSubHandle *arc;
   struct Library *XpkArchiveBase;
   struct XarProgress xprog={"Dummy","Dummy archiver","extracting","extracted"};

   if(!xio->IOMem) return XARERR_OUTPUT_NOT_SUPPORTED;

   XpkArchiveBase=lock->Arc->MasterLib;

   mlock=(struct MyLock *)lock;
   arc=lock->Arc;

   if(xio->Size<mlock->Lock.FileData.Filesize) return XARERR_SMALL_BUF;

   if(Seek(arc->ArcFile,mlock->DataPos,OFFSET_BEGINNING)<0)
      return XARERR_READ_ERROR;

   XarProgress(XARPROG_START,xio,&xprog,xio->Size,xio->Size);

   if(Read(arc->ArcFile,xio->IOMem,mlock->Lock.FileData.Filesize)<
      mlock->Lock.FileData.Filesize) {

      XarProgress(XARPROG_END,xio,&xprog,xio->Size,xio->Size);
      return XARERR_READ_ERROR;
   }
   XarProgress(XARPROG_END,xio,&xprog,xio->Size,xio->Size);

   return XARERR_NO_ERROR;
}
long __saveds __asm XarsDeleteFile(REG __a0 struct XarSubLock *lock)
{
   struct MyLock *mlock;
   struct XarSubHandle *arc;

   mlock=(struct MyLock *) lock;
   arc=lock->Arc;

   memmove(mlock->FHead.FileName+1,mlock->FHead.FileName,127);
   mlock->FHead.FileName[0]=mlock->FHead.FileName[127]=0;

   mlock->Lock.FileData.Flags|=XARFF_Deleted;

   CheckName(mlock->FHead.FileName,mlock->Lock.FileData.Type);

   if(Seek(arc->ArcFile,mlock->FileHeaderPos,OFFSET_BEGINNING)<0) return XARERR_READ_ERROR;
   if(Write(arc->ArcFile,mlock->FHead.FileName,128)<0) return XARERR_WRITE_ERROR;

   return XARERR_NO_ERROR;
}
long __saveds __asm XarsUndeleteFile(REG __a0 struct XarSubLock *lock)
{
   struct MyLock *mlock;
   struct XarSubHandle *arc;

   mlock=(struct MyLock *) lock;
   arc=lock->Arc;

   memmove(mlock->FHead.FileName,mlock->FHead.FileName+1,127);
   mlock->FHead.FileName[127]=0;

   mlock->Lock.FileData.Flags&=~XARFF_Deleted;

   if(Seek(arc->ArcFile,mlock->FileHeaderPos,OFFSET_BEGINNING)<0) return XARERR_READ_ERROR;
   if(Write(arc->ArcFile,mlock->FHead.FileName,128)<0) return XARERR_WRITE_ERROR;

   return XARERR_NO_ERROR;
}

long __saveds __asm XarsCopyFile(REG __a0 struct XarSubLock **lock,REG __a1 struct XarSubHandle *dstarc)
/*
 *
 * Lock of copied file -> lock
 *
 * NOTE: XarCopy of the xpkarchive.library may temporarily replace
 *       the filename, filenote and generation of the lock !!
 *       Therefore you must not change filename or filenote of the
 *       original lock.
 *
 *
 */
{
   struct MyLock *mlock;
   long err;

   mlock=XAllocMem((void *)dstarc->MemPool,sizeof(struct MyLock));

   if(!mlock) return XARERR_NO_MEMORY;
   mlock->Lock.FileData.ValidMask=XAR_ValidTime|XAR_ValidProtect;

   *mlock=*(struct MyLock *)*lock;

   if(mlock->Lock.FileData.Flags & XARFF_Deleted) {
      mlock->Lock.FileName=mlock->FHead.FileName+1;
   } else {
      mlock->Lock.FileName=mlock->FHead.FileName;
   }

   stccpy(mlock->Lock.FileName,(*lock)->FileName,127);
   CheckName(mlock->Lock.FileName,mlock->Lock.FileData.Type);
   mlock->FHead.Generation=mlock->Lock.FileData.Generation=(*lock)->FileData.Generation;

   mlock->Lock.Arc=dstarc;

   stccpy(mlock->FHead.Comment,(*lock)->FileNote,80);
   mlock->Lock.FileNote=mlock->FHead.Comment;

   mlock->FileHeaderPos=dstarc->ArcSize;
   mlock->DataPos=dstarc->ArcSize+sizeof(struct FileHeader);

   if(Seek((*lock)->Arc->ArcFile,((struct MyLock *)(*lock))->DataPos,OFFSET_BEGINNING)<0
      || Seek(dstarc->ArcFile,dstarc->ArcSize,OFFSET_BEGINNING)<0)
   {
      XFreeMem((void *)dstarc->MemPool,mlock);
      return XARERR_READ_ERROR;
   }

   if(Write(dstarc->ArcFile,&mlock->FHead,sizeof(struct FileHeader))<0) {
      XFreeMem((void *)dstarc->MemPool,mlock);
      return XARERR_WRITE_ERROR;
   }
   if(err=FCopy((*lock)->Arc->ArcFile,dstarc->ArcFile,(*lock)->FileData.CompressedSize)) {
      XFreeMem((void *)dstarc->MemPool,mlock);
      return err;
   }

   dstarc->ArcSize+=(*lock)->FileData.CompressedSize+sizeof(struct FileHeader);

   AddTail((struct List *)&dstarc->ArcLocks,(struct Node *)mlock);

   /*
    * NOTE: the following does not change the original lock, but the pointer
    *       to this lock.
    */

   *lock=(struct XarSubLock *)mlock;

   return XARERR_NO_ERROR;
}
long __saveds __asm XarsPackArchive(REG __a0 struct XarSubHandle *from, REG __a1 struct XarSubHandle *to)
{
   struct XarSubLock *lock,*newlock;
   long err=XARERR_NO_ERROR;

   for(lock=(struct XarSubLock *)from->ArcLocks.mlh_Head;
           lock->Node.mln_Succ;
           lock=(struct XarSubLock *)lock->Node.mln_Succ) {

      if(!(lock->FileData.Flags & XARFF_Deleted)) {
         newlock=lock;
         if(err=XarsCopyFile(&newlock,to)) break;
      }
   }

   return err;
}

long __saveds __asm XarsModifyFileData(REG __a0 struct XarSubLock *lock,REG __a1 struct XarFileData *FData)
{
   struct MyLock *mlock;
   struct XarSubHandle *arc;

   mlock=(struct MyLock *)lock;
   arc=lock->Arc;

   mlock->FHead.Protection=mlock->Lock.FileData.Protection=FData->Protection;
   mlock->FHead.Time=mlock->Lock.FileData.Time=FData->Time;

   if(Seek(arc->ArcFile,mlock->FileHeaderPos+offsetof(struct FileHeader,Protection),OFFSET_BEGINNING)<0) return XARERR_READ_ERROR;
   if(Write(arc->ArcFile,&mlock->FHead.Protection,sizeof(struct FileHeader)-offsetof(struct FileHeader,Protection))<0) return XARERR_WRITE_ERROR;

   return XARERR_NO_ERROR;
}
long __saveds __asm XarsSetFilenote(REG __a0 struct XarSubLock *lock,REG __a1 const UBYTE *FileNote)
{
   struct MyLock *mlock;
   struct XarSubHandle *arc;

   mlock=(struct MyLock *)lock;
   arc=lock->Arc;

   stccpy(mlock->FHead.Comment,FileNote,80);

   if(Seek(arc->ArcFile,mlock->FileHeaderPos+offsetof(struct FileHeader,Comment),OFFSET_BEGINNING)<0) return XARERR_READ_ERROR;
   if(Write(arc->ArcFile,mlock->FHead.Comment,80)<0) return XARERR_WRITE_ERROR;

   return XARERR_NO_ERROR;
}
long __saveds __asm XarsRenameFile(REG __a0 struct XarSubLock *lock,REG __a1 const UBYTE *NewName, REG __d0 long NewGeneration)
{
   struct MyLock *mlock;
   struct XarSubHandle *arc;

   mlock=(struct MyLock *)lock;
   arc=lock->Arc;

   stccpy(mlock->Lock.FileName,NewName,127);
   CheckName(mlock->Lock.FileName,mlock->Lock.FileData.Type);

   mlock->Lock.FileData.Generation=mlock->FHead.Generation=NewGeneration;

   if(Seek(arc->ArcFile,mlock->FileHeaderPos+offsetof(struct FileHeader,FileName),OFFSET_BEGINNING)<0) return XARERR_READ_ERROR;
   if(Write(arc->ArcFile,mlock->FHead.FileName,sizeof(struct FileHeader)-offsetof(struct FileHeader,FileName))<0) return XARERR_WRITE_ERROR;

   return XARERR_NO_ERROR;
}

#if 0
long __saveds __asm XarsExamine(REG __a0 struct XarSubLock *lock,REG __a1 struct XpkFib *fib)
{
   memset(fib,0,sizeof(*fib));
   fib->CLen=fib->ULen=lock->FileData.Filesize;
   fib->ID=XAR_NULL_XPKID;

   return XARERR_NO_ERROR;
}
#endif

long __saveds __asm XarsCmpFilename(REG __a0 const UBYTE *s1,REG __a1 const UBYTE *s2)
{
   return StringICmp(s1,s2);
}
UBYTE * __saveds __asm XarsWhy(register __a0 struct XarSubHandle *arc, register __d0 long ErrorCode)
{
   return NULL;
}
LONG __saveds __asm XarsDirect(register __a0 struct XarSubHandle *arc, register __a1 const UBYTE *command)
{
   return XARERR_OPERATION_NOT_SUPPORTED;
}

long __saveds __asm XarsGetPackMode(register __a0 struct XarSubLock *lock, register __a1 struct XarPackMode *mode)
{
   strcpy(mode->ID,"Dummy");
   mode->Description[0]=0;
   mode->Mode=100;
   return XARERR_NO_ERROR;
}



