Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

src/rle.c

Go to the documentation of this file.
00001 /* {{{
00002  * CalcRogue, a roguelike game for PCs, calculators and PDAs
00003  * Copyright (C) 2003 Jim Babcock
00004  * 
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  * 
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  * 
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018  * }}} */
00019 // rle.c
00025 
00026 #ifdef TARGET // Compiling the main program
00027 #include "crogue.h"
00028 #endif
00029 
00030 #include "substdio.h"
00031 #include "config.h"
00032 #include "rle.h"
00033 #include "huffman.h"
00034 
00035 #ifdef DISABLE_COMPRESSION
00036 #define DISABLE_RLE
00037 #endif
00038 
00039 #define CHECKSUM_INIT 0xF1897134L
00040 #define CHECKSUM_MUL  0xA8159803L
00041 #define CHECKSUM_ADD  0x81340967L
00042 static unsigned short _runlen=0;
00043 static unsigned char _runchar=' ';
00044 
00045 static unsigned long _checksum_file(FILE *f);
00046 
00047 static unsigned char _rleread_file(void);
00048 static unsigned char _rleread_ptr(void);
00049 static unsigned char _rleread_huffman(void);
00050 
00051 static const void *_rlepos;
00052 static unsigned char (*_rleread)(void);
00053 #ifdef CPROTO //HACKHACK - for some reason, cproto's choking
00054 static void *_putc_compress;
00055 #else
00056 static void (*_putc_compress)(unsigned char c, FILE *f) = (void(*)(unsigned char c, FILE *f))&fputc;
00057 #endif
00058 
00059 static const unsigned char magic_byte = 0x5F;
00060 
00061 //{{{
00062 unsigned long checksum_bytes(void *data, unsigned int length)
00063 {
00064     unsigned long checksum = CHECKSUM_INIT;
00065     unsigned i;
00066     
00067     for(i=0; i<length; i++)
00068     {
00069         checksum += ((unsigned char*)data)[i];
00070         checksum *= CHECKSUM_MUL;
00071         checksum += CHECKSUM_ADD;
00072     }
00073     
00074     return checksum;
00075 }
00076 //}}}
00077 //{{{
00078 static unsigned long _checksum_file(FILE *f)
00079 {
00080 #ifdef IS_CALCULATOR
00081     return checksum_bytes((char*)f->base+6, peek_w(f->base)-6);
00082 #else
00083     return 0;
00084 #endif
00085 }
00086 //}}}
00087 
00088 //{{{
00089 // Verify the checksum on a file
00090 int frleverify(FILE *f)
00091 {
00092 #ifdef IS_CALCULATOR
00093     return _checksum_file(f) == peek_l( (char*)f->base + 2 );
00094 #else
00095     return 1; // Stubbed on PC
00096 #endif
00097 }
00098 //}}}
00099 
00100 //{{{
00101 void frlewrite(void *data, size_t s, size_t granularity, FILE *f)
00102 {
00103     unsigned long i, j;
00104     
00105 #ifdef DEBUG_FILES
00106     unsigned long checksum;
00107 #endif
00108     
00109 #ifdef DEBUG_FILES
00110     checksum = checksum_bytes(data, s);
00111 #   ifdef DISABLE_COMPRESSION
00112         fwrite(&checksum, sizeof(long), 1, f);
00113 #   else
00114         frleflush(f);
00115         for(i=0; i<sizeof(checksum); i++)
00116             _putc_compress( (char)(checksum>>(8*i)), f );
00117 #   endif
00118 #endif
00119     
00120 #ifdef DISABLE_COMPRESSION
00121     fwrite(data, s, 1, f);
00122 #else
00123 #   ifdef DISABLE_RLE
00124         for(i=0; i<s; i++)
00125             _putc_compress( ((unsigned char*)data)[i], f );
00126 #   else
00127         for(i=0; i<granularity; i++)
00128         for(j=i; j<s; j+=granularity)
00129             frleputc( ((unsigned char*)data)[j], f);
00130 #   endif
00131 #endif
00132 }
00133 //}}}
00134 //{{{
00135 void frleread(void *data, size_t s, size_t granularity, FILE *f)
00136 {
00137     unsigned long i, j;
00138 #ifdef DEBUG_FILES
00139     unsigned long checksum_read, checksum_validate;
00140 #endif
00141     
00142 #ifndef DISABLE_COMPRESSION
00143     rle_set_source(rle_huffman, f);
00144 #endif
00145     
00146 #ifdef DEBUG_FILES
00147 #   ifdef DISABLE_COMPRESSION
00148         fread(&checksum_read, sizeof(long), 1, f);
00149 #   else
00150         checksum_read = 0;
00151         for(i=0; i<sizeof(checksum_read); i++)
00152             checksum_read += rlegetc() << (8*i);
00153 #   endif
00154 #endif
00155 
00156 #ifdef DISABLE_COMPRESSION
00157     fread(data, s, 1, f);
00158 #else
00159 #   ifdef DISABLE_RLE
00160         for(i=0; i<s; i++)
00161             ((unsigned char*)data)[i] = _rleread();
00162 #   else
00163         for(i=0; i<granularity; i++)
00164         for(j=i; j<s; j+=granularity)
00165             ((char*)data)[j] = rlegetc();
00166 #   endif
00167 #endif
00168     
00169 #ifdef DEBUG_FILES
00170     checksum_validate = checksum_bytes(data, s);
00171     if(checksum_read != checksum_validate) {
00172 #       ifndef EXTERNAL_TOOL
00173             message( gettext(
00174             "File checksum doesn't match! %lp vs %lp, size=%i, granularity=%i"),
00175                    checksum_read, checksum_validate, s, granularity);
00176 #       endif
00177     }
00178 #endif
00179 }
00180 //}}}
00181 
00182 //{{{
00183 void frleinit(void)
00184 {
00185     _runlen=0;
00186     _runchar=0;
00187 }
00188 //}}}
00189 //{{{
00190 void frleinit_read(FILE *f)
00191 {
00192     frleinit();
00193     fseek(f, 4, SEEK_SET);
00194 }
00195 //}}}
00196 //{{{
00197 void frleinit_write(FILE *f, void (*outfunc)(unsigned char c, FILE *f))
00198 {
00199     unsigned long padding = 0; // Added to the beginning to make room for a checksum
00200     frleinit();
00201     _putc_compress = outfunc;
00202     
00203     // Make space at the beginning for a checksum
00204     fwrite(&padding, sizeof padding, 1, f);
00205 }
00206 //}}}
00207 //{{{
00208 void frleinit_write_nochecksum(void (*outfunc)(unsigned char c, FILE *f))
00209 {
00210     frleinit();
00211     _putc_compress = outfunc;
00212 }
00213 //}}}
00214 //{{{
00215 void frlefinalize(FILE *f)
00216 {
00217     unsigned long checksum;
00218     
00219     frleflush(f);
00220     
00221     checksum = _checksum_file(f);
00222     rewind(f);
00223     fwrite(&checksum, sizeof checksum, 1, f);
00224 }
00225 //}}}
00226 //{{{
00227 void frleflush(FILE *fout)
00228 {
00229     if(_runlen > 127)
00230     {
00231         _putc_compress( magic_byte, fout );
00232         _putc_compress( _runchar, fout );
00233         _putc_compress( (unsigned char)((_runlen>>8)|0x80), fout );
00234         _putc_compress( (unsigned char)(_runlen & 0xFF), fout );
00235     }
00236     else if(_runlen>2 || _runchar==magic_byte) // encode only runs that are long enough to be profitable
00237         // encode only runs that are long enough to be profitable
00238         // (but the magic byte MUST be encoded)
00239     {
00240         _putc_compress( magic_byte, fout );
00241         _putc_compress( _runchar, fout );
00242         _putc_compress( (unsigned char)_runlen, fout );
00243     }
00244     else if(_runlen==2)
00245     {
00246         _putc_compress( _runchar, fout );
00247         _putc_compress( _runchar, fout );
00248     }
00249     else if(_runlen==1)
00250     {
00251         _putc_compress( _runchar, fout );
00252     }
00253     
00254     _runlen = 0;
00255     _runchar = 0;
00256 }
00257 //}}}
00258 //{{{
00259 //
00260 // Writes a run-length encoded character to a file
00261 // Uses static data to keep track of state; the 'mode' parameter
00262 // is used to indicate special conditions
00263 // mode: 0 for normal write, 1 for end of write, 2 for initialize
00264 //
00265 void frleputc(unsigned char c, FILE *fout)
00266 {
00267     if( _runlen == 0 )
00268     {
00269         _runlen=1;
00270         _runchar=c;
00271         return;
00272     }
00273     
00274     if( _runlen >= 0x7FFF || c != _runchar )
00275     {
00276         frleflush(fout);
00277         _runchar = c;
00278         _runlen = 1;
00279     } else
00280         _runlen++;
00281 }
00282 //}}}
00283 //{{{
00284 //
00285 // Reads a run-length encoded character from a file
00286 // Uses static data to keep track of state
00287 //
00288 unsigned char rlegetc(void)
00289 {
00290     unsigned char in;
00291     
00292     if( !_runlen ) {
00293         in = _rleread();
00294         
00295         if(in!=magic_byte)
00296             return in;
00297         
00298         _runchar = _rleread();
00299         _runlen = _rleread();
00300         if(_runlen & 0x80) {
00301             _runlen &= ~0x80;
00302             _runlen <<= 8;
00303             _runlen |= _rleread();
00304         }
00305     }
00306     _runlen--;
00307     return _runchar;
00308 }
00309 //}}}
00310 
00311 //{{{
00312 void rle_set_source(rle_source_type t, const void *s)
00313 {
00314     _rlepos = s;
00315     
00316     switch(t)
00317     {
00318         case rle_huffman:
00319             _rleread = _rleread_huffman;
00320             break;
00321         case rle_file:
00322             _rleread = _rleread_file;
00323             break;
00324         case rle_pointer:
00325             _rleread = _rleread_ptr;
00326             break;
00327     }
00328 }
00329 //}}}
00330 //{{{
00331 static unsigned char _rleread_huffman(void)
00332 {
00333     return fhuffmangetc( (FILE*)_rlepos );
00334 }
00335 //}}}
00336 //{{{
00337 static unsigned char _rleread_file(void)
00338 {
00339     return fgetc( (FILE*)_rlepos );
00340 }
00341 //}}}
00342 //{{{
00343 static unsigned char _rleread_ptr(void)
00344 {
00345     return *( ((const unsigned char*)_rlepos) ++);
00346 }
00347 //}}}
00348 

Generated on Thu May 20 13:12:11 2004 for CalcRogue by doxygen 1.3.6