home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_C / COMP_JCM.ZIP / COMPRESS.C next >
C/C++ Source or Header  |  1994-01-14  |  9KB  |  372 lines

  1. /*
  2.   Compress.c from Jeffrey C. Moxon, CIS: 73561,1431
  3.  
  4.   GIF file writer converted to file compression routine.  Original GIF file
  5.   reader is from "Bitmapped Graphics, 2nd Edition" by Steve Rimmer from
  6.   McGraw Hill books.  The original GIF writer source can be purchased from:
  7.  
  8.   Alchemy Mindworks Inc.
  9.   P.O. Box 500
  10.   Beeton, Ontario
  11.   L0G 1A0, Canada
  12.  
  13.   Although I wouldn't recomend the book because the code is complex and
  14.   sloppy (like mine is neat!), I spent some time playing with the GIF
  15.   file reader and came up with this.  I don't hold any responsibilities
  16.   for the code, nor do I make any claims to its effectiveness.  It works
  17.   moderately well, is written entirely in C and is totally free - I typed
  18.   in the code from the book, and corrected the 50 some-odd warning messages.
  19.  
  20.   I would like to see a copy with performance improvements if you manage
  21.   any, and/or anything that you produce using this.  As it is, I put minimal
  22.   effort into this, and it still worked out to be fairly valuable.  I also
  23.   have no understanding of the mechanics behind the routine.
  24.  
  25.   Have fun,
  26.   -JCM-
  27.  
  28.   PS,
  29.     The header and main routine aren't necessary.  If you open two files
  30.     and run them through the compress routine the data from the source
  31.     file will be compressed into the target file.
  32.  
  33.     Also, a 'self extracting' module can be made using the dos copy
  34.     command with the /B option and a few changes to the explode routine
  35.     as follows:
  36.  
  37.     1.  compress the files normally
  38.     2.  copy /B explode.exe + compressedfile
  39.     3.  modify explode routine to open itself as a source file
  40.     4.  position file pointer at end of explode.exe/start of data
  41.     5.  de-compress normally.
  42.  
  43. */
  44.  
  45. // Link with wildargs.obj
  46. #include <process.h>
  47. #include <string.h>
  48. #include <ctype.h>
  49. #include <conio.h>
  50. #include <stdio.h>
  51. #include <io.h>
  52.  
  53. #define largest_code    4095 //<- This number is based on the number
  54. #define table_size      5003 // of bits somehow.
  55.  
  56. FILE *sf,*tf;
  57.  
  58. unsigned char code_buffer[259];
  59.  
  60. int oldcode[table_size];
  61. int currentcode[table_size];
  62. unsigned char newcode[table_size];
  63. int min_code_size = 7;    //<- This is the key number for bits stored
  64. int hx,d;
  65. int code_size;
  66. int clear_code;
  67. int eof_code;
  68. int bit_offset;
  69. int bits_left;
  70. int byte_offset;
  71. int max_code;
  72. int free_code;
  73. float percent;
  74. double total_size, compressed_size;
  75.  
  76. void compress(void);
  77. void init_table(int min_code_size);
  78. void write_code(int code);
  79. void flush(int n);
  80. void strmfe(char *new, char *old, char *ext);
  81.  
  82. struct file
  83.        {
  84.         struct ftime i_date;        //Original date of file
  85.         char        name[13];       //Original file name
  86.         long        initial_size;   //Size of original file
  87.         long        stored_size;    //Size of compressed file
  88.         char    reserved[32];    //Future Use
  89.        } files;
  90.  
  91.  
  92. struct head
  93.        {
  94.         char        sig[5];
  95.         int         bytes;
  96.         int         num_files;
  97.         char    reserved[32];
  98.        } header;
  99.  
  100. void display_percent(int c_per);
  101.  
  102. long ten_percent; //Ten percent of file size for display
  103. long c_pos;      //Current offset in file used for display of percent done
  104.  
  105. void main(int argc, char *argv[])
  106. {
  107.  struct ftime f_date;
  108.  int file_number;
  109.  long pos;
  110.  char path[69];
  111.  
  112.  total_size = compressed_size = 0;
  113.  
  114.  if (argc < 2)
  115.  {
  116.   printf("Usage:\nCOMPRESS source target\n");
  117.   return;
  118.  }
  119.  if ((tf = fopen(argv[argc-1], "rb")) != NULL)
  120.  {
  121.   printf("Target file already exists.\n");
  122.   return;
  123.  }
  124.  if ((tf = fopen(argv[argc-1], "wb")) == NULL)
  125.  {
  126.   printf("Error creating target file.\n");
  127.   return;
  128.  }
  129.  
  130.  for(file_number=0;file_number < argc;file_number++)
  131.  {
  132.   argv[argc] = strupr(argv[file_number]);
  133.   strcpy(argv[file_number], argv[argc]);
  134.  }
  135.  
  136.  strcpy(header.sig, "MOX94");
  137.  strncpy(path, argv[0], strlen(argv[0]) - strlen("COMPRESS.EXE"));
  138.  path[strlen(argv[0]) - strlen("COMPRESS.EXE")] = '\0';
  139.  
  140.  header.bytes = (argc-2) * sizeof(files) + sizeof(header);
  141.  header.num_files = argc - 1;
  142.  
  143.  /* Create Header Structure */
  144.  fwrite(&header, sizeof(header), 1, tf);
  145.  fwrite(&files, sizeof(files), argc-2, tf);
  146.  
  147.  for(file_number = 1;file_number < argc - 1;file_number++)
  148.  {
  149.   printf("Compressing:  %13s",argv[file_number]);
  150.   if ((sf = fopen(argv[file_number], "rb")) == NULL)
  151.    {
  152.     printf("Error opening source file.\n");
  153.     return;
  154.    }
  155.  
  156.   strcpy(files.name, argv[file_number]);
  157.   getftime(fileno(sf), &f_date);
  158.   files.i_date = f_date;
  159.   fseek(sf, 0, SEEK_END);
  160.   files.initial_size = ftell(sf);
  161.   total_size += files.initial_size;
  162.   rewind(sf);
  163.   //Determine if text file or binary, and adjust number of bits accordingly
  164.   while(!feof(sf))
  165.    if (fgetc(sf) > 128)
  166.    {
  167.     min_code_size = 8;
  168.     break;
  169.    }
  170.   rewind(sf);
  171.   fseek(tf, 0, SEEK_END);
  172.   pos = ftell(tf);
  173.   compress();
  174.   pos = ftell(tf) - pos;
  175.   fflush(tf);
  176.   files.stored_size = pos;
  177.   compressed_size += files.stored_size;
  178.   percent = (float) files.stored_size/files.initial_size * 100;
  179.   printf("%2.02f%%\n", percent);
  180.   fseek(tf, sizeof(header) + sizeof(files)*(file_number-1), SEEK_SET);
  181.   fwrite(&files, sizeof(files), 1, tf);
  182.   fclose(sf);
  183.  }
  184.  
  185.   fseek(tf, 0, SEEK_END);
  186.   compressed_size = ftell(tf);
  187.  
  188.   fclose(tf);
  189.  
  190.  compressed_size /= 1000;
  191.  total_size /= 1000;
  192.  
  193.  percent = (float) compressed_size/total_size * 100;
  194.  printf("\nTotal:  %.02f kb compressed to %.02f (%3.02f%% of original)\n",
  195.        total_size, compressed_size, percent);
  196. return;
  197. }
  198.  
  199. void compress(void)
  200. {
  201.  int prefix_code;
  202.  int suffix_char;
  203.  static int c_per;
  204.  
  205.  ten_percent = (long) files.initial_size * 0.10;
  206.  c_pos = 0;
  207.  c_per = 0;
  208.  
  209.     /* write initial code size */
  210.     fputc(min_code_size,tf);
  211.  
  212.     /* initialize the encoder */
  213.     bit_offset=0;
  214.  
  215.  
  216.     /* initialize the prefix */
  217.     prefix_code = 0;
  218.     init_table(min_code_size);
  219.  
  220.     /* get a character to compress */
  221.     while((suffix_char=fgetc(sf)) != EOF)
  222.     {
  223.       if (c_pos++ >= ten_percent)
  224.       {
  225.        display_percent(c_per+=10);
  226.        c_pos = 0;
  227.       }
  228.  
  229.         /* derive an index into the code table */
  230.         hx=(prefix_code ^ (suffix_char << 5)) % table_size;
  231.         d=1;
  232.  
  233.         for(;;)
  234.         {
  235.             /* see if the code is in the table */
  236.             if(currentcode[hx] == 0)
  237.             {
  238.  
  239.                 /* if not, put it there */
  240.                 write_code(prefix_code);
  241.                 d = free_code;
  242.  
  243.                 /* find the next free code */
  244.                 if(free_code <= largest_code)
  245.                 {
  246.                     oldcode[hx] = prefix_code;
  247.                     newcode[hx] = suffix_char;
  248.                     currentcode[hx] = free_code;
  249.                     free_code++;
  250.                 }
  251.  
  252.                 /* expand the code size or scrap the table */
  253.                 if(d == max_code)
  254.                 {
  255.                     if(code_size < 12)
  256.                     {
  257.                         code_size++;
  258.                         max_code <<= 1;
  259.                     }
  260.                     else
  261.                     {
  262.                         write_code(clear_code);
  263.                         init_table(min_code_size);
  264.                     }
  265.                 }
  266.                 prefix_code = suffix_char;
  267.                 break;
  268.             }
  269.             if(oldcode[hx] == prefix_code &&
  270.                newcode[hx] == suffix_char)
  271.             {
  272.                 prefix_code = currentcode[hx];
  273.                 break;
  274.             }
  275.             hx += d;
  276.             d += 2;
  277.             if(hx >= table_size) hx -= table_size;
  278.         }
  279.     }
  280.  
  281.     /* write the prefix code */
  282.     write_code(prefix_code);
  283.  
  284.     /* and the end of file code */
  285.     write_code(eof_code);
  286.  
  287.     /* flush the buffer */
  288.     if(bit_offset > 0) flush((bit_offset+7)/8);
  289.  
  290.     /* write a zero length block */
  291.     flush(0);
  292.  
  293. return;
  294. }
  295.  
  296.  
  297.  
  298. /* initialize the code table */
  299. void init_table(int min_code_size)
  300. {
  301.  int i;
  302.  
  303.     code_size=min_code_size+1;
  304.     clear_code=(1<<min_code_size);
  305.     eof_code=clear_code+1;
  306.     free_code=clear_code+2;
  307.     max_code=(1<<code_size);
  308.  
  309.     for(i=0;i<table_size;i++) currentcode[i]=0;
  310. return;
  311. }
  312.  
  313. /* flush the code buffer */
  314. void flush(int n)
  315. {
  316.     fputc(n,tf);
  317.     fwrite(code_buffer,1,n,tf);
  318. return;
  319. }
  320.  
  321. /* write a code to the code buffer */
  322. void write_code(int code)
  323. {
  324.     long temp;
  325.  
  326.     byte_offset = bit_offset >> 3;
  327.     bits_left = bit_offset & 7;
  328.  
  329.     if(byte_offset >= 254)
  330.     {
  331.         flush(byte_offset);
  332.         code_buffer[0] = code_buffer[byte_offset];
  333.         bit_offset = bits_left;
  334.         byte_offset = 0;
  335.     }
  336.  
  337.     if(bits_left > 0)
  338.     {
  339.         temp = ((long) code << bits_left) | code_buffer[byte_offset];
  340.         code_buffer[byte_offset]=temp;
  341.         code_buffer[byte_offset+1]=(temp >> 8);
  342.         code_buffer[byte_offset+2]=(temp >> 16);
  343.     }
  344.     else
  345.     {
  346.         code_buffer[byte_offset] = code;
  347.         code_buffer[byte_offset+1]=(code >> 8);
  348.     }
  349.     bit_offset += code_size;
  350.  
  351. return;
  352. }
  353.  
  354.  
  355. void display_percent(int c_per)
  356. {
  357.  int x, y;
  358.  
  359.  if (c_per <= 10) //First trip
  360.   x = wherex() + 2;
  361.  else
  362.   x = wherex() - 4;
  363.  
  364.   y = wherey();
  365.  
  366.  gotoxy(x, y);
  367.  printf("%i %", c_per);
  368.  
  369.  if (c_per >= 90) gotoxy(x,y); //Reset for final percentage
  370. return;
  371. }
  372.