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