123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609 |
- /* Catacomb Apocalypse Source Code
- * Copyright (C) 1993-2014 Flat Rock Software
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- //===========================================================================
- //
- // LZW COMPRESSION ROUTINES
- // VERSION 1.1
- //
- // Compression algrythim by Haruhiko OKUMURA
- // Implementation by Jim T. Row
- //
- //
- // Copyright (c) 1992 - Softdisk Publishing inc. - All rights reserved
- //
- //===========================================================================
- //
- //
- //---------------------------------------------------------------------------
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <alloc.h>
- #include <fcntl.h>
- #include <dos.h>
- #include <io.h>
- #include "lzw.h"
- #include "jam_io.h"
- //===========================================================================
- //
- // SWITCHES
- //
- // NOTE : Make sure the appropriate switches are set in SOFT.c for Softlib
- // archive support.
- //
- //===========================================================================
- #define INCLUDE_LZW_COMP 0
- #define INCLUDE_LZW_DECOMP 1
- //===========================================================================
- //
- // DEFINES
- //
- //===========================================================================
- #define LZW_N 4096
- #define LZW_F 18
- #define LZW_THRESHOLD 2 // encode string into position and
- // length if match_length is greater
- // than this
- #define LZW_NIL LZW_N // index for root of bin search trees
- //============================================================================
- //
- // GLOBAL VARIABLES NECESSARY FOR
- //
- // COMP/DECOMP ROUTINES.
- //
- //============================================================================
- unsigned char far LZW_ring_buffer[LZW_N + LZW_F - 1]; // ring buffer of size
- // LZW_N, with extra
- // LZW_F-1 bytes to
- // facilitate
- // string comparison
- #if INCLUDE_LZW_COMP
- int LZW_match_pos, // MAtchLength of longest match. These are set by the
- // InsertNode() procedure.
- LZW_match_len,
- // left & right children & parents -- These constitute binary search trees. */
- LZW_left_child[LZW_N + 1],
- LZW_right_child[LZW_N + 257],
- LZW_parent[LZW_N + 1];
- #endif
- //============================================================================
- //
- // LZW DISPLAY VECTORS
- //
- // These vectors allow you to hook up any form of display you desire for
- // displaying the compression/decompression status.
- //
- // These routines are called inside of the compression/decompression routines
- // and pass the orginal size of data and current position within that
- // data. This allows for any kind of "% Done" messages.
- //
- // Your functions MUST have the following parameters in this order...
- //
- // void VectorRoutine(unsigned long OrginalSize,unsigned long CurPosition)
- //
- //
- void (*LZW_CompressDisplayVector)() = NULL;
- void (*LZW_DecompressDisplayVector)() = NULL;
- //============================================================================
- //
- // SUPPORT ROUTINES FOR LZW COMPRESSION
- //
- //============================================================================
- #if INCLUDE_LZW_COMP
- static void InitLZWTree(void) /* initialize trees */
- {
- int i;
- /* For i = 0 to LZW_N - 1, LZW_right_child[i] and LZW_left_child[i] will be the right and
- left children of node i. These nodes need not be initialized.
- Also, LZW_parent[i] is the parent of node i. These are initialized to
- LZW_NIL (= LZW_N), which stands for 'not used.'
- For i = 0 to 255, LZW_right_child[LZW_N + i + 1] is the root of the tree
- for strings that begin with character i. These are initialized
- to LZW_NIL. Note there are 256 trees. */
- for (i = LZW_N + 1; i <= LZW_N + 256; i++)
- LZW_right_child[i] = LZW_NIL;
- for (i = 0; i < LZW_N; i++)
- LZW_parent[i] = LZW_NIL;
- }
- ////////////////////////////////////////////////////////////////////////////
- static void InsertLZWNode(unsigned long r)
- /* Inserts string of length LZW_F, LZW_ring_buffer[r..r+LZW_F-1], into one of the
- trees (LZW_ring_buffer[r]'th tree) and returns the longest-match position
- and length via the global variables LZW_match_pos and LZW_match_len.
- If LZW_match_len = LZW_F, then removes the old node in favor of the new
- one, because the old one will be deleted sooner.
- Note r plays double role, as tree node and position in buffer. */
- {
- int i, p, cmp;
- unsigned char *key;
- cmp = 1;
- key = &LZW_ring_buffer[r];
- p = LZW_N + 1 + key[0];
- LZW_right_child[r] = LZW_left_child[r] = LZW_NIL;
- LZW_match_len = 0;
- for ( ; ; )
- {
- if (cmp >= 0)
- {
- if (LZW_right_child[p] != LZW_NIL)
- p = LZW_right_child[p];
- else
- {
- LZW_right_child[p] = r;
- LZW_parent[r] = p;
- return;
- }
- }
- else
- {
- if (LZW_left_child[p] != LZW_NIL)
- p = LZW_left_child[p];
- else
- {
- LZW_left_child[p] = r;
- LZW_parent[r] = p;
- return;
- }
- }
- for (i = 1; i < LZW_F; i++)
- if ((cmp = key[i] - LZW_ring_buffer[p + i]) != 0)
- break;
- if (i > LZW_match_len)
- {
- LZW_match_pos = p;
- if ((LZW_match_len = i) >= LZW_F)
- break;
- }
- }
- LZW_parent[r] = LZW_parent[p];
- LZW_left_child[r] = LZW_left_child[p];
- LZW_right_child[r] = LZW_right_child[p];
- LZW_parent[LZW_left_child[p]] = r;
- LZW_parent[LZW_right_child[p]] = r;
- if (LZW_right_child[LZW_parent[p]] == p)
- LZW_right_child[LZW_parent[p]] = r;
- else
- LZW_left_child[LZW_parent[p]] = r;
- LZW_parent[p] = LZW_NIL; /* remove p */
- }
- ////////////////////////////////////////////////////////////////////////////
- static void DeleteLZWNode(unsigned long p) /* deletes node p from tree */
- {
- int q;
- if (LZW_parent[p] == LZW_NIL)
- return; /* not in tree */
- if (LZW_right_child[p] == LZW_NIL)
- q = LZW_left_child[p];
- else
- if (LZW_left_child[p] == LZW_NIL)
- q = LZW_right_child[p];
- else
- {
- q = LZW_left_child[p];
- if (LZW_right_child[q] != LZW_NIL)
- {
- do {
- q = LZW_right_child[q];
- } while (LZW_right_child[q] != LZW_NIL);
- LZW_right_child[LZW_parent[q]] = LZW_left_child[q];
- LZW_parent[LZW_left_child[q]] = LZW_parent[q];
- LZW_left_child[q] = LZW_left_child[p];
- LZW_parent[LZW_left_child[p]] = q;
- }
- LZW_right_child[q] = LZW_right_child[p];
- LZW_parent[LZW_right_child[p]] = q;
- }
- LZW_parent[q] = LZW_parent[p];
- if (LZW_right_child[LZW_parent[p]] == p)
- LZW_right_child[LZW_parent[p]] = q;
- else
- LZW_left_child[LZW_parent[p]] = q;
- LZW_parent[p] = LZW_NIL;
- }
- //--------------------------------------------------------------------------
- //
- // lzwCompress() - Compresses data from an input ptr to a dest ptr
- //
- // PARAMS:
- // infile - Pointer at the BEGINNING of the data to compress
- // outfile - Pointer to the destination (no header).
- // DataLength - Number of bytes to compress.
- // PtrTypes - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc).
- //
- // RETURNS:
- // Length of compressed data.
- //
- // COMPTYPE : ct_LZW
- //
- // NOTES : Does not write ANY header information!
- //
- unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)
- {
- short i;
- short c, len, r, s, last_LZW_match_len, code_buf_ptr;
- unsigned char code_buf[17], mask;
- unsigned long complen = 0;
- unsigned CodeCount = 0;
- unsigned long OrgDataLen = DataLength;
- // initialize trees
- InitLZWTree();
- code_buf[0] = 0;
- //
- // code_buf[1..16] saves eight units of code, and code_buf[0] works
- // as eight flags, "1" representing that the unit is an unencoded
- // letter (1 byte), "0" a position-and-length pair (2 bytes). Thus,
- // eight units require at most 16 bytes of code.
- //
- code_buf_ptr = mask = 1;
- s = 0;
- r = LZW_N - LZW_F;
- // Clear the buffer with any character that will appear often.
- //
- for (i = s; i < r; i++)
- LZW_ring_buffer[i] = ' ';
- // Read LZW_F bytes into the last LZW_F bytes of the buffer
- //
- for (len = 0; (len < LZW_F) && DataLength; len++)
- {
- c = ReadPtr((long)&infile,PtrTypes);
- DataLength--;
- // text of size zero
- LZW_ring_buffer[r + len] = c;
- }
- if (!(len && DataLength))
- return(0);
- //
- // Insert the LZW_F strings, each of which begins with one or more
- // 'space' characters. Note the order in which these strings
- // are inserted. This way, degenerate trees will be less likely
- // to occur.
- //
- for (i = 1; i <= LZW_F; i++)
- InsertLZWNode(r - i);
- //
- // Finally, insert the whole string just read. The global
- // variables LZW_match_len and LZW_match_pos are set. */
- //
- InsertLZWNode(r);
- do {
- // LZW_match_len may be spuriously long near the end of text.
- //
- if (LZW_match_len > len)
- LZW_match_len = len;
- if (LZW_match_len <= LZW_THRESHOLD)
- {
- // Not long enough match. Send one byte.
- //
- LZW_match_len = 1;
- // 'send one byte' flag
- //
- code_buf[0] |= mask;
- // Send uncoded.
- //
- code_buf[code_buf_ptr++] = LZW_ring_buffer[r];
- }
- else
- {
- code_buf[code_buf_ptr++] = (unsigned char) LZW_match_pos;
- code_buf[code_buf_ptr++] = (unsigned char) (((LZW_match_pos >> 4) & 0xf0) | (LZW_match_len - (LZW_THRESHOLD + 1)));
- // Send position and length pair.
- // Note LZW_match_len > LZW_THRESHOLD.
- }
- if ((mask <<= 1) == 0)
- {
- // Shift mask left one bit.
- // Send at most 8 units of data
- for (i = 0; i < code_buf_ptr; i++)
- WritePtr((long)&outfile,code_buf[i],PtrTypes);
- complen += code_buf_ptr;
- code_buf[0] = 0;
- code_buf_ptr = mask = 1;
- }
- last_LZW_match_len = LZW_match_len;
- for (i = 0; i < last_LZW_match_len && DataLength; i++)
- {
- c = ReadPtr((long)&infile,PtrTypes);
- DataLength--;
- DeleteLZWNode(s); // Delete old strings and
- LZW_ring_buffer[s] = c; // read new bytes
- // If the position is near the end of buffer, extend the
- // buffer to make string comparison easier.
- if (s < LZW_F - 1)
- LZW_ring_buffer[s + LZW_N] = c;
- // Since this is a ring buffer, inc the position modulo LZW_N.
- //
- s = (s + 1) & (LZW_N - 1);
- r = (r + 1) & (LZW_N - 1);
- // Register the string in LZW_ring_buffer[r..r+LZW_F-1]
- //
- InsertLZWNode(r);
- }
- //
- // MANAGE DISPLAY VECTOR
- //
- if (LZW_CompressDisplayVector)
- {
- // Update display every 1k!
- //
- if ((CodeCount += i) > 1024)
- {
- LZW_CompressDisplayVector(OrgDataLen,OrgDataLen-DataLength);
- CodeCount = 0;
- }
- }
- //
- // Manage Compression tree..
- //
- while (i++ < last_LZW_match_len)
- {
- // After the end of text,
- DeleteLZWNode(s); // no need to read, but
- s = (s + 1) & (LZW_N - 1);
- r = (r + 1) & (LZW_N - 1);
- if (--len)
- InsertLZWNode(r); // buffer may not be empty.
- }
- } while (len > 0); // until length of string to be processed is zero
- if (code_buf_ptr > 1)
- {
- // Send remaining code.
- //
- for (i = 0; i < code_buf_ptr; i++)
- WritePtr((long)&outfile,code_buf[i],PtrTypes);
- complen += code_buf_ptr;
- }
- if (LZW_CompressDisplayVector)
- {
- if ((CodeCount += i) > 1024)
- {
- LZW_CompressDisplayVector(OrgDataLen,OrgDataLen-DataLength);
- }
- }
- return(complen);
- }
- #endif
- //============================================================================
- //
- // SUPPORT ROUTINES FOR LZW DECOMPRESSION
- //
- //============================================================================
- #if INCLUDE_LZW_DECOMP
- //--------------------------------------------------------------------------
- //
- // lzwDecompress() - Compresses data from an input ptr to a dest ptr
- //
- // PARAMS:
- // infile - Pointer at the BEGINNING of the compressed data (no header!)
- // outfile - Pointer to the destination.
- // DataLength - Number of bytes to decompress.
- // PtrTypes - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc).
- //
- // RETURNS:
- // Length of compressed data.
- //
- // COMPTYPE : ct_LZW
- //
- // NOTES : Does not write ANY header information!
- //
- void lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)
- {
- int i, j, k, r, c;
- unsigned int flags;
- unsigned char Buffer[8];
- // unsigned char LZW_ring_buffer[LZW_N + LZW_F - 1];
- unsigned CodeCount = 0;
- unsigned long OrgDataLen = DataLength;
- for (i = 0; i < LZW_N - LZW_F; i++)
- LZW_ring_buffer[i] = ' ';
- r = LZW_N - LZW_F;
- flags = 0;
- for ( ; ; )
- {
- if (((flags >>= 1) & 256) == 0)
- {
- c = ReadPtr((long)&infile,PtrTypes);
- flags = c | 0xff00; // uses higher byte cleverly to count 8
- }
- if (flags & 1)
- {
- c = ReadPtr((long)&infile,PtrTypes); // Could test for EOF iff FFILE type
- WritePtr((long)&outfile,c,PtrTypes);
- if (!--DataLength)
- return;
- LZW_ring_buffer[r++] = c;
- r &= (LZW_N - 1);
- }
- else
- {
- i = ReadPtr((long)&infile,PtrTypes);
- j = ReadPtr((long)&infile,PtrTypes);
- i |= ((j & 0xf0) << 4);
- j = (j & 0x0f) + LZW_THRESHOLD;
- for (k = 0; k <= j; k++)
- {
- c = LZW_ring_buffer[(i + k) & (LZW_N - 1)];
- WritePtr((long)&outfile,c,PtrTypes);
- if (!--DataLength)
- return;
- LZW_ring_buffer[r++] = c;
- r &= (LZW_N - 1);
- }
- }
- //
- // MANAGE DISPLAY VECTOR
- //
- if (LZW_DecompressDisplayVector)
- {
- //
- // Update DisplayVector every 1K
- //
- if ((CodeCount+=k) > 1024)
- {
- LZW_DecompressDisplayVector(OrgDataLen,OrgDataLen-DataLength);
- CodeCount = 0;
- }
- }
- }
- }
- #endif
|