]> icculus.org git repositories - btb/d2x.git/blob - 2d/pcx.c
Use PhysicsFS for making the hoard ham.
[btb/d2x.git] / 2d / pcx.c
1 /* $Id: pcx.c,v 1.10 2004-12-01 12:48:13 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14 /*
15  *
16  * Routines to read/write pcx images.
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <conf.h>
22 #endif
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "gr.h"
29 #include "grdef.h"
30 #include "u_mem.h"
31 #include "pcx.h"
32 #include "cfile.h"
33
34 #ifdef OGL
35 #include "palette.h"
36 #endif
37
38 #if defined(POLY_ACC)
39 #include "poly_acc.h"
40 #endif
41 #include "physfsx.h"
42
43 int pcx_encode_byte(ubyte byt, ubyte cnt, PHYSFS_file *fid);
44 int pcx_encode_line(ubyte *inBuff, int inLen, PHYSFS_file *fp);
45
46 /* PCX Header data type */
47 typedef struct {
48         ubyte   Manufacturer;
49         ubyte   Version;
50         ubyte   Encoding;
51         ubyte   BitsPerPixel;
52         short   Xmin;
53         short   Ymin;
54         short   Xmax;
55         short   Ymax;
56         short   Hdpi;
57         short   Vdpi;
58         ubyte   ColorMap[16][3];
59         ubyte   Reserved;
60         ubyte   Nplanes;
61         short   BytesPerLine;
62         ubyte   filler[60];
63 } __pack__ PCXHeader;
64
65 #define PCXHEADER_SIZE 128
66
67 #ifdef FAST_FILE_IO
68 #define PCXHeader_read_n(ph, n, fp) cfread(ph, sizeof(PCXHeader), n, fp)
69 #else
70 /*
71  * reads n PCXHeader structs from a CFILE
72  */
73 int PCXHeader_read_n(PCXHeader *ph, int n, CFILE *fp)
74 {
75         int i;
76
77         for (i = 0; i < n; i++) {
78                 ph->Manufacturer = cfile_read_byte(fp);
79                 ph->Version = cfile_read_byte(fp);
80                 ph->Encoding = cfile_read_byte(fp);
81                 ph->BitsPerPixel = cfile_read_byte(fp);
82                 ph->Xmin = cfile_read_short(fp);
83                 ph->Ymin = cfile_read_short(fp);
84                 ph->Xmax = cfile_read_short(fp);
85                 ph->Ymax = cfile_read_short(fp);
86                 ph->Hdpi = cfile_read_short(fp);
87                 ph->Vdpi = cfile_read_short(fp);
88                 cfread(&ph->ColorMap, 16*3, 1, fp);
89                 ph->Reserved = cfile_read_byte(fp);
90                 ph->Nplanes = cfile_read_byte(fp);
91                 ph->BytesPerLine = cfile_read_short(fp);
92                 cfread(&ph->filler, 60, 1, fp);
93         }
94         return i;
95 }
96 #endif
97
98 int pcx_get_dimensions( char *filename, int *width, int *height)
99 {
100         CFILE *PCXfile;
101         PCXHeader header;
102
103         PCXfile = cfopen(filename, "rb");
104         if (!PCXfile) return PCX_ERROR_OPENING;
105
106         if (PCXHeader_read_n(&header, 1, PCXfile) != 1) {
107                 cfclose(PCXfile);
108                 return PCX_ERROR_NO_HEADER;
109         }
110         cfclose(PCXfile);
111
112         *width = header.Xmax - header.Xmin+1;
113         *height = header.Ymax - header.Ymin+1;
114
115         return PCX_ERROR_NONE;
116 }
117
118 #ifdef MACINTOSH
119 int pcx_read_bitmap_palette( char *filename, ubyte *palette)
120 {
121         PCXHeader header;
122         CFILE * PCXfile;
123         ubyte data;
124         int i;
125
126         PCXfile = cfopen( filename , "rb" );
127         if ( !PCXfile )
128                 return PCX_ERROR_OPENING;
129
130         // read 128 char PCX header
131         if (PCXHeader_read_n( &header, 1, PCXfile )!=1) {
132                 cfclose( PCXfile );
133                 return PCX_ERROR_NO_HEADER;
134         }
135         // Is it a 256 color PCX file?
136         if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5))      {
137                 cfclose( PCXfile );
138                 return PCX_ERROR_WRONG_VERSION;
139         }
140
141         // Read the extended palette at the end of PCX file
142         // Read in a character which should be 12 to be extended palette file
143
144         if (palette != NULL) {
145                 cfseek( PCXfile, -768, SEEK_END );
146                 cfread( palette, 3, 256, PCXfile );
147                 cfseek( PCXfile, PCXHEADER_SIZE, SEEK_SET );
148                 for (i=0; i<768; i++ )
149                         palette[i] >>= 2;
150 #ifdef MACINTOSH
151                 for (i = 0; i < 3; i++) {
152                         data = palette[i];
153                         palette[i] = palette[765+i];
154                         palette[765+i] = data;
155                 }
156 #endif
157         }
158 }
159 #endif
160
161 int pcx_read_bitmap( char * filename, grs_bitmap * bmp,int bitmap_type ,ubyte * palette )
162 {
163         PCXHeader header;
164         CFILE * PCXfile;
165         int i, row, col, count, xsize, ysize;
166         ubyte data, *pixdata;
167 #if defined(POLY_ACC)
168     unsigned char local_pal[768];
169
170     pa_flush();
171 #endif
172
173         PCXfile = cfopen( filename , "rb" );
174         if ( !PCXfile )
175                 return PCX_ERROR_OPENING;
176
177         // read 128 char PCX header
178         if (PCXHeader_read_n( &header, 1, PCXfile )!=1) {
179                 cfclose( PCXfile );
180                 return PCX_ERROR_NO_HEADER;
181         }
182
183         // Is it a 256 color PCX file?
184         if ((header.Manufacturer != 10)||(header.Encoding != 1)||(header.Nplanes != 1)||(header.BitsPerPixel != 8)||(header.Version != 5))      {
185                 cfclose( PCXfile );
186                 return PCX_ERROR_WRONG_VERSION;
187         }
188
189         // Find the size of the image
190         xsize = header.Xmax - header.Xmin + 1;
191         ysize = header.Ymax - header.Ymin + 1;
192
193 #if defined(POLY_ACC)
194     // Read the extended palette at the end of PCX file
195     if(bitmap_type == BM_LINEAR15)      // need palette for conversion from 8bit pcx to 15bit.
196     {
197         cfseek( PCXfile, -768, SEEK_END );
198         cfread( local_pal, 3, 256, PCXfile );
199         cfseek( PCXfile, PCXHEADER_SIZE, SEEK_SET );
200         for (i=0; i<768; i++ )
201             local_pal[i] >>= 2;
202         pa_save_clut();
203         pa_update_clut(local_pal, 0, 256, 0);
204     }
205 #endif
206
207         if ( bitmap_type == BM_LINEAR ) {
208                 if ( bmp->bm_data == NULL )     {
209                         gr_init_bitmap_alloc (bmp, bitmap_type, 0, 0, xsize, ysize, xsize);
210                 }
211         }
212
213         if ( bmp->bm_type == BM_LINEAR )        {
214                 for (row=0; row< ysize ; row++)      {
215                         pixdata = &bmp->bm_data[bmp->bm_rowsize*row];
216                         for (col=0; col< xsize ; )      {
217                                 if (cfread( &data, 1, 1, PCXfile )!=1 ) {
218                                         cfclose( PCXfile );
219                                         return PCX_ERROR_READING;
220                                 }
221                                 if ((data & 0xC0) == 0xC0)     {
222                                         count =  data & 0x3F;
223                                         if (cfread( &data, 1, 1, PCXfile )!=1 ) {
224                                                 cfclose( PCXfile );
225                                                 return PCX_ERROR_READING;
226                                         }
227 #ifdef MACINTOSH
228                                         if (data == 0)
229                                                 data = 255;
230                                         else if (data == 255)
231                                                 data = 0;
232 #endif
233                                         memset( pixdata, data, count );
234                                         pixdata += count;
235                                         col += count;
236                                 } else {
237 #ifdef MACINTOSH
238                                         if (data == 0)
239                                                 data = 255;
240                                         else if (data == 255)
241                                                 data = 0;
242 #endif
243                                         *pixdata++ = data;
244                                         col++;
245                                 }
246                         }
247                 }
248 #if defined(POLY_ACC)
249     } else if( bmp->bm_type == BM_LINEAR15 )    {
250         ushort *pixdata2, pix15;
251         PA_DFX (pa_set_backbuffer_current());
252                   PA_DFX (pa_set_write_mode(0));
253                 for (row=0; row< ysize ; row++)      {
254             pixdata2 = (ushort *)&bmp->bm_data[bmp->bm_rowsize*row];
255                         for (col=0; col< xsize ; )      {
256                                 if (cfread( &data, 1, 1, PCXfile )!=1 ) {
257                                         cfclose( PCXfile );
258                                         return PCX_ERROR_READING;
259                                 }
260                                 if ((data & 0xC0) == 0xC0)     {
261                                         count =  data & 0x3F;
262                                         if (cfread( &data, 1, 1, PCXfile )!=1 ) {
263                                                 cfclose( PCXfile );
264                                                 return PCX_ERROR_READING;
265                                         }
266                     pix15 = pa_clut[data];
267                     for(i = 0; i != count; ++i) pixdata2[i] = pix15;
268                     pixdata2 += count;
269                                         col += count;
270                                 } else {
271                     *pixdata2++ = pa_clut[data];
272                                         col++;
273                                 }
274                         }
275         }
276         pa_restore_clut();
277                   PA_DFX (pa_swap_buffer());
278         PA_DFX (pa_set_frontbuffer_current());
279
280 #endif
281         } else {
282                 for (row=0; row< ysize ; row++)      {
283                         for (col=0; col< xsize ; )      {
284                                 if (cfread( &data, 1, 1, PCXfile )!=1 ) {
285                                         cfclose( PCXfile );
286                                         return PCX_ERROR_READING;
287                                 }
288                                 if ((data & 0xC0) == 0xC0)     {
289                                         count =  data & 0x3F;
290                                         if (cfread( &data, 1, 1, PCXfile )!=1 ) {
291                                                 cfclose( PCXfile );
292                                                 return PCX_ERROR_READING;
293                                         }
294                                         for (i=0;i<count;i++)
295                                                 gr_bm_pixel( bmp, col+i, row, data );
296                                         col += count;
297                                 } else {
298                                         gr_bm_pixel( bmp, col, row, data );
299                                         col++;
300                                 }
301                         }
302                 }
303         }
304
305         // Read the extended palette at the end of PCX file
306         if ( palette != NULL )  {
307                 // Read in a character which should be 12 to be extended palette file
308                 if (cfread( &data, 1, 1, PCXfile )==1)  {
309                         if ( data == 12 )       {
310                                 if (cfread(palette,768, 1, PCXfile)!=1) {
311                                         cfclose( PCXfile );
312                                         return PCX_ERROR_READING;
313                                 }
314                                 for (i=0; i<768; i++ )
315                                         palette[i] >>= 2;
316 #ifdef MACINTOSH
317                                 for (i = 0; i < 3; i++) {
318                                         data = palette[i];
319                                         palette[i] = palette[765+i];
320                                         palette[765+i] = data;
321                                 }
322 #endif
323                         }
324                 } else {
325                         cfclose( PCXfile );
326                         return PCX_ERROR_NO_PALETTE;
327                 }
328         }
329         cfclose(PCXfile);
330         return PCX_ERROR_NONE;
331 }
332
333 int pcx_write_bitmap( char * filename, grs_bitmap * bmp, ubyte * palette )
334 {
335         int retval;
336         int i;
337         ubyte data;
338         PCXHeader header;
339         PHYSFS_file *PCXfile;
340
341         memset( &header, 0, PCXHEADER_SIZE );
342
343         header.Manufacturer = 10;
344         header.Encoding = 1;
345         header.Nplanes = 1;
346         header.BitsPerPixel = 8;
347         header.Version = 5;
348         header.Xmax = bmp->bm_w-1;
349         header.Ymax = bmp->bm_h-1;
350         header.BytesPerLine = bmp->bm_w;
351
352         PCXfile = PHYSFS_openWrite(filename);
353         if ( !PCXfile )
354                 return PCX_ERROR_OPENING;
355
356         if (PHYSFS_write(PCXfile, &header, PCXHEADER_SIZE, 1) != 1)
357         {
358                 PHYSFS_close(PCXfile);
359                 return PCX_ERROR_WRITING;
360         }
361
362         for (i=0; i<bmp->bm_h; i++ )    {
363                 if (!pcx_encode_line( &bmp->bm_data[bmp->bm_rowsize*i], bmp->bm_w, PCXfile ))   {
364                         PHYSFS_close(PCXfile);
365                         return PCX_ERROR_WRITING;
366                 }
367         }
368
369         // Mark an extended palette
370         data = 12;
371         if (PHYSFS_write(PCXfile, &data, 1, 1) != 1)
372         {
373                 PHYSFS_close(PCXfile);
374                 return PCX_ERROR_WRITING;
375         }
376
377         // Write the extended palette
378         for (i=0; i<768; i++ )
379                 palette[i] <<= 2;
380
381         retval = PHYSFS_write(PCXfile, palette, 768, 1);
382
383         for (i=0; i<768; i++ )
384                 palette[i] >>= 2;
385
386         if (retval !=1) {
387                 PHYSFS_close(PCXfile);
388                 return PCX_ERROR_WRITING;
389         }
390
391         PHYSFS_close(PCXfile);
392         return PCX_ERROR_NONE;
393
394 }
395
396 // returns number of bytes written into outBuff, 0 if failed
397 int pcx_encode_line(ubyte *inBuff, int inLen, PHYSFS_file *fp)
398 {
399         ubyte this, last;
400         int srcIndex, i;
401         register int total;
402         register ubyte runCount;        // max single runlength is 63
403         total = 0;
404         last = *(inBuff);
405         runCount = 1;
406
407         for (srcIndex = 1; srcIndex < inLen; srcIndex++) {
408                 this = *(++inBuff);
409                 if (this == last)       {
410                         runCount++;                     // it encodes
411                         if (runCount == 63)     {
412                                 if (!(i=pcx_encode_byte(last, runCount, fp)))
413                                         return(0);
414                                 total += i;
415                                 runCount = 0;
416                         }
417                 } else {        // this != last
418                         if (runCount)   {
419                                 if (!(i=pcx_encode_byte(last, runCount, fp)))
420                                         return(0);
421                                 total += i;
422                         }
423                         last = this;
424                         runCount = 1;
425                 }
426         }
427
428         if (runCount)   {               // finish up
429                 if (!(i=pcx_encode_byte(last, runCount, fp)))
430                         return 0;
431                 return total + i;
432         }
433         return total;
434 }
435
436 // subroutine for writing an encoded byte pair
437 // returns count of bytes written, 0 if error
438 int pcx_encode_byte(ubyte byt, ubyte cnt, PHYSFS_file *fid)
439 {
440         if (cnt) {
441                 if ( (cnt==1) && (0xc0 != (0xc0 & byt)) )       {
442                         if(EOF == PHYSFSX_putc(fid, (int)byt))
443                                 return 0;       // disk write error (probably full)
444                         return 1;
445                 } else {
446                         if(EOF == PHYSFSX_putc(fid, (int)0xC0 | cnt))
447                                 return 0;       // disk write error
448                         if(EOF == PHYSFSX_putc(fid, (int)byt))
449                                 return 0;       // disk write error
450                         return 2;
451                 }
452         }
453         return 0;
454 }
455
456 //text for error messges
457 char pcx_error_messages[] = {
458         "No error.\0"
459         "Error opening file.\0"
460         "Couldn't read PCX header.\0"
461         "Unsupported PCX version.\0"
462         "Error reading data.\0"
463         "Couldn't find palette information.\0"
464         "Error writing data.\0"
465 };
466
467
468 //function to return pointer to error message
469 char *pcx_errormsg(int error_number)
470 {
471         char *p = pcx_error_messages;
472
473         while (error_number--) {
474
475                 if (!p) return NULL;
476
477                 p += strlen(p)+1;
478
479         }
480
481         return p;
482
483 }
484
485 // fullscreen loading, 10/14/99 Jan Bobrowski
486
487 int pcx_read_fullscr(char * filename, ubyte * palette)
488 {
489         int pcx_error;
490         grs_bitmap bm;
491         gr_init_bitmap_data(&bm);
492         pcx_error = pcx_read_bitmap(filename, &bm, BM_LINEAR, palette);
493         if (pcx_error == PCX_ERROR_NONE) {
494 #ifdef OGL
495                 gr_palette_load(palette);
496 #endif
497                 show_fullscr(&bm);
498         }
499         gr_free_bitmap_data(&bm);
500         return pcx_error;
501 }