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 #include <stdio.h>
16 #include <stdlib.h>
17 #include "ac/common.h"	// quit()
18 #include "ac/roomstruct.h"
19 #include "util/compress.h"
20 #include "util/lzw.h"
21 #include "util/misc.h"
22 #include "util/bbop.h"
23 
24 #ifdef _MANAGED
25 // ensure this doesn't get compiled to .NET IL
26 #pragma unmanaged
27 #endif
28 
29 #include "util/misc.h"
30 #include "util/stream.h"
31 #include "util/filestream.h"
32 #include "gfx/bitmap.h"
33 
34 using namespace AGS::Common;
35 
36 #ifndef __WGT4_H
37 struct color
38 {
39   unsigned char r, g, b;
40 };
41 #endif
42 
43 #ifndef __CJONES_H
44 long csavecompressed(char *, __block, color[256], long = 0);
45 long cloadcompressed(char *, __block, color *, long = 0);
46 #endif
47 
cpackbitl(unsigned char * line,int size,Stream * out)48 void cpackbitl(unsigned char *line, int size, Stream *out)
49 {
50   int cnt = 0;                  // bytes encoded
51 
52   while (cnt < size) {
53     int i = cnt;
54     int j = i + 1;
55     int jmax = i + 126;
56     if (jmax >= size)
57       jmax = size - 1;
58 
59     if (i == size - 1) {        //................last byte alone
60       out->WriteInt8(0);
61       out->WriteInt8(line[i]);
62       cnt++;
63 
64     } else if (line[i] == line[j]) {    //....run
65       while ((j < jmax) && (line[j] == line[j + 1]))
66         j++;
67 
68       out->WriteInt8(i - j);
69       out->WriteInt8(line[i]);
70       cnt += j - i + 1;
71 
72     } else {                    //.............................sequence
73       while ((j < jmax) && (line[j] != line[j + 1]))
74         j++;
75 
76       out->WriteInt8(j - i);
77       out->WriteArray(line + i, j - i + 1, 1);
78       cnt += j - i + 1;
79 
80     }
81   } // end while
82 }
83 
cpackbitl16(unsigned short * line,int size,Stream * out)84 void cpackbitl16(unsigned short *line, int size, Stream *out)
85 {
86   int cnt = 0;                  // bytes encoded
87 
88   while (cnt < size) {
89     int i = cnt;
90     int j = i + 1;
91     int jmax = i + 126;
92     if (jmax >= size)
93       jmax = size - 1;
94 
95     if (i == size - 1) {        //................last byte alone
96       out->WriteInt8(0);
97       out->WriteInt16(line[i]);
98       cnt++;
99 
100     } else if (line[i] == line[j]) {    //....run
101       while ((j < jmax) && (line[j] == line[j + 1]))
102         j++;
103 
104       out->WriteInt8(i - j);
105       out->WriteInt16(line[i]);
106       cnt += j - i + 1;
107 
108     } else {                    //.............................sequence
109       while ((j < jmax) && (line[j] != line[j + 1]))
110         j++;
111 
112       out->WriteInt8(j - i);
113       out->WriteArray(line + i, j - i + 1, 2);
114       cnt += j - i + 1;
115 
116     }
117   } // end while
118 }
119 
cpackbitl32(unsigned int * line,int size,Stream * out)120 void cpackbitl32(unsigned int *line, int size, Stream *out)
121 {
122   int cnt = 0;                  // bytes encoded
123 
124   while (cnt < size) {
125     int i = cnt;
126     int j = i + 1;
127     int jmax = i + 126;
128     if (jmax >= size)
129       jmax = size - 1;
130 
131     if (i == size - 1) {        //................last byte alone
132       out->WriteInt8(0);
133       out->WriteInt32(line[i]);
134       cnt++;
135 
136     } else if (line[i] == line[j]) {    //....run
137       while ((j < jmax) && (line[j] == line[j + 1]))
138         j++;
139 
140       out->WriteInt8(i - j);
141       out->WriteInt32(line[i]);
142       cnt += j - i + 1;
143 
144     } else {                    //.............................sequence
145       while ((j < jmax) && (line[j] != line[j + 1]))
146         j++;
147 
148       out->WriteInt8(j - i);
149       out->WriteArray(line + i, j - i + 1, 4);
150       cnt += j - i + 1;
151 
152     }
153   } // end while
154 }
155 
156 
csavecompressed(char * finam,__block tobesaved,color pala[256],long exto)157 long csavecompressed(char *finam, __block tobesaved, color pala[256], long exto)
158 {
159   Stream *outpt;
160 
161   if (exto > 0) {
162     outpt = ci_fopen(finam, Common::kFile_Create, Common::kFile_ReadWrite);
163     outpt->Seek(exto, kSeekBegin);
164   }
165   else
166     outpt = ci_fopen(finam, Common::kFile_CreateAlways, Common::kFile_Write);
167 
168   int widt, hit;
169   long ofes;
170   widt = *tobesaved++;
171   widt += (*tobesaved++) * 256;
172   hit = *tobesaved++;
173   hit += (*tobesaved++) * 256;
174   // Those were originally written as shorts, although they are ints
175   outpt->WriteInt16(widt);
176   outpt->WriteInt16(hit);
177 
178   unsigned char *ress = (unsigned char *)malloc(widt + 1);
179   int ww;
180 
181   for (ww = 0; ww < hit; ww++) {
182     for (int ss = 0; ss < widt; ss++)
183       (*ress++) = (*tobesaved++);
184 
185     ress -= widt;
186     cpackbitl(ress, widt, outpt);
187   }
188 
189   for (ww = 0; ww < 256; ww++) {
190     outpt->WriteInt8(pala[ww].r);
191     outpt->WriteInt8(pala[ww].g);
192     outpt->WriteInt8(pala[ww].b);
193   }
194 
195   ofes = outpt->GetPosition();
196   delete outpt;
197   free(ress);
198   return ofes;
199 }
200 
cunpackbitl(unsigned char * line,int size,Stream * in)201 int cunpackbitl(unsigned char *line, int size, Stream *in)
202 {
203   int n = 0;                    // number of bytes decoded
204 
205   while (n < size) {
206     int ix = in->ReadByte();     // get index byte
207     if (in->HasErrors())
208       break;
209 
210     char cx = ix;
211     if (cx == -128)
212       cx = 0;
213 
214     if (cx < 0) {                //.............run
215       int i = 1 - cx;
216       char ch = in->ReadInt8();
217       while (i--) {
218         // test for buffer overflow
219         if (n >= size)
220           return -1;
221 
222         line[n++] = ch;
223       }
224     } else {                     //.....................seq
225       int i = cx + 1;
226       while (i--) {
227         // test for buffer overflow
228         if (n >= size)
229           return -1;
230 
231         line[n++] = in->ReadByte();
232       }
233     }
234   }
235 
236   return in->HasErrors() ? -1 : 0;
237 }
238 
cunpackbitl16(unsigned short * line,int size,Stream * in)239 int cunpackbitl16(unsigned short *line, int size, Stream *in)
240 {
241   int n = 0;                    // number of bytes decoded
242 
243   while (n < size) {
244     int ix = in->ReadByte();     // get index byte
245     if (in->HasErrors())
246       break;
247 
248     char cx = ix;
249     if (cx == -128)
250       cx = 0;
251 
252     if (cx < 0) {                //.............run
253       int i = 1 - cx;
254       unsigned short ch = in->ReadInt16();
255       while (i--) {
256         // test for buffer overflow
257         if (n >= size)
258           return -1;
259 
260         line[n++] = ch;
261       }
262     } else {                     //.....................seq
263       int i = cx + 1;
264       while (i--) {
265         // test for buffer overflow
266         if (n >= size)
267           return -1;
268 
269         line[n++] = in->ReadInt16();
270       }
271     }
272   }
273 
274   return in->HasErrors() ? -1 : 0;
275 }
276 
cunpackbitl32(unsigned int * line,int size,Stream * in)277 int cunpackbitl32(unsigned int *line, int size, Stream *in)
278 {
279   int n = 0;                    // number of bytes decoded
280 
281   while (n < size) {
282     int ix = in->ReadByte();     // get index byte
283     if (in->HasErrors())
284       break;
285 
286     char cx = ix;
287     if (cx == -128)
288       cx = 0;
289 
290     if (cx < 0) {                //.............run
291       int i = 1 - cx;
292       unsigned int ch = in->ReadInt32();
293       while (i--) {
294         // test for buffer overflow
295         if (n >= size)
296           return -1;
297 
298         line[n++] = ch;
299       }
300     } else {                     //.....................seq
301       int i = cx + 1;
302       while (i--) {
303         // test for buffer overflow
304         if (n >= size)
305           return -1;
306 
307         line[n++] = (unsigned int)in->ReadInt32();
308       }
309     }
310   }
311 
312   return in->HasErrors() ? -1 : 0;
313 }
314 
315 //=============================================================================
316 
317 char *lztempfnm = "~aclzw.tmp";
318 Bitmap *recalced = NULL;
319 
320 // returns bytes per pixel for bitmap's color depth
bmp_bpp(Bitmap * bmpt)321 int bmp_bpp(Bitmap*bmpt) {
322   if (bmpt->GetColorDepth() == 15)
323     return 2;
324 
325   return bmpt->GetColorDepth() / 8;
326 }
327 
save_lzw(char * fnn,Bitmap * bmpp,color * pall,long offe)328 long save_lzw(char *fnn, Bitmap *bmpp, color *pall, long offe) {
329   Stream  *lz_temp_s, *out;
330   long  fll, toret, gobacto;
331 
332   lz_temp_s = ci_fopen(lztempfnm, Common::kFile_CreateAlways, Common::kFile_Write);
333   lz_temp_s->WriteInt32(bmpp->GetWidth() * bmpp->GetBPP());
334   lz_temp_s->WriteInt32(bmpp->GetHeight());
335   lz_temp_s->WriteArray(bmpp->GetDataForWriting(), bmpp->GetLineLength(), bmpp->GetHeight());
336   delete lz_temp_s;
337 
338   out = ci_fopen(fnn, Common::kFile_Open, Common::kFile_ReadWrite);
339   out->Seek(offe, kSeekBegin);
340 
341   lz_temp_s = ci_fopen(lztempfnm);
342   fll = lz_temp_s->GetLength();
343   out->WriteArray(&pall[0], sizeof(color), 256);
344   out->WriteInt32(fll);
345   gobacto = out->GetPosition();
346 
347   // reserve space for compressed size
348   out->WriteInt32(fll);
349   lzwcompress(lz_temp_s, out);
350   toret = out->GetPosition();
351   out->Seek(gobacto, kSeekBegin);
352   fll = (toret - gobacto) - 4;
353   out->WriteInt32(fll);      // write compressed size
354   delete lz_temp_s;
355   delete out;
356   unlink(lztempfnm);
357 
358   return toret;
359 }
360 
361 /*long load_lzw(char*fnn,Bitmap*bmm,color*pall,long ooff) {
362   recalced=bmm;
363   FILE*iii=clibfopen(fnn,"rb");
364   Seek(iii,ooff,SEEK_SET);*/
365 
load_lzw(Stream * in,Common::Bitmap * bmm,color * pall)366 long load_lzw(Stream *in, Common::Bitmap *bmm, color *pall) {
367   int          uncompsiz, *loptr;
368   unsigned char *membuffer;
369   int           arin;
370 
371   recalced = bmm;
372   // MACPORT FIX (HACK REALLY)
373   in->Read(&pall[0], sizeof(color)*256);
374   maxsize = in->ReadInt32();
375   uncompsiz = in->ReadInt32();
376 
377   uncompsiz += in->GetPosition();
378   outbytes = 0; putbytes = 0;
379 
380   update_polled_stuff_if_runtime();
381   membuffer = lzwexpand_to_mem(in);
382   update_polled_stuff_if_runtime();
383 
384   loptr = (int *)&membuffer[0];
385   membuffer += 8;
386 #if defined(AGS_BIG_ENDIAN)
387   loptr[0] = AGS::Common::BBOp::SwapBytesInt32(loptr[0]);
388   loptr[1] = AGS::Common::BBOp::SwapBytesInt32(loptr[1]);
389   int bitmapNumPixels = loptr[0]*loptr[1]/_acroom_bpp;
390   switch (_acroom_bpp) // bytes per pixel!
391   {
392     case 1:
393     {
394       // all done
395       break;
396     }
397     case 2:
398     {
399       short *sp = (short *)membuffer;
400       for (int i = 0; i < bitmapNumPixels; ++i)
401       {
402         sp[i] = AGS::Common::BBOp::SwapBytesInt16(sp[i]);
403       }
404       // all done
405       break;
406     }
407     case 4:
408     {
409       int *ip = (int *)membuffer;
410       for (int i = 0; i < bitmapNumPixels; ++i)
411       {
412         ip[i] = AGS::Common::BBOp::SwapBytesInt32(ip[i]);
413       }
414       // all done
415       break;
416     }
417   }
418 #endif // defined(AGS_BIG_ENDIAN)
419 
420   delete bmm;
421 
422   update_polled_stuff_if_runtime();
423 
424   bmm = BitmapHelper::CreateBitmap((loptr[0] / _acroom_bpp), loptr[1], _acroom_bpp * 8);
425   if (bmm == NULL)
426     quit("!load_room: not enough memory to load room background");
427 
428   update_polled_stuff_if_runtime();
429 
430   bmm->Acquire ();
431   recalced = bmm;
432 
433   for (arin = 0; arin < loptr[1]; arin++)
434     memcpy(&bmm->GetScanLineForWriting(arin)[0], &membuffer[arin * loptr[0]], loptr[0]);
435 
436   bmm->Release ();
437 
438   update_polled_stuff_if_runtime();
439 
440   free(membuffer-8);
441 
442   if (in->GetPosition() != uncompsiz)
443     in->Seek(uncompsiz, kSeekBegin);
444 
445   update_polled_stuff_if_runtime();
446 
447   return uncompsiz;
448 }
449 
savecompressed_allegro(char * fnn,Common::Bitmap * bmpp,color * pall,long write_at)450 long savecompressed_allegro(char *fnn, Common::Bitmap *bmpp, color *pall, long write_at) {
451   unsigned char *wgtbl = (unsigned char *)malloc(bmpp->GetWidth() * bmpp->GetHeight() + 4);
452   short         *sss = (short *)wgtbl;
453   long          toret;
454 
455   sss[0] = bmpp->GetWidth();
456   sss[1] = bmpp->GetHeight();
457 
458   memcpy(&wgtbl[4], bmpp->GetDataForWriting(), bmpp->GetWidth() * bmpp->GetHeight());
459 
460   toret = csavecompressed(fnn, wgtbl, pall, write_at);
461   free(wgtbl);
462   return toret;
463 }
464 
loadcompressed_allegro(Stream * in,Common::Bitmap ** bimpp,color * pall,long read_at)465 long loadcompressed_allegro(Stream *in, Common::Bitmap **bimpp, color *pall, long read_at) {
466   short widd,hitt;
467   int   ii;
468 
469   Bitmap *bim = *bimpp;
470   delete bim;
471 
472   widd = in->ReadInt16();
473   hitt = in->ReadInt16();
474   bim = BitmapHelper::CreateBitmap(widd, hitt, 8);
475   if (bim == NULL)
476     quit("!load_room: not enough memory to decompress masks");
477   *bimpp = bim;
478 
479   for (ii = 0; ii < hitt; ii++) {
480     cunpackbitl(&bim->GetScanLineForWriting(ii)[0], widd, in);
481     if (ii % 20 == 0)
482       update_polled_stuff_if_runtime();
483   }
484 
485   in->Seek(768);  // skip palette
486 
487   return in->GetPosition();
488 }
489 
490 
491