Logo Search packages:      
Sourcecode: macutils version File versions  Download package

lzh.c

#include "macunpack.h"
#ifdef LZH
#include "globals.h"
#include "lzh.h"
#include "crc.h"
#include "../fileio/wrfile.h"
#include "../fileio/machdr.h"
#include "../util/masks.h"
#include "../util/util.h"
#include "bits_be.h"

#define LZ5LOOKAHEAD    18    /* look ahead buffer size for LArc */
#define LZ5BUFFSIZE     8192
#define LZ5MASK         8191
#define LZSLOOKAHEAD    17
#define LZSBUFFSIZE     4096
#define LZSMASK         4095
#define LZBUFFSIZE      8192  /* Max of above buffsizes */

extern char *malloc();
extern char *realloc();
extern void de_lzah();
extern unsigned char (*lzah_getbyte)();
extern void de_lzh();

typedef struct methodinfo {
      char *name;
      int number;
};

static struct methodinfo methods[] = {
    {"-lh0-", lh0},
    {"-lh1-", lh1},
    {"-lh2-", lh2},
    {"-lh3-", lh3},
    {"-lh4-", lh4},
    {"-lh5-", lh5},
    {"-lz4-", lz4},
    {"-lz5-", lz5},
    {"-lzs-", lzs}
};
static char *lzh_archive;
static char *lzh_pointer;
static char *lzh_data;
static char *lzh_finfo;
static int lzh_fsize;
static int lzh_kind;
static int oldsize;
static char *lzh_file;
static int lzh_filesize;
static char *lzh_current;
static char *tmp_out_ptr;
static char lzh_lzbuf[LZBUFFSIZE];

static int lzh_filehdr();
static int lzh_checkm();
static char *lzh_methname();
static void lzh_wrfile();
static void lzh_skip();
static void lzh_nocomp();
#ifdef UNTESTED
static void lzh_lzss1();
static void lzh_lzss2();
#endif /* UNTESTED */
static void lzh_lzah();
static unsigned char lzh_getbyte();
#ifdef UNDEF
static void lzh_lh2();
static void lzh_lh3();
#endif /* UNDEF */
#ifdef UNTESTED
static void lzh_lzh12();
#endif /* UNTESTED */
static void lzh_lzh13();

void lzh(kind)
int kind;
{
    struct fileHdr filehdr;
    int m, i, j;
    char loc_name[64];
    char dirinfo[INFOBYTES];

    updcrc = arc_updcrc;
    crcinit = arc_crcinit;
    write_it = 1;
    lzh_fsize = 0;
    lzh_kind = kind;
    if(lzh_archive == NULL) {
      lzh_archive = malloc((unsigned)in_data_size);
      oldsize = in_data_size;
    } else if(in_data_size > oldsize) {
      lzh_archive = realloc(lzh_archive, (unsigned)in_data_size);
      oldsize = in_data_size;
    }
    if(lzh_archive == NULL) {
      (void)fprintf(stderr, "Insufficient memory for archive.\n");
      exit(1);
    }
    if(fread(lzh_archive, 1, in_data_size, infp) != in_data_size) {
      (void)fprintf(stderr, "Can't read archive.\n");
#ifdef SCAN
      do_error("macunpack: Can't read archive");
#endif /* SCAN */
      exit(1);
    }
    lzh_pointer = lzh_archive;
    while(1) {
      if(in_data_size == 0) {
          break;
      }
      if(lzh_filehdr(&filehdr) == 0) {
          break;
      }
      m = lzh_checkm(&filehdr);
      if(m < 0) {
          (void)fprintf(stderr,
                "Skipping file: \"%s\"; unknown method: %.5s.\n",
                text, filehdr.method);
          lzh_skip(&filehdr);
          continue;
      }
      if(!write_it) {
          /*  We are skipping a folder.  Skip the file if lzh_finfo is a
            prefix of or identical to the folder info in the file. */
          if(lzh_fsize <= filehdr.extendsize &&
             !strncmp(lzh_finfo, filehdr.extend, lzh_fsize)) {
            /* It was true, so we skip. */
            lzh_skip(&filehdr);
            continue;
          }
          /*  We have left the folder we were skipping. */
      }
      /*  Now we must leave folders until lzh_finfo is a proper prefix or
          identical to the folder info in the file. */
      while(lzh_fsize > filehdr.extendsize ||
            strncmp(lzh_finfo, filehdr.extend, lzh_fsize)) {
          /*  Not a proper prefix, leave folder.  First determine which! */
          i = lzh_fsize - 1;
          while(--i >= 0 && lzh_finfo[i] != ':');
          i = i + 1;
          transname(lzh_finfo + i, loc_name, lzh_fsize - i - 1);
          lzh_fsize = i;
          if(write_it) {
            indent--;
            if(!info_only) {
                enddir();
            }
            if(list) {
                do_indent(indent);
                (void)fprintf(stderr, "leaving folder \"%s\"\n", loc_name);
            }
          }
          write_it = 1;
      }
      write_it = 1;
      /*  lzh_finfo is a proper prefix or identical, just show so. */
      lzh_finfo = filehdr.extend;
      /*  Now enter directories while lzh_finfo is smaller than extend. */
      while(lzh_fsize < filehdr.extendsize) {
          i = lzh_fsize;
          while(lzh_finfo[++i] != ':');
          transname(lzh_finfo + lzh_fsize, loc_name, i - lzh_fsize);
          for(j = 0; j < INFOBYTES; j++) {
            dirinfo[j] = 0;
          }
          dirinfo[I_NAMEOFF] = i - lzh_fsize;
          copy(dirinfo + I_NAMEOFF + 1, lzh_finfo + lzh_fsize, i - lzh_fsize);
          lzh_fsize = i + 1;
          if(list) {
            do_indent(indent);
            (void)fprintf(stderr, "folder=\"%s\"", loc_name);
            if(query) {
                write_it = do_query();
            } else {
                (void)fputc('\n', stderr);
            }
            if(write_it) {
                indent++;
            }
          }
          if(write_it && !info_only) {
            do_mkdir(loc_name, dirinfo);
          }
          if(!write_it) {
            break;
          }
      }
      if(!write_it) {
          lzh_skip(&filehdr);
      } else {
          lzh_wrfile(&filehdr, m);
      }
    }
    /*  Leaving some more directories! */
    while(lzh_fsize != 0) {
      i = lzh_fsize - 1;
      while(--i >= 0 && lzh_finfo[i] != ':');
      i = i + 1;
      transname(lzh_finfo + i, loc_name, lzh_fsize - i - 1);
      lzh_fsize = i;
      if(write_it) {
      }
      if(write_it) {
          indent--;
          if(!info_only) {
            enddir();
          }
          if(list) {
            do_indent(indent);
            (void)fprintf(stderr, "leaving folder \"%s\"\n", loc_name);
          }
      }
    }
}

static int lzh_filehdr(f)
struct fileHdr *f;
{
    register int i;
    char *hdr;
    int c;
    int ext_ptr;
    int chk_sum = 0;
    char *ptr;

    if(in_data_size <= 0) {
      return 0;
    }
    for(i = 0; i < INFOBYTES; i++) {
      info[i] = '\0';
    }
    hdr = lzh_pointer;
    in_data_size -= 2;
    lzh_pointer += 2;
    if(in_data_size < 0) {
      in_data_size++;
    }
    f->hsize = (unsigned char)hdr[L_HSIZE];
    if(f->hsize == 0) {
      return 0;
    }
    f->hcrc = (unsigned char)hdr[L_HCRC];
    ptr = hdr + L_METHOD;
    in_data_size -= f->hsize;
    lzh_pointer += f->hsize;
    copy(&(f->method[0]), hdr + L_METHOD, 5);
    f->psize = get4i(hdr + L_PSIZE);
    f->upsize = get4i(hdr + L_UPSIZE);
    f->lastmod = get4i(hdr + L_LASTMOD);
    f->attribute = hdr[L_ATTRIBUTE + 1];
    if(f->attribute < 2) {
      for(i = 0; i < f->hsize; i++) {
          chk_sum += *ptr++;
      }
      chk_sum &= BYTEMASK;
      if(chk_sum != f->hcrc) {
          (void)fprintf(stderr,
                "Header checksum error; got %.2x, must be %.2x.\n",
                chk_sum, f->hcrc);
#ifdef SCAN
          do_error("macunpack: Header checksum error");
#endif /* SCAN */
          exit(1);
      }
      f->nlength = (unsigned char)hdr[L_NLENGTH];
      info[I_NAMEOFF] = f->nlength;
      copy(info + I_NAMEOFF + 1, hdr + L_NAME, (int)f->nlength);
      transname(hdr + L_NAME, text, (int)f->nlength);
      ext_ptr = L_NLENGTH + f->nlength + 1;
      f->crc = get2i(hdr + ext_ptr + L_CRC);
      if(f->attribute == 1) {
          f->etype = hdr[ext_ptr + L_ETYPE];
          f->extendsize = hdr[ext_ptr + L_EXTENDSZ];
          f->extend = hdr + ext_ptr + L_EXTEND;
      } else {
          f->extend = NULL;
          f->extendsize = 0;
      }
    } else if(f->attribute == 2) {
      in_data_size += 2;
      lzh_pointer -= 2;
      f->nlength = hdr[L_2EXTENDSZ] - 3;
      info[I_NAMEOFF] = f->nlength;
      copy(info + I_NAMEOFF + 1, hdr + L_2EXTEND + 2, (int)f->nlength);
      transname(hdr + L_2EXTEND + 2, text, (int)f->nlength);
      ext_ptr = 
      f->crc = get2i(hdr + L_2CRC);
      f->etype = hdr[L_2ETYPE];
      ext_ptr = L_2EXTEND + 2 + f->nlength;
      f->extendsize = hdr[ext_ptr + L_EEXTENDSZ];
      f->extend = hdr + ext_ptr + L_EEXTEND;
    } else {
      (void)fprintf(stderr, "Unknown file header format (%d).\n",
            (int)f->attribute);
#ifdef SCAN
      do_error("macunpack: Unknown file header format");
#endif /* SCAN */
      exit(1);
    }
    if(f->extend != NULL) {
      if(f->extendsize > 5) {
          f->extend += 2;
          hdr = f->extend;
          f->extendsize -= 3;
          for(i = 0; i < f->extendsize; i++) {
            c = *hdr++;
            if((c & BYTEMASK) == BYTEMASK) {
                hdr[-1] = ':';
                c = ':';
            }
          }
          c = *hdr++;
          if(c == 5) {
              hdr += 5;
          }
      } else {
           if(f->extendsize == 5) {
            hdr = f->extend;
            f->extend = NULL;
            f->extendsize = 0;
            hdr += 5;
          } else {
            hdr = f->extend;
            f->extend = NULL;
            f->extendsize = 0;
          }
      }
    } else {
      hdr = hdr + ext_ptr;
    }
    lzh_data = hdr;
    if(f->attribute != 0) {
      lzh_data++;
    }
    return 1;
}

static int lzh_checkm(f)
struct fileHdr *f;
{
    int i, nummeth;
    char *meth;

    meth = f->method;
    nummeth = sizeof(methods) / sizeof(struct methodinfo);
    for(i = 0; i < nummeth; i++) {
      if(!strncmp(methods[i].name, meth, 5)) {
          return methods[i].number;
      }
    }
    return -1;
}

static char *lzh_methname(n)
int n;
{
    if(n > sizeof(methods) / sizeof(struct methodinfo)) {
      return NULL;
    }
    return methods[n].name;
}

static void lzh_wrfile(filehdr, method)
struct fileHdr *filehdr;
int method;
{
    char ftype[5], fauth[5];
    int rsrcLength, dataLength;
    int doit;
    char *mname;
    unsigned long crc;

    if(filehdr->upsize > lzh_filesize) {
      if(lzh_filesize == 0) {
          lzh_file = malloc((unsigned)filehdr->upsize);
      } else {
          lzh_file = realloc(lzh_file, (unsigned)filehdr->upsize);
      }
      if(lzh_file == NULL) {
          (void)fprintf(stderr, "Insufficient memory to unpack file.\n");
          exit(1);
      }
    }
    switch(method) {
    case lz4:
      lzh_nocomp((unsigned long)128);
      break;
#ifdef UNTESTED
    case lz5:
      lzh_lzss1((unsigned long)128);
      break;
    case lzs:
      lzh_lzss2((unsigned long)128);
      break;
#endif /* UNTESTED */
    case lh0:
      lzh_nocomp((unsigned long)128);
      break;
    case lh1:
      lzh_lzah((unsigned long)128);
      break;
#ifdef UNDEF
    case lh2:
      lzh_lh2((unsigned long)128);
      break;
    case lh3:
      lzh_lh3((unsigned long)128);
      break;
#endif /* UNDEF */
#ifdef UNTESTED
    case lh4:
      lzh_lzh12((unsigned long)128);
      break;
#endif /* UNTESTED */
    case lh5:
      lzh_lzh13((unsigned long)128);
      break;
    default:
      mname = lzh_methname(method);
      if(mname != NULL) {
          do_indent(indent);
          (void)fprintf(stderr,
                "\tSorry, packing method not yet implemented.\n");
          do_indent(indent);
          (void)fprintf(stderr, "File = \"%s\"; ", text);
          (void)fprintf(stderr, "method = %s, skipping file.\n", mname);
          lzh_skip(filehdr);
          return;
      }
      (void)fprintf(stderr,
            "There is something very wrong with this program!\n");
#ifdef SCAN
      do_error("macunpack: program error");
#endif /* SCAN */
      exit(1);
    }
    /* Checks whether everything is packed as MacBinary. */
    if(*lzh_file != 0 /* More checks possible here. */) {
      do_indent(indent);
      (void)fprintf(stderr, "File = \"%s\" ", text);
      (void)fprintf(stderr, "not packed in MacBinary, skipping file.\n");
#ifdef SCAN
      do_error("macunpack: not MacBinary");
#endif /* SCAN */
      lzh_skip(filehdr);
      return;
    }
    copy(info, lzh_file, 128);
    rsrcLength = get4(info + I_RLENOFF);
    dataLength = get4(info + I_DLENOFF);
    transname(info + I_TYPEOFF, ftype, 4);
    transname(info + I_AUTHOFF, fauth, 4);
    if(list) {
      do_indent(indent);
      (void)fprintf(stderr,
            "name=\"%s\", type=%4.4s, author=%4.4s, data=%ld, rsrc=%ld",
            text, ftype, fauth, (long)dataLength, (long)rsrcLength);
    }
    if(info_only) {
      doit = 0;
    } else {
      doit = 1;
    }
    if(query) {
      doit = do_query();
    } else if(list) {
      (void)fputc('\n', stderr);
    }
    if(doit) {
      define_name(text);
      start_info(info, (unsigned long)rsrcLength, (unsigned long)dataLength);
    }
    switch(method) {
    case lz4:
      if(verbose) {
          (void)fprintf(stderr, "\tNo Compression (%.5s)", filehdr->method);
      }
      if(doit) {
          lzh_nocomp(filehdr->upsize);
      }
      break;
#ifdef UNTESTED
    case lz5:
      if(verbose) {
          (void)fprintf(stderr, "\tLZSS (%.5s) compressed (%4.1f%%)",
                filehdr->method, 100.0 * filehdr->psize / filehdr->upsize);
      }
      if(doit) {
          lzh_lzss1(filehdr->upsize);
      }
      break;
    case lzs:
      if(verbose) {
          (void)fprintf(stderr, "\tLZSS (%.5s) compressed (%4.1f%%)",
                filehdr->method, 100.0 * filehdr->psize / filehdr->upsize);
      }
      if(doit) {
          lzh_lzss2(filehdr->upsize);
      }
      break;
#endif /* UNTESTED */
    case lh0:
      if(verbose) {
          (void)fprintf(stderr, "\tNo Compression (%.5s)", filehdr->method);
      }
      if(doit) {
          lzh_nocomp(filehdr->upsize);
      }
      break;
    case lh1:
      if(verbose) {
          (void)fprintf(stderr, "\tLZAH (%.5s) compressed (%4.1f%%)",
                filehdr->method, 100.0 * filehdr->psize / filehdr->upsize);
      }
      if(doit) {
          lzh_lzah(filehdr->upsize);
      }
      break;
#ifdef UNDEF
    case lh2:
      if(verbose) {
          (void)fprintf(stderr, "\tLZAH (%.5s) compressed (%4.1f%%)",
                filehdr->method, 100.0 * filehdr->psize / filehdr->upsize);
      }
      if(doit) {
          lzh_lh2(filehdr->upsize);
      }
      break;
    case lh3:
      if(verbose) {
          (void)fprintf(stderr, "\tLZH (%.5s) compressed (%4.1f%%)",
                filehdr->method, 100.0 * filehdr->psize / filehdr->upsize);
      }
      if(doit) {
          lzh_lzh3(filehdr->upsize);
      }
      break;
#endif /* UNDEF */
#ifdef UNTESTED
    case lh4:
      if(verbose) {
          (void)fprintf(stderr, "\tLZH (%.5s) compressed (%4.1f%%)",
                filehdr->method, 100.0 * filehdr->psize / filehdr->upsize);
      }
      if(doit) {
          lzh_lzh12(filehdr->upsize);
      }
      break;
#endif /* UNTESTED */
    case lh5:
      if(verbose) {
          (void)fprintf(stderr, "\tLZH (%.5s) compressed (%4.1f%%)",
                filehdr->method, 100.0 * filehdr->psize / filehdr->upsize);
      }
      if(doit) {
          lzh_lzh13(filehdr->upsize);
      }
    }
    if(doit) {
      crc = (*updcrc)(INIT_CRC, lzh_file, filehdr->upsize);
      if(filehdr->crc != crc) {
          (void)fprintf(stderr,
                "CRC error on file: need 0x%04x, got 0x%04x\n",
                filehdr->crc, (int)crc);
#ifdef SCAN
          do_error("macunpack: CRC error on file");
#endif /* SCAN */
          exit(1);
      }
      start_data();
      copy(out_ptr, lzh_file + 128, (int)(filehdr->upsize - 128));
    }
    if(verbose) {
      (void)fprintf(stderr, ".\n");
    }
    if(doit) {
      end_file();
    }
    lzh_skip(filehdr);
}

static void lzh_skip(filehdr)
struct fileHdr *filehdr;
{
    lzh_pointer += filehdr->psize;
    in_data_size -= filehdr->psize;
}

/*---------------------------------------------------------------------------*/
/*    -lz4- and -lh0: No compression                                   */
/*---------------------------------------------------------------------------*/
static void lzh_nocomp(obytes)
unsigned long obytes;
{
    copy(lzh_file, lzh_data, (int)obytes);
}

#ifdef UNTESTED
/*---------------------------------------------------------------------------*/
/*    -lz5-: LZSS compression, variant 1                         */
/*---------------------------------------------------------------------------*/
static void lzh_lzss1(obytes)
unsigned long obytes;
{
    int mask, ch, lzcnt, lzptr, ptr, count;
    char *p = lzh_lzbuf;
    int i, j;

    for(i = 0; i < 256; i++) {
      for(j = 0; j < 13; j++) {
          *p++ = i;
      }
    }
    for(i = 0; i < 256; i++) {
      *p++ = i;
    }
    for(i = 0; i < 256; i++) {
      *p++ = 255 - i;
    }
    for(i = 0; i < 128; i++) {
      *p++ = 0;
    }
    for(i = 0; i < 128; i++) {
      *p++ = ' ';
    }

    tmp_out_ptr = out_ptr;
    out_ptr = lzh_file;
    ptr = LZ5BUFFSIZE - LZ5LOOKAHEAD;
    count = 0;
    lzh_current = lzh_data;
    while(obytes != 0) {
      if(count == 0) {
          mask = *lzh_current++ & BYTEMASK;
          count = 8;
      }
      count--;
      ch = *lzh_current++ & BYTEMASK;
      if ((mask & 1) != 0) {
          *out_ptr++ = ch;
          lzh_lzbuf[ptr++] = ch;
          ptr &= LZ5MASK;
          obytes--;
      } else {
          lzcnt = *lzh_current++;
          lzptr = (ch & 0x00ff) | ((lzcnt << 4) & 0x0f00);
          lzcnt = (lzcnt & 0x000f) + 3;
          obytes -= lzcnt;
          do {
            ch = lzh_lzbuf[lzptr++];
            lzh_lzbuf[ptr++] = ch;
            *out_ptr++ = ch;
            lzptr &= LZ5MASK;
            ptr &= LZ5MASK;
          } while (--lzcnt != 0) ;
      }
      mask >>= 1;
    }
    out_ptr = tmp_out_ptr;
}

/*---------------------------------------------------------------------------*/
/*    -lzs-: LZSS compression, variant 2                         */
/*---------------------------------------------------------------------------*/
static void lzh_lzss2(obytes)
unsigned long obytes;
{
    int ch, lzcnt, lzptr, ptr, i;

    tmp_out_ptr = out_ptr;
    out_ptr = lzh_file;
    ptr = LZSBUFFSIZE - LZSLOOKAHEAD;
    for(i = 0; i < ptr; i++) {
      lzh_lzbuf[i] = ' ';
    }
    for(i = ptr; i < LZSBUFFSIZE; i++) {
      lzh_lzbuf[i] = 0;
    }
    bit_be_init_getbits();
    bit_be_filestart = lzh_data;
    bit_be_inbytes = -1;
    while(obytes != 0) {
      if(bit_be_getbits(1) == 0) {
          ch = bit_be_getbits(8);
          *out_ptr++ = ch;
          lzh_lzbuf[ptr++] = ch;
          ptr &= LZSMASK;
          obytes--;
      } else {
          lzptr = bit_be_getbits(11);
          lzcnt = bit_be_getbits(4) + 3;
          obytes -= lzcnt;
          do {
            ch = lzh_lzbuf[lzptr++];
            lzh_lzbuf[ptr++] = ch;
            *out_ptr++ = ch;
            lzptr &= LZSMASK;
            ptr &= LZSMASK;
          } while (--lzcnt != 0) ;
      }
    }
    out_ptr = tmp_out_ptr;
}
#endif /* UNTESTED */

/*---------------------------------------------------------------------------*/
/*    -lh1-: LZ compression plus adaptive Huffman encoding             */
/*---------------------------------------------------------------------------*/
static void lzh_lzah(obytes)
unsigned long obytes;
{
    lzh_current = lzh_data + 2; /* SKIPPING BLOCKSIZE! */
    tmp_out_ptr = out_ptr;
    out_ptr = lzh_file;
    lzah_getbyte = lzh_getbyte;
    de_lzah(obytes);
    out_ptr = tmp_out_ptr;
}

static unsigned char lzh_getbyte()
{
    return *lzh_current++;
}

#ifdef UNDEF
/*---------------------------------------------------------------------------*/
/*    -lh2-: LZ** compression                                    */
/*---------------------------------------------------------------------------*/
static void lzh_lh2(obytes)
unsigned long obytes;
{
}

/*---------------------------------------------------------------------------*/
/*    -lh3-: LZ** compression                                    */
/*---------------------------------------------------------------------------*/
static void lzh_lh3(obytes)
unsigned long obytes;
{
}
#endif /* UNDEF */

#ifdef UNTESTED
/*---------------------------------------------------------------------------*/
/*    -lh4-: LZ(12) compression plus Huffman encoding                  */
/*---------------------------------------------------------------------------*/
static void lzh_lzh12(obytes)
unsigned long obytes;
{
    lzh_current = lzh_data;
    tmp_out_ptr = out_ptr;
    out_ptr = lzh_file;
    /* Controlled by obytes only */
    de_lzh((long)(-1), (long)obytes, &lzh_current, 12);
    out_ptr = tmp_out_ptr;
}
#endif /* UNTESTED */

/*---------------------------------------------------------------------------*/
/*    -lh5-: LZ(13) compression plus Huffman encoding                  */
/*---------------------------------------------------------------------------*/
static void lzh_lzh13(obytes)
unsigned long obytes;
{
    lzh_current = lzh_data;
    tmp_out_ptr = out_ptr;
    out_ptr = lzh_file;
    /* Controlled by obytes only */
    de_lzh((long)(-1), (long)obytes, &lzh_current, 13);
    out_ptr = tmp_out_ptr;
}
#else /* LZH */
int lzh; /* keep lint and some compilers happy */
#endif /* LZH */


Generated by  Doxygen 1.6.0   Back to index