/*************************************************************************/
/*                                                                       */
/*                    DLZW.C Décompression de fichiers                   */
/*              Méthode Lempel-Ziv Welch variable à 15 bits              */
/* Décompresseur de fichiers V1.00 CopyRight Christian BRUNON 15-09-1993 */
/*                                                                       */
/*             +------------------------------------------+              */
/*             | Logiciel placé dans le domaine SHAREWARE |              */
/*             +------------------------------------------+              */
/* Conformément à la législation concernant les logiciels placés dans le */
/* domaine SHAREWARE, la diffusion de ce logiciel est libre. L'auteur ne */
/*  demande, en cas d'utilisation, qu'une modique contribution de 50 FF  */
/* ou 90 FF en monnaie étrangère ( dollars, marks, franc suisses, ... )  */
/*                            à adresser à :                             */
/*                                                                       */
/*                           Christian BRUNON                            */
/*                        30 Rue Georges Brassens                        */
/*                      43140 LA SEAUVE SUR SEMENE                       */
/*                                FRANCE                                 */
/*                                                                       */
/*            Fichier source Lattice C AmigaDOS Version 5.04             */
/*                                                                       */
/*************************************************************************/

#define strlen __builtin_strlen
#define printf __builtin_printf
#define strcpy __builtin_strcpy
extern int printf (char *,...);
extern char *strcat(char *, char *);
extern int strlen(char *);
extern char *strcpy(char *, char *);

extern struct DosLibrary *DOSBase;
extern struct ExecBase   *SysBase;

/* Fichiers INCLUDE */
/*#include "CPRG:LZ.H"*/

/* Définitions de pseudo-types */
#define BPTR  long
#define BCPL  long
#define UBYTE unsigned char
#define LONG  long
#define ULONG unsigned long
/* ATTENTION une variable de type "int" est codée sur 4 octets !!! */
/* Une variable de type "short" est codée sur 2 octets.            */
#define WORD unsigned short

/* Pointeur sur une structure FileInfoBlock */
#define PtrFIB (struct FileInfoBlock *)

#define NULL 0L

/* Drapeaux de compression */
#define COMP_LZW     0X8000
/*
#define COMP_HUF     0X4000
*/
#define COMP_LZW_HUF 0XC000

/* VARIABLES GLOBALES */
BPTR FIn,  /* Fichier à décompresser */
     FOut; /* Fichier décompressé    */

UBYTE NomFicIn [60], /* Nom du fichier à décompresser */
      NomFicOut[60], /* Nom du fichier décompressé    */
      CodeErreur=0;  /* Code d'erreur                 */

/* Structure utilisée par la structure FileInfoBlock */
struct DateStamp
  {
   LONG ds_Days,
        ds_Minute,
        ds_Tick;
  };

/* Structure utilisée pour Examine(), doit étre alignée sur une adresse divisible par 4 */
struct FileInfoBlock
  {
   LONG   fib_DiskKey;
   LONG   fib_DirEntryType;  /* Type of Directory. If < 0, then a plain file. If > 0 a directory */
   char   fib_FileName[108]; /* Null terminated. Max 30 chars used for now */
   LONG   fib_Protection;    /* bit mask of protection, rwed are 3-0.      */
   LONG   fib_EntryType;
   LONG   fib_Size;          /* Number of bytes in file  */
   LONG   fib_NumBlocks;     /* Number of blocks in file */
   struct DateStamp fib_Date;/* Date file last changed   */
   char   fib_Comment[80];   /* Null terminated comment associated with file */
   char   fib_Reserved[36];
  };

ULONG LongFicIn, LongFicOut=0L;

/* CONSTANTES DE CODES D'ERREURS */
#define SyntaxError        1
#define FileOpenReadError  2
#define CreateError        3
#define FileBufError       4
#define WriteError         5
#define ReadError          6
#define DeCompBufError     7
#define IOError            8
#define CompressError      9
#define BreakError        10

static UBYTE *ListeErreurs[] =
     {
      "\n",
#ifdef TINY_LZW
      "Décompresseur économique Lempel-Ziv Welch variable à 15 bits\nUtilisation : DLZWT [-DEST] Fichier1[.LZWT] [-DEST] [Fichier2[.LZWT]] [...] [-Destination]\n",
#else
      "Décompresseur Lempel-Ziv Welch variable à 15 bits\nUtilisation : DLZW [-DEST] Fichier1[.LZW] [-DEST] [Fichier2[.LZW]] [...] [-Destination]\n",
#endif
      "\aFichier inexistant ou illisible\n",
      "\aErreur en création\n",
      "\aPas assez de mémoire pour les buffers de fichiers\n",
      "\aErreur en écriture\n",
      "\aErreur en lecture\n",
      "\aPas assez de mémoire pour les buffers de décompression\n",
      "\aErreur DOS ( IoErr() )\n",
      "\aErreur de compression\n",
      "\a*** BREAK\n"
     };

/* Déclarations pour les zones mémoires allouées de taille BUFSIZE */
/*#define BUFSIZE 5003L*/
#ifdef TINY_LZW
#define BUFSIZE 16384L
#else
#define BUFSIZE 32767L
#endif
/* La valeur de BUFSIZE doit être inférieure à 32768.
 * La longueur du buffer compressé est lue sur 2 octets avant de
 * décompresser le buffer compressé.
 * Comme on lit le flag de compression ( buffer de données compressé ou non ),
 * dans le bit de poids le plus fort, on ne peut utiliser une longueur de données
 * supérieure à 32767.
 * La valeur de BUFSIZE doit être supérieure à 259 puisque on utilise le
 * buffer du fichier à décompresser pour stocker le contenu d'une structure
 * FileInfoBlock d'une longueur de 260 octets.
 */
#if BUFSIZE > 32767
Erreur : La valeur de BUFSIZE doit être inférieure à 32768
#endif
#if BUFSIZE < 260
Erreur : La valeur de BUFSIZE doit être supérieure à 259
#endif

#define BufIn  Buffer[0]
#define BufOut Buffer[1]

/* Deux zones mémoires pour les fichiers */
static UBYTE *Buffer[2] = {
                           NULL, /* Buffer en lecture  */
                           NULL  /* Buffer en écriture */
                          };

extern int (*_ONBREAK)();

/**************************************************************************/

int CXBRK()
{
 CodeErreur=BreakError;

 return(0);
}

/**************************************************************************/

void LibererMemoire()
{
 /* Déclarations et fonctions de Exec.Library */
 extern void FreeMem(void *,long);
 #pragma syscall FreeMem d2 902

 register UBYTE i;

 for(i=0;i<2;i++) if(Buffer[i]) FreeMem(Buffer[i],BUFSIZE);
}

/**************************************************************************/

UBYTE ReserverMemoire()
{
 /* Déclarations et fonctions de Exec.Library
  * Type de mémoires : l'un des 4 premiers paramètres est à combiner
  * éventuellement avec l'un des 2 derniers paramètres.
  * MEMF_ANY     0L
  * MEMF_PUBLIC  1L<<0
  * MEMF_CHIP    1L<<1
  * MEMF_FAST    1L<<2
  * MEMF_CLEAR   1L<<16
  * MEMF_LARGEST 1L<<17
  */

 /* Mémoire quelconque la première portion suffisamment grande */
 #define MEMF_ANY 0L
 extern void *AllocMem(long,long);
 #pragma syscall AllocMem c6 1002

 register UBYTE i;

 for(i=0;i<2;i++) Buffer[i]=(UBYTE *)AllocMem(BUFSIZE,MEMF_ANY);

 return((UBYTE)(BufIn && BufOut));
}

/**************************************************************************/

UBYTE DeCompresserFichier()
{
 /* Fonctions de DeLZBuffer.O */
 extern long __regargs DeCompLZBuf(unsigned char *,long,unsigned char *,long);
 extern void           DeCompLZBufExit(void);
 extern UBYTE          DeCompLZBufInit(void);

 /* Déclarations et fonctions de Dos.Library */
 #define MODE_OLDFILE 1005L
 #define MODE_NEWFILE 1006L
 /* #define MODE_READWRITE 1004L INUTILE ICI */
 extern BPTR Open(char *,long);
 extern long Read(BPTR,unsigned char *,long);
 extern long Write(BPTR,UBYTE *,long);
 extern long IoErr(void);
 #pragma libcall DOSBase Open 1e 2102
 #pragma libcall DOSBase Read 2a 32103
 #pragma libcall DOSBase Write 30 32103
 #pragma libcall DOSBase IoErr 84 0

 /* MACRO Ecriture du contenu d'un buffer dans le fichier décompressé */
 #define EcrireBuffer(Buffer,LongBuf) {if(Write(FOut,Buffer,LongBuf)!=LongBuf) CodeErreur=WriteError; else LongFicOut+=LongBuf;}

 register WORD LongIn,LongOut;
 register UBYTE FlagCompress;

 LongFicIn=LongFicOut=0L;

 if(!(FIn =Open(NomFicIn ,MODE_OLDFILE))) return(FileOpenReadError);
 if(!(FOut=Open(NomFicOut,MODE_NEWFILE))) return(CreateError);

 if(!DeCompLZBufInit()) CodeErreur=DeCompBufError;

 while((!CodeErreur) && ((WORD)2==(WORD)Read(FIn,BufIn,2L)))
      {
       /* Décodage de l'entête : Longueur du bloc compressé et flag de compression  */
       LongIn=*(WORD *)BufIn;
       FlagCompress=(UBYTE)((LongIn & COMP_LZW)>0);
       LongIn&=~COMP_LZW;

       /* Lecture du bloc */
       if(LongIn!=(WORD) Read(FIn,BufIn,LongIn))
         {
          CodeErreur=CompressError;
          break;
         }

       if(FlagCompress)
           {
            /* Bloc compressé, on le décompresse avant de l'écrire */
            LongOut=(WORD)DeCompLZBuf(BufIn,LongIn,BufOut,BUFSIZE);

            if(LongOut && LongOut<=BUFSIZE) EcrireBuffer(BufOut,LongOut) /* Pas de ';' !! */
            else CodeErreur=CompressError;
           }
       else EcrireBuffer(BufIn,LongIn); /* Bloc non compressé, écrit directement */

       LongFicIn+=LongIn+(WORD)2;
       printf("\r %7lu ( %5lu ) -> %7lu ( %5lu )",LongFicIn,LongIn,LongFicOut,LongOut);
      }

 DeCompLZBufExit();

 if(!CodeErreur && IoErr()) CodeErreur=IOError;

 return(CodeErreur);
}

/**************************************************************************/

int main(int argc,char *argv[])
{
 /* Déclarations de variables externes de la DOS Library */
 extern BPTR CreateDir(char *);
 extern long DeleteFile(char *);
 extern long SetProtection(char *, ULONG);
 extern LONG SetComment(char *, char *);
 extern LONG Examine(BPTR, struct FileInfoBlock *);
 extern void Close(BPTR);
 extern BPTR Lock(char *, LONG);
 extern void UnLock(BPTR);
 #pragma libcall DOSBase CreateDir 78 101
 #pragma libcall DOSBase DeleteFile 48 101
 #pragma libcall DOSBase SetProtection ba 2102
 #pragma libcall DOSBase SetComment b4 2102
 #pragma libcall DOSBase Examine 66 2102
 #pragma libcall DOSBase Close 24 101
 #pragma libcall DOSBase Lock 54 2102
 #pragma libcall DOSBase UnLock 5a 101

 /* Type à passer à la fonction Lock() */
 /* #define SHARED_LOCK    -2L  File is readable by others */
    #define ACCESS_READ    -2L  /* Synonym                 */
 /* #define EXCLUSIVE_LOCK -1L  No other access allowed    */
 /* #define ACCESS_WRITE   -1L  Synonym                    */

 #define UpChar(C) ((C) & (255-32))
 #define LastChar(S) S[strlen(S)-1]

 register BCPL Clef; /* Clef renvoyée par la fonction Lock() */

 register UBYTE NuArg,
                FlagNewDir=0,   /* Changer le répertoire de destination ? */
                NewChemin[80],
                Commentaire[80],
                FileName[108],
                LongNom;        /* Longueur du nom du fichier passé en paramètre */

 register LONG Protection;

 _ONBREAK=CXBRK;

#ifdef TINY_LZW
 printf("V1.00 CopyRight SHAREWARE Christian BRUNON 15-09-1993\n");
#else
 printf("V1.00 CopyRight SHAREWARE Christian BRUNON 15-09-1993\n");
#endif

 /* Pas de paramètre ou paramètre ? */
 if(argc==1 || (argc==2 && argv[1][0]=='?') && (!argv[1][1]))
   {
    printf(ListeErreurs[1]); /* Erreur fatale */
    return(1);
   }

 if(!ReserverMemoire()) CodeErreur=FileBufError;

 if(CodeErreur==0)
 for(NuArg=1 ; NuArg<(UBYTE)argc ; NuArg++)
 if(argv[NuArg][0]=='-')
     /* Le paramètre lu est un nouveau chemin */
     {
      FlagNewDir=(UBYTE)1;
      strcpy(NewChemin,&argv[NuArg][1]);

      if(NewChemin[0] && LastChar(NewChemin)!=':' && LastChar(NewChemin)!='/')
        {
         if(Clef=CreateDir(NewChemin)) UnLock(Clef);

         strcat(NewChemin,"/");
        }
     }
 /* Le paramètre lu est un fichier à décompresser */
 else{
      CodeErreur=0;

      strcpy(NomFicIn,argv[NuArg]);
      LongNom=strlen(NomFicIn);

#ifdef TINY_LZW
      if(NomFicIn[LongNom-5]!='.' || UpChar(NomFicIn[LongNom-4])!='L' || UpChar(NomFicIn[LongNom-3])!='Z' || UpChar(NomFicIn[LongNom-2])!='W' || UpChar(NomFicIn[LongNom-1])!='T')
         strcat(NomFicIn,".LZWT");
#else
      if(NomFicIn[LongNom-4]!='.' || UpChar(NomFicIn[LongNom-3])!='L' || UpChar(NomFicIn[LongNom-2])!='Z' || UpChar(NomFicIn[LongNom-1])!='W')
         strcat(NomFicIn,".LZW");
#endif

      /* On utilitise le Buffer de lecture pour stocker la structure FileInfoBlock
       * Recopie des bits d'attributs de fichier et du commentaire
       * Inutile d'aligner l'adresse de la structure FileInfoBlock,
       * la fonction AllocMem() renvoie toujours une adresse divisible par 4.
       * On utilise directement l'adresse BufIn.
       */
      if(Clef=Lock(NomFicIn,ACCESS_READ))
          {
           /* Test existence et test type = fichier */
           if(Examine(Clef,PtrFIB BufIn) && (PtrFIB BufIn)->fib_DirEntryType<0)
               {
                strcpy(FileName,(PtrFIB BufIn)->fib_FileName);
                LongFicIn=(PtrFIB BufIn)->fib_Size;
                Protection=(PtrFIB BufIn)->fib_Protection;
                strcpy(Commentaire,(PtrFIB BufIn)->fib_Comment);
               }
           else CodeErreur=FileOpenReadError;

           UnLock(Clef);
          }
      else CodeErreur=FileOpenReadError;

      if(CodeErreur){
                     printf("%s %s",NomFicIn,ListeErreurs[CodeErreur]);
                     continue;
                    }

      /* Détermination du nom du fichier décompressé */
      if(FlagNewDir)
          {
           strcpy(NomFicOut,NewChemin);
           strcat(NomFicOut,FileName);
          }
      else strcpy(NomFicOut,NomFicIn);

#ifdef TINY_LZW
      /* Suppression de l'extension .LZWT */
      NomFicOut[strlen(NomFicOut)-5]='\0';
#else
      /* Suppression de l'extension .LZW */
      NomFicOut[strlen(NomFicOut)-4]='\0';
#endif

      /* Affichage des noms des fichiers */
      printf("%s (%lu) -> %s\n  LEMPEL-ZIV WELCH ->     ORIGINAL\n",NomFicIn,LongFicIn,NomFicOut);

      CodeErreur=DeCompresserFichier();

      if(FOut) Close(FOut);
      if(FIn ) Close(FIn);

      /* Recopie des bits d'attributs de fichier et du commentaire */
      if(CodeErreur==0)
        {
         SetProtection(NomFicOut,Protection);
         SetComment(NomFicOut,Commentaire);
        }

      /* Erreur intervenue pendant que le fichier décompressé est ouvert.
       * Le fichier doit étre détruit car certainement incomplet.
       */
      if(CodeErreur>4) DeleteFile(NomFicOut);

      printf(ListeErreurs[CodeErreur]);
     }

 LibererMemoire();

 /* Renvoie le code d'erreur du DERNIER fichier décompressé */
 return(CodeErreur);
}
