1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 //
15 // LZW compression -- the LZW/GIF patent has expired, so we can use it now!!!
16 //
17 //=============================================================================
18 
19 #define MSS
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include "ac/common.h"
24 #include "util/stream.h"
25 
26 using AGS::Common::Stream;
27 using namespace AGS; // FIXME later
28 
29 #ifdef _MANAGED
30 // ensure this doesn't get compiled to .NET IL
31 #pragma unmanaged
32 #endif
33 
34 #if defined (WINDOWS_VERSION)
35 #include <io.h>
36 #endif
37 
38 int insert(int, int);
39 void _delete(int);
40 
41 #define N 4096
42 #define F 16
43 #define THRESHOLD 3
44 #define min(xx,yy) ((yy<xx) ? yy : xx)
45 
46 #define dad (node+1)
47 #define lson (node+1+N)
48 #define rson (node+1+N+N)
49 #define root (node+1+N+N+N)
50 #define NIL -1
51 
52 char *lzbuffer;
53 int *node;
54 int pos;
55 long outbytes = 0, maxsize = 0, putbytes = 0;
56 
insert(int i,int run)57 int insert(int i, int run)
58 {
59   int c, j, k, l, n, match;
60   int *p;
61 
62   c = NIL;
63 
64   k = l = 1;
65   match = THRESHOLD - 1;
66   p = &root[(unsigned char)lzbuffer[i]];
67   lson[i] = rson[i] = NIL;
68   while ((j = *p) != NIL) {
69     for (n = min(k, l); n < run && (c = (lzbuffer[j + n] - lzbuffer[i + n])) == 0; n++) ;
70 
71     if (n > match) {
72       match = n;
73       pos = j;
74     }
75 
76     if (c < 0) {
77       p = &lson[j];
78       k = n;
79     } else if (c > 0) {
80       p = &rson[j];
81       l = n;
82     } else {
83       dad[j] = NIL;
84       dad[lson[j]] = lson + i - node;
85       dad[rson[j]] = rson + i - node;
86       lson[i] = lson[j];
87       rson[i] = rson[j];
88       break;
89     }
90   }
91 
92   dad[i] = p - node;
93   *p = i;
94   return match;
95 }
96 
_delete(int z)97 void _delete(int z)
98 {
99   int j;
100 
101   if (dad[z] != NIL) {
102     if (rson[z] == NIL)
103       j = lson[z];
104     else if (lson[z] == NIL)
105       j = rson[z];
106     else {
107       j = lson[z];
108       if (rson[j] != NIL) {
109         do {
110           j = rson[j];
111         } while (rson[j] != NIL);
112 
113         node[dad[j]] = lson[j];
114         dad[lson[j]] = dad[j];
115         lson[j] = lson[z];
116         dad[lson[z]] = lson + j - node;
117       }
118 
119       rson[j] = rson[z];
120       dad[rson[z]] = rson + j - node;
121     }
122 
123     dad[j] = dad[z];
124     node[dad[z]] = j;
125     dad[z] = NIL;
126   }
127 }
128 
lzwcompress(Common::Stream * lzw_in,Common::Stream * out)129 void lzwcompress(Common::Stream *lzw_in, Common::Stream *out)
130 {
131   int ch, i, run, len, match, size, mask;
132   char buf[17];
133 
134   lzbuffer = (char *)malloc(N + F + (N + 1 + N + N + 256) * sizeof(int));       // 28.5 k !
135   if (lzbuffer == NULL) {
136     quit("unable to compress: out of memory");
137   }
138 
139   node = (int *)(lzbuffer + N + F);
140   for (i = 0; i < 256; i++)
141     root[i] = NIL;
142 
143   for (i = NIL; i < N; i++)
144     dad[i] = NIL;
145 
146   size = mask = 1;
147   buf[0] = 0;
148   i = N - F - F;
149 
150   for (len = 0; len < F && (ch = lzw_in->ReadByte()) != -1; len++) {
151     lzbuffer[i + F] = ch;
152     i = (i + 1) & (N - 1);
153   }
154 
155   run = len;
156 
157   do {
158     ch = lzw_in->ReadByte();
159     if (i >= N - F) {
160       _delete(i + F - N);
161       lzbuffer[i + F] = lzbuffer[i + F - N] = ch;
162     } else {
163       _delete(i + F);
164       lzbuffer[i + F] = ch;
165     }
166 
167     match = insert(i, run);
168     if (ch == -1) {
169       run--;
170       len--;
171     }
172 
173     if (len++ >= run) {
174       if (match >= THRESHOLD) {
175         buf[0] |= mask;
176         // possible fix: change int* to short* ??
177         *(short *)(buf + size) = ((match - 3) << 12) | ((i - pos - 1) & (N - 1));
178         size += 2;
179         len -= match;
180       } else {
181         buf[size++] = lzbuffer[i];
182         len--;
183       }
184 
185       if (!((mask += mask) & 0xFF)) {
186         out->WriteArray(buf, size, 1);
187         outbytes += size;
188         size = mask = 1;
189         buf[0] = 0;
190       }
191     }
192     i = (i + 1) & (N - 1);
193   } while (len > 0);
194 
195   if (size > 1) {
196     out->WriteArray(buf, size, 1);
197     outbytes += size;
198   }
199 
200   free(lzbuffer);
201 }
202 
203 int expand_to_mem = 0;
204 unsigned char *membfptr = NULL;
myputc(int ccc,Stream * out)205 void myputc(int ccc, Stream *out)
206 {
207   if (maxsize > 0) {
208     putbytes++;
209     if (putbytes > maxsize)
210       return;
211   }
212 
213   outbytes++;
214   if (expand_to_mem) {
215     membfptr[0] = ccc;
216     membfptr++;
217   } else
218     out->WriteInt8(ccc);
219 }
220 
lzwexpand(Stream * lzw_in,Stream * out)221 void lzwexpand(Stream *lzw_in, Stream *out)
222 {
223   int bits, ch, i, j, len, mask;
224   char *lzbuffer;
225 //  printf(" UnShrinking: %s ",filena);
226   putbytes = 0;
227 
228   lzbuffer = (char *)malloc(N);
229   if (lzbuffer == NULL) {
230     quit("compress.cpp: unable to decompress: insufficient memory");
231   }
232   i = N - F;
233 
234   // this end condition just checks for EOF, which is no good to us
235   while ((bits = lzw_in->ReadByte()) != -1) {
236     for (mask = 0x01; mask & 0xFF; mask <<= 1) {
237       if (bits & mask) {
238         // MACPORT FIX: read to short and expand
239         short jshort = 0;
240         jshort = lzw_in->ReadInt16();
241         j = jshort;
242 
243         len = ((j >> 12) & 15) + 3;
244         j = (i - j - 1) & (N - 1);
245 
246         while (len--) {
247           myputc(lzbuffer[i] = lzbuffer[j], out);
248           j = (j + 1) & (N - 1);
249           i = (i + 1) & (N - 1);
250         }
251       } else {
252         ch = lzw_in->ReadByte();
253         myputc(lzbuffer[i] = ch, out);
254         i = (i + 1) & (N - 1);
255       }
256 
257       if ((putbytes >= maxsize) && (maxsize > 0))
258         break;
259 
260       if ((lzw_in->EOS()) && (maxsize > 0))
261         quit("Read error decompressing image - file is corrupt");
262     }                           // end for mask
263 
264     if ((putbytes >= maxsize) && (maxsize > 0))
265       break;
266   }
267 
268   free(lzbuffer);
269   expand_to_mem = 0;
270 }
271 
lzwexpand_to_mem(Common::Stream * in)272 unsigned char *lzwexpand_to_mem(Common::Stream *in)
273 {
274   unsigned char *membuff = (unsigned char *)malloc(maxsize + 10);
275   expand_to_mem = 1;
276   membfptr = membuff;
277   lzwexpand(in, NULL);
278   return membuff;
279 }
280