1 /*
2  *  Abuse - dark 2D side-scrolling platform game
3  *  Copyright (c) 1995 Crack dot Com
4  *  Copyright (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
5  *
6  *  This software was released into the Public Domain. As with most public
7  *  domain software, no warranty is made or implied by Crack dot Com, by
8  *  Jonathan Clark, or by Sam Hocevar.
9  */
10 
11 #if defined HAVE_CONFIG_H
12 #   include "config.h"
13 #endif
14 
15 #include "common.h"
16 
17 #include "pcxread.h"
18 #include "specs.h"
19 
20 enum PCX_type { not_PCX, PCX_8, PCX_24 };
21 
22 struct PCX_header_type
23 {
24   char manufactururer,version,encoding,bits_per_pixel;
25   short xmin,ymin,xmax,ymax,hres,vres;
26   char palette[48];
27   char reserved,color_planes;
28   short bytes_per_line,palette_type;
29   char filter[58];
30 } PCX_header;
31 
read_PCX_header(FILE * fp)32 int read_PCX_header(FILE *fp)
33 {
34   if (!fread(&PCX_header.manufactururer,1,1,fp)) return 0;
35   if (!fread(&PCX_header.version,1,1,fp)) return 0;
36   if (!fread(&PCX_header.encoding,1,1,fp)) return 0;
37   if (!fread(&PCX_header.bits_per_pixel,1,1,fp)) return 0;
38   PCX_header.xmin=read_uint16(fp);
39   PCX_header.ymin=read_uint16(fp);
40   PCX_header.xmax=read_uint16(fp);
41   PCX_header.ymax=read_uint16(fp);
42   PCX_header.hres=read_uint16(fp);
43   PCX_header.vres=read_uint16(fp);
44   if (!fread(PCX_header.palette,1,48,fp)) return 0;
45   if (!fread(&PCX_header.reserved,1,1,fp)) return 0;
46   if (!fread(&PCX_header.color_planes,1,1,fp)) return 0;
47   PCX_header.bytes_per_line=read_uint16(fp);
48   PCX_header.palette_type=read_uint16(fp);
49   if (!fread(PCX_header.filter,1,58,fp)) return 0;
50   return 1;
51 }
52 
write_PCX_header(FILE * fp)53 int write_PCX_header(FILE *fp)
54 {
55   if (!fwrite(&PCX_header.manufactururer,1,1,fp)) return 0;
56   if (!fwrite(&PCX_header.version,1,1,fp)) return 0;
57   if (!fwrite(&PCX_header.encoding,1,1,fp)) return 0;
58   if (!fwrite(&PCX_header.bits_per_pixel,1,1,fp)) return 0;
59   write_uint16(fp,PCX_header.xmin);
60   write_uint16(fp,PCX_header.ymin);
61   write_uint16(fp,PCX_header.xmax);
62   write_uint16(fp,PCX_header.ymax);
63   write_uint16(fp,PCX_header.hres);
64   write_uint16(fp,PCX_header.vres);
65   if (!fwrite(PCX_header.palette,1,48,fp)) return 0;
66   if (!fwrite(&PCX_header.reserved,1,1,fp)) return 0;
67   if (!fwrite(&PCX_header.color_planes,1,1,fp)) return 0;
68   write_uint16(fp,PCX_header.bytes_per_line);
69   write_uint16(fp,PCX_header.palette_type);
70   if (!fwrite(PCX_header.filter,1,58,fp)) return 0;
71   return 1;
72 }
73 
PCX_file_type(char const * filename)74 PCX_type PCX_file_type(char const *filename)
75 {
76   FILE *fp=fopen(filename,"rb");
77   if (!fp)
78     return not_PCX;
79 
80   if (!read_PCX_header(fp))
81   {
82     fclose(fp);
83     return not_PCX;
84   }
85   fclose(fp);
86   if (PCX_header.manufactururer!=10)
87     return not_PCX;
88   if (PCX_header.color_planes==3 && PCX_header.bits_per_pixel==8)
89     return PCX_24;
90   else if (PCX_header.color_planes==1 && PCX_header.bits_per_pixel==8)
91     return PCX_8;
92   else return not_PCX;
93 }
94 
read_PCX_line(FILE * fp,unsigned char * start,short skip,int width)95 void read_PCX_line(FILE *fp, unsigned char *start, short skip, int width)
96 {
97   int c,n=0,i;
98 
99   do
100   {
101     c=fgetc(fp)&0xff;
102     if ((c&0xc0)==0xc0)
103     {
104       i=c&0x3f;
105       c=fgetc(fp);
106       while (i--)
107       {
108     *start=c;
109     start+=skip;
110         n++;
111       }
112     }
113     else
114     {
115       *start=c;
116       start+=skip;
117       n++;
118     }
119   } while (n<width);
120 }
121 
read_PCX(char const * filename,palette * & pal)122 image *read_PCX(char const *filename, palette *&pal)
123 {
124   if (PCX_file_type(filename)!=PCX_8) return NULL;
125   FILE *fp=fopen(filename,"rb");
126   read_PCX_header(fp);
127 
128   image *im=new image(vec2i(PCX_header.xmax-PCX_header.xmin+1,
129                             PCX_header.ymax-PCX_header.ymin+1));
130   int y;
131   for (y=0;y<im->Size().y;y++)
132     read_PCX_line(fp,im->scan_line(y),1,PCX_header.bytes_per_line);
133   unsigned char palette_confirm;
134   if (!fread(&palette_confirm,1,1,fp) || palette_confirm!=12)
135   {
136     pal=new palette;
137     pal->defaults();
138   }
139   else
140   {
141     pal=new palette;
142     fread(pal->addr(),1,256*3,fp);
143   }
144   fclose(fp);
145   return im;
146 }
147 
write_PCX(image * im,palette * pal,char const * filename)148 void write_PCX(image *im, palette *pal, char const *filename)
149 {
150   FILE *fp=fopen(filename,"wb");
151   if (!fp)
152     return ;
153 
154   PCX_header.manufactururer=10;
155   PCX_header.version=5;
156   PCX_header.encoding=1;
157   PCX_header.bits_per_pixel=8;
158   PCX_header.xmin=0;
159   PCX_header.ymin=0;
160   PCX_header.xmax=im->Size().x-1;
161   PCX_header.ymax=im->Size().y-1;
162   PCX_header.hres=320;
163   PCX_header.vres=200;
164   PCX_header.reserved=0;
165   PCX_header.color_planes=1;
166   PCX_header.bytes_per_line=im->Size().x;
167   PCX_header.palette_type=0;
168   memset(PCX_header.filter,0,58);
169 
170   if (!write_PCX_header(fp))
171   {
172     fclose(fp);
173     return;
174   }
175 
176   int y,run_length,x;
177   unsigned char *sl,code;
178   for (y=0; y<im->Size().y; y++)
179   {
180     sl=im->scan_line(y);
181     for (x=0; x<im->Size().x; )
182     {
183       run_length=1;
184       while (x+run_length<im->Size().x && sl[x]==sl[x+run_length])
185         run_length++;
186       if (run_length==1 && sl[x]<64)
187         fputc(sl[x],fp);
188       else
189       {
190         if (run_length>=64)
191       run_length=63;
192     code=0xc0 | run_length;
193     fputc(code,fp);
194     fputc(sl[x],fp);
195 
196       }
197       x+=run_length;
198 
199     }
200   }
201   fputc(12,fp);  // note that there is a palette attached
202   fwrite(pal->addr(),1,256*3,fp);
203   fclose(fp);
204 }
205 
206