
// DYNAMIC HUFFMAN ENCODING
// copyright (c) 1998 by Z0MBiE/29A

// 28-12-98 15:10:33

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>

typedef unsigned char   byte;
typedef unsigned int    cod;
#define COD_BITS        32

void error(const char *errmsg)
  {
    printf("%s",errmsg);
    exit(1);
  }

int htree_count[511];
int htree_next [511];
int htree_prev0[511];
int htree_prev1[511];
int htree_bit  [511];
cod htree_code [256];
int htree_len  [256];
int htree_max;
int htree_charcount;

int htree_findmin(int n)
  {
    int j = -1;
    int v = -1;
    for (int i=0; i<htree_max; i++)
      if (htree_next[i]==-1)
      if ((v>htree_count[i]) || (v==-1))
      if (i != n)
        {
          v = htree_count[i];
          j = i;
        }
    return j;
  }

void htree_build(void)
  {
    int a,b,s;

    for(int i=0; i<511; i++)
      {
        htree_next[i] = -1;
        htree_prev0[i] = -1;
        htree_prev1[i] = -1;
      }

    s = 0;
    for (int i=0; i<256; i++) s += htree_count[i];

    htree_max = 256;
    for (;;)
      {
        a = htree_findmin(-1);
        if (a == -1) error("error 1\n");
        b = htree_findmin( a);
        if (b == -1) break;
        htree_next[a] = htree_max;
        htree_next[b] = htree_max;
        htree_prev0[htree_max] = a;
        htree_prev1[htree_max] = b;
        htree_bit[a] = 0;
        htree_bit[b] = 1;
        htree_count[htree_max] = htree_count[a] + htree_count[b];
        htree_max++;

        if (htree_max > 511) error("error 2\n");
      }

    if (s != htree_count[htree_max-1]) error("error 3\n");

    for (int i=0; i<256; i++)
      {
        htree_code[i] = 0;
        htree_len [i] = 0;

        a = i;
        for (;;)
          {
            if (htree_len[i] >= COD_BITS)
              error("error 4\n");
            if (htree_next[a] == -1) break;

            htree_code[i] = (htree_code[i] << 1) | htree_bit[a];
            htree_len[i]++;

            a = htree_next[a];
          }
      }

  }

void htree_init(void)
  {
    for (int i=0; i<256; i++) htree_count[i] = 1;
    htree_charcount=0;
  }

void htree_update(byte c)
  {
    htree_count[c]++;

    htree_charcount++;
    if ((htree_charcount&63)==0) htree_build();
  }

void compressfile(const char *ifile, const char *ofile)
  {
    FILE *i, *o;
    byte c;
    int s,len;
    cod code;

    i = fopen(ifile,"rb");
    if (i == NULL) error("input file reading error\n");
    o = fopen(ofile,"wb");
    if (o == NULL) error("output file writing error\n");

    printf("- compressing %s into %s\n",ifile,ofile);

    s = filelength(fileno(i));
    fwrite(&s, 1,sizeof(s), o);

    htree_init();
    htree_build();

    code = 0;
    len  = 0;

    for (;;)
      {
        c = fgetc(i);
        if (feof(i)) break;

        code |= htree_code[c] << len;
        len  += htree_len[c];

        if (len > COD_BITS)
          {
            printf("c=%i\n",c);
            error("error 6\n");
          }

        while (len >= 8)
          {
            fputc(code&255,o);
            code >>= 8;
            len -= 8;
          }

        htree_update(c);

      }
    if (len != 0)
      fputc(code&255,o);

    fclose(i);
    fclose(o);

    printf("done\n");

  }

void decompressfile(const char *ifile, const char *ofile)
  {
    FILE *i, *o;
    int s,j,len;
    byte c;
    cod code;

    i = fopen(ifile,"rb");
    if (i == NULL) error("input file reading error\n");
    o = fopen(ofile,"wb");
    if (o == NULL) error("output file writing error\n");

    printf("- decompressing %s into %s\n",ifile,ofile);

    fread(&s, 1,sizeof(s), i);

    htree_init();
    htree_build();

    code = 0;
    len  = 0;

    for (;;)
      {
        j = htree_max-1;

        for (;;)
          {
            if (len==0)
              {
                c = fgetc(i);
                if (feof(i)) break;
                code = c;
                len  = 8;
              }
            if ((len==0)&&(feof(i))) break;

            if ((code&1)==0)
              j = htree_prev0[j];
            else
              j = htree_prev1[j];

            if (j==-1) error("error 5\n");

            code >>= 1;
            len--;

            if (j < 256) break;
          }
        if ((len==0)&&(feof(i))) break;

        fputc(j, o);
        if (--s==0) break;

        htree_update(j);
      }

    fclose(i);
    fclose(o);

    printf("done\n");
  }

void main(int argc, char *argv[])
  {
    printf("Huffman Encoding (dynamic) demo  32-bit/CPP  by Z0MBiE/29A\n");
    if (argc != 4) error("syntax: huffd e|d <infile> <outfile>\n");

    if (stricmp(argv[1],"e")==0)
      {
        compressfile  (argv[2], argv[3]);
        return;
      }
    if (stricmp(argv[1],"d")==0)
      {
        decompressfile(argv[2], argv[3]);
        return;
      }

    error("unknown command\n");

  }

