rnd-20001204-1-src
[rocksndiamonds.git] / src / libgame / pcx.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2000 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * pcx.c                                                    *
12 ***********************************************************/
13
14 #ifndef TARGET_SDL
15
16 #include <stdio.h>
17
18 #include "pcx.h"
19 #include "misc.h"
20
21
22 #define PCX_DEBUG               FALSE
23
24 #define PCX_MAGIC               0x0a    /* first byte in a PCX image file    */
25 #define PCX_LAST_VERSION        5       /* last acceptable version number    */
26 #define PCX_ENCODING            1       /* PCX encoding method               */
27 #define PCX_256COLORS_MAGIC     0x0c    /* first byte of a PCX 256 color map */
28 #define PCX_MAXDEPTH            8       /* supports up to 8 bits per pixel   */
29 #define PCX_MAXCOLORS           256     /* maximum number of colors          */
30
31 #define PCX_HEADER_SIZE         128
32 #define PCX_COLORMAP_SIZE       (3 * PCX_MAXCOLORS)
33
34 struct PCX_Header
35 {
36   unsigned char signature;      /* PCX file identifier                 */
37   unsigned char version;        /* version compatibility level         */
38   unsigned char encoding;       /* encoding method                     */
39   unsigned char bits_per_pixel; /* bits per pixel, or depth            */
40   unsigned short xmin;          /* X position of left edge             */
41   unsigned short ymin;          /* Y position of top edge              */
42   unsigned short xmax;          /* X position of right edge            */
43   unsigned short ymax;          /* Y position of bottom edge           */
44   unsigned short hres;          /* X screen resolution of source image */
45   unsigned short vres;          /* Y screen resolution of source image */
46   unsigned char palette[16][3]; /* PCX color map                       */
47   unsigned char reserved;       /* should be 0, 1 if std res fax       */
48   unsigned char color_planes;   /* bit planes in image                 */
49   unsigned short bytes_per_line;/* byte delta between scanlines        */
50   unsigned short palette_type;  /* 0 = undef, 1 = color, 2 = grayscale */
51   unsigned char filler[58];     /* fill to struct size of 128          */
52 };
53
54 /* global PCX error value */
55 int errno_pcx = PCX_Success;
56
57 static byte *PCX_ReadBitmap(Image *image, byte *buffer_ptr, byte *buffer_last)
58 {
59   /* Run Length Encoding: If the two high bits are set,
60    * then the low 6 bits contain a repeat count, and the byte to
61    * repeat is the next byte in the file.  If the two high bits are
62    * not set, then this is the byte to write.
63    */
64
65   unsigned int bytes_per_pixel = (image->depth + 7) / 8;
66   register byte *bitmap_ptr, *bitmap_last;
67   register byte value, count;
68
69   bitmap_ptr = image->data;
70   bitmap_last = bitmap_ptr + (image->width * image->height * bytes_per_pixel);
71
72   while (bitmap_ptr < bitmap_last && buffer_ptr < buffer_last)
73   {
74     value = *buffer_ptr++;
75
76     if ((value & 0xc0) == 0xc0)         /* this is a repeat count byte */
77     {
78       count = value & 0x3f;             /* extract repeat count from byte */
79       value = *buffer_ptr++;            /* next byte is value to repeat */
80
81       for (; count && bitmap_ptr < bitmap_last; count--)
82         *bitmap_ptr++ = value;
83
84       if (count)                        /* repeat count spans end of bitmap */
85         return NULL;
86     }
87     else
88       *bitmap_ptr++ = value;
89
90     image->rgb.color_used[value] = TRUE;
91   }
92
93   /* check if end of buffer was reached before end of bitmap */
94   if (bitmap_ptr < bitmap_last)
95     return NULL;
96
97   /* return current buffer position for next decoding function */
98   return buffer_ptr;
99 }
100
101 static byte *PCX_ReadColormap(Image *image,byte *buffer_ptr, byte *buffer_last)
102 {
103   int i, magic;
104
105   /* read colormap magic byte */
106   magic = *buffer_ptr++;
107
108   /* check magic colormap header byte */
109   if (magic != PCX_256COLORS_MAGIC)
110     return NULL;
111
112   /* check if enough bytes left for a complete colormap */
113   if (buffer_ptr + PCX_COLORMAP_SIZE > buffer_last)
114     return NULL;
115
116   /* read 256 colors from PCX colormap */
117   for (i=0; i<PCX_MAXCOLORS; i++)
118   {
119     image->rgb.red[i]   = *buffer_ptr++ << 8;
120     image->rgb.green[i] = *buffer_ptr++ << 8;
121     image->rgb.blue[i]  = *buffer_ptr++ << 8;
122   }
123
124   /* return current buffer position for next decoding function */
125   return buffer_ptr;
126 }
127
128 Image *Read_PCX_to_Image(char *filename)
129 {
130   FILE *file;
131   byte *file_buffer;
132   byte *buffer_ptr, *buffer_last;
133   unsigned int file_length;
134   struct PCX_Header pcx;
135   Image *image;
136   int width, height, depth;
137   int i;
138
139   errno_pcx = PCX_Success;
140
141   if (!(file = fopen(filename, "r")))
142   {
143     errno_pcx = PCX_OpenFailed;
144     return NULL;
145   }
146
147   if (fseek(file, 0, SEEK_END) == -1)
148   {
149     fclose(file);
150     errno_pcx = PCX_ReadFailed;
151     return NULL;
152   }
153
154   file_length = ftell(file);
155   rewind(file);
156
157   if (file_length < PCX_HEADER_SIZE)
158   {
159     /* PCX file is too short to contain a valid PCX header */
160     fclose(file);
161
162     errno_pcx = PCX_FileInvalid;
163     return NULL;
164   }
165
166   file_buffer = checked_malloc(file_length);
167
168   if (fread(file_buffer, 1, file_length, file) != file_length)
169   {
170     fclose(file);
171     errno_pcx = PCX_ReadFailed;
172     return NULL;
173   }
174
175   fclose(file);
176
177   pcx.signature      = file_buffer[0];
178   pcx.version        = file_buffer[1];
179   pcx.encoding       = file_buffer[2];
180   pcx.bits_per_pixel = file_buffer[3];
181   pcx.xmin           = file_buffer[4]  + 256 * file_buffer[5];
182   pcx.ymin           = file_buffer[6]  + 256 * file_buffer[7];
183   pcx.xmax           = file_buffer[8]  + 256 * file_buffer[9];
184   pcx.ymax           = file_buffer[10] + 256 * file_buffer[11];
185   pcx.color_planes   = file_buffer[65];
186   pcx.bytes_per_line = file_buffer[66] + 256 * file_buffer[67];
187   pcx.palette_type   = file_buffer[68] + 256 * file_buffer[69];
188
189   width  = pcx.xmax - pcx.xmin + 1;
190   height = pcx.ymax - pcx.ymin + 1;
191   depth  = pcx.bits_per_pixel;
192
193   if (pcx.signature != PCX_MAGIC || pcx.version > PCX_LAST_VERSION ||
194       pcx.encoding != PCX_ENCODING || pcx.color_planes > PCX_MAXDEPTH ||
195       width < 0 || height < 0)
196   {
197     free(file_buffer);
198
199     errno_pcx = PCX_FileInvalid;
200     return NULL;
201   }
202
203 #if PCX_DEBUG
204   if (options.verbose)
205   {
206     printf("%s is a %dx%d PC Paintbrush image with %d bitplanes\n",
207            filename, width, height,
208            pcx.color_planes);
209     printf("depth: %d\n", pcx.bits_per_pixel);
210     printf("color_planes: %d\n", pcx.color_planes);
211     printf("bytes_per_line: %d\n", pcx.bytes_per_line);
212     printf("palette type: %s\n",
213            (pcx.palette_type == 1 ? "color" :
214             pcx.palette_type == 2 ? "grayscale" : "undefined"));
215   }
216 #endif
217
218   /* allocate new image structure */
219   image = newImage(width, height, depth);
220
221   buffer_ptr  = file_buffer + PCX_HEADER_SIZE;
222   buffer_last = file_buffer + file_length;
223
224   /* read compressed bitmap data */
225   if ((buffer_ptr = PCX_ReadBitmap(image, buffer_ptr, buffer_last)) == NULL)
226   {
227     free(file_buffer);
228     freeImage(image);
229
230     errno_pcx = PCX_FileInvalid;
231     return NULL;
232   }
233
234   if (file_length < PCX_HEADER_SIZE + PCX_COLORMAP_SIZE)
235   {
236     /* PCX file is too short to contain a valid 256 colors colormap */
237     fclose(file);
238     errno_pcx = PCX_ColorFailed;
239     return NULL;
240   }
241
242   /* read colormap data */
243   if (!PCX_ReadColormap(image, buffer_ptr, buffer_last))
244   {
245     free(file_buffer);
246     freeImage(image);
247     errno_pcx = PCX_ColorFailed;
248     return NULL;
249   }
250
251   free(file_buffer);
252
253   /* determine number of used colormap entries */
254   image->rgb.used = 0;
255   for (i=0; i<PCX_MAXCOLORS; i++)
256     if (image->rgb.color_used[i])
257       image->rgb.used++;
258
259 #if PCX_DEBUG
260   if (options.verbose)
261     printf("Read_PCX_to_Image: %d colors found\n", image->rgb.used);
262 #endif
263
264   return image;
265 }
266
267 #endif /* !TARGET_SDL */