1 /*	SCCS Id: @(#)xpm2iff.c	3.2	95/08/04	*/
2 /* 	Copyright (c) 1995 by Gregg Wonderly, Naperville, Illinois */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 #include <stdlib.h>
6 
7 #include "config.h"
8 #include "tile.h"
9 
10 #include <dos/dos.h>
11 #include <dos/dos.h>
12 #include <dos/dosextens.h>
13 #include <graphics/gfx.h>
14 #include <graphics/gfxbase.h>
15 #include <graphics/view.h>
16 #include <libraries/iffparse.h>
17 #include <libraries/dos.h>
18 #include <clib/dos_protos.h>
19 #include <clib/iffparse_protos.h>
20 #ifndef _DCC
21 # include <pragmas/iffparse_pragmas.h>
22 # include <pragmas/dos_pragmas.h>
23 # include <pragmas/exec_pragmas.h>
24 #endif
25 
26 struct xpmscreen {
27 	int	Width;
28 	int	Height;
29 	int	Colors;
30 	int	ColorResolution;
31 	int	Background;
32 	int	AspectRatio;
33 	int	Interlace;
34 	int	BytesPerRow;
35 } XpmScreen;
36 
37 /* translation table from xpm characters to RGB and colormap slots */
38 struct Ttable {
39 	char flag;
40 	char r,g,b;
41 	int slot;	/* output colortable index */
42 }ttable[256];
43 
44 pixval ColorMap[3][MAXCOLORMAPSIZE];
45 int colorsinmap;
46 
47 /*
48  * We are using a hybrid form of our own design which we call a BMAP (for
49  * bitmap) form.  It is an ILBM with the bitmaps already deinterleaved,
50  * completely uncompressed.
51  * This speeds the loading of the images from the games point of view because it
52  * does not have to deinterleave and uncompress them.
53  */
54 #define	ID_BMAP	MAKE_ID( 'B', 'M', 'A', 'P' )	/* instead of ILBM */
55 #define	ID_BMHD	MAKE_ID( 'B', 'M', 'H', 'D' )	/* Same as ILBM */
56 #define	ID_CAMG	MAKE_ID( 'C', 'A', 'M', 'G' )	/* Same as ILBM */
57 #define	ID_CMAP	MAKE_ID( 'C', 'M', 'A', 'P' )	/* Same as ILBM */
58 #define	ID_PDAT	MAKE_ID( 'P', 'D', 'A', 'T' )	/* Extra data describing plane
59 						 * size due to graphics.library
60 						 * rounding requirements.
61 						 */
62 #define	ID_PLNE	MAKE_ID( 'P', 'L', 'N', 'E' )	/* The planes of the image */
63 
64 extern struct DOSBase *DOSBase;
65 #ifndef _DCC
66 extern
67 #endif
68 struct Library *IFFParseBase;
69 
70 int nplanes;
71 
72 /* BMHD from IFF documentation */
73 typedef struct {
74     UWORD w, h;
75     WORD x, y;
76     UBYTE nPlanes;
77     UBYTE masking;
78     UBYTE compression;
79     UBYTE reserved1;
80     UWORD transparentColor;
81     UBYTE xAspect, yAspect;
82     WORD pageWidth, pageHeight;
83 } BitMapHeader;
84 
85 typedef struct {
86     UBYTE r, g, b;
87 } AmiColorMap;
88 
89 pixel pixels[TILE_Y][TILE_X];
90 AmiColorMap *cmap;
91 
92 void
error(char * str)93 error( char *str )
94 {
95     fprintf( stderr, "ERROR: %s\n", str );
96 }
97 
98 char **planes;
99 
main(int argc,char ** argv)100 main( int argc, char **argv )
101 {
102     int colors;
103     struct {
104 	long nplanes;
105 	long pbytes;
106 	long across;
107 	long down;
108 	long npics;
109 	long xsize;
110 	long ysize;
111     } pdat;
112     long pbytes;	/* Bytes of data in a plane */
113     int i, cnt;
114     BitMapHeader bmhd;
115     struct IFFHandle *iff;
116     long camg = HIRES|LACE;
117     int tiles=0;
118     int index;
119 
120 #ifdef _DCC
121     IFFParseBase = OpenLibrary( "iffparse.library", 0 );
122     if( !IFFParseBase ) {
123 	error( "unable to open iffparse.library" );
124 	exit( 1 );
125     }
126 #endif
127 
128     if( fopen_xpm_file( argv[1], "r" ) != TRUE )
129     {
130 	perror( argv[1] );
131 	return( 1 );
132     }
133 
134     nplanes = 0;
135     i = XpmScreen.Colors - 1;
136     while( i != 0 )
137     {
138 	nplanes++;
139 	i >>= 1;
140     }
141 
142     planes = malloc( nplanes * sizeof( char * ) );
143     if( planes == 0 )
144     {
145 	error( "can not allocate planes pointer" );
146 	exit( 1 );
147     }
148 
149     XpmScreen.BytesPerRow = ((XpmScreen.Width + 15)/16)*2;
150     pbytes = XpmScreen.BytesPerRow * XpmScreen.Height;
151     for( i = 0; i < nplanes; ++i )
152     {
153 	planes[ i ] = malloc( pbytes );
154 	if( planes[ i ] == 0 )
155 	{
156 	    error( "can not allocate planes pointer" );
157 	    exit( 1 );
158 	}
159 	memset( planes[i], 0, pbytes );
160     }
161 
162     iff = AllocIFF();
163     if( !iff )
164     {
165 	error( "Can not allocate IFFHandle" );
166 	return( 1 );
167     }
168 
169     iff->iff_Stream = Open( argv[2], MODE_NEWFILE );
170     if( !iff->iff_Stream )
171     {
172 	error( "Can not open output file" );
173 	return( 1 );
174     }
175 
176     InitIFFasDOS( iff );
177     OpenIFF( iff, IFFF_WRITE );
178 
179     PushChunk( iff, ID_BMAP, ID_FORM, IFFSIZE_UNKNOWN );
180 
181     bmhd.w = XpmScreen.Width;
182     bmhd.h = XpmScreen.Height;
183     bmhd.x = 0;
184     bmhd.y = 0;
185     bmhd.nPlanes = nplanes;
186     bmhd.masking = 0;
187     bmhd.compression = 0;
188     bmhd.reserved1 = 0;
189     bmhd.transparentColor = 0;
190     bmhd.xAspect = 100;
191     bmhd.yAspect = 100;
192     bmhd.pageWidth = 0;			/* not needed for this program */
193     bmhd.pageHeight = 0;		/* not needed for this program */
194 
195     PushChunk( iff, ID_BMAP, ID_BMHD, sizeof( bmhd ) );
196     WriteChunkBytes( iff, &bmhd, sizeof( bmhd ) );
197     PopChunk( iff );
198 
199     PushChunk( iff, ID_BMAP, ID_CAMG, sizeof( camg ) );
200     WriteChunkBytes( iff, &camg, sizeof( camg ) );
201     PopChunk( iff );
202 
203 #define SCALE(x) (x)
204     cmap = malloc( (colors = (1L<<nplanes)) * sizeof(AmiColorMap) );
205     if(cmap == 0){
206 	error("Can't allocate color map");
207 	exit(1);
208     }
209     for(index = 0; index<256; index++){
210 	if(ttable[index].flag){
211 	    cmap[ttable[index].slot].r = SCALE(ttable[index].r);
212 	    cmap[ttable[index].slot].g = SCALE(ttable[index].g);
213 	    cmap[ttable[index].slot].b = SCALE(ttable[index].b);
214 	}
215     }
216 #undef SCALE
217 
218     PushChunk( iff, ID_BMAP, ID_CMAP, IFFSIZE_UNKNOWN );
219     WriteChunkBytes( iff, cmap, colors*sizeof(*cmap) );
220     PopChunk( iff );
221 
222     conv_image();
223 
224     pdat.nplanes = nplanes;
225     pdat.pbytes = pbytes;
226     pdat.xsize = XpmScreen.Width;
227     pdat.ysize = XpmScreen.Height;
228     pdat.across = 0;
229     pdat.down = 0;
230     pdat.npics = 1;
231 
232     PushChunk( iff, ID_BMAP, ID_PDAT, IFFSIZE_UNKNOWN );
233     WriteChunkBytes( iff, &pdat, sizeof( pdat ) );
234     PopChunk( iff );
235 
236     PushChunk( iff, ID_BMAP, ID_PLNE, IFFSIZE_UNKNOWN );
237     for( i = 0; i < nplanes; ++i )
238 	WriteChunkBytes( iff, planes[i], pbytes );
239     PopChunk( iff );
240 
241     CloseIFF( iff );
242     Close( iff->iff_Stream );
243     FreeIFF( iff );
244 
245 #ifdef _DCC
246     CloseLibrary( IFFParseBase );
247 #endif
248     exit( 0 );
249 }
250 
251 #define SETBIT(Plane, Plane_offset, Col, Value)				\
252 	if(Value){							\
253 	    planes[Plane][Plane_offset + (Col/8)] |= 1<<(7-(Col & 7));	\
254 	}
255 
conv_image()256 conv_image(){
257     int row, col, planeno;
258 
259     for(row = 0;row<XpmScreen.Height;row++){
260 	char *xb = xpmgetline();
261 	int plane_offset;
262 	if(xb==0)return;
263 	plane_offset = row*XpmScreen.BytesPerRow;
264 	for(col = 0;col<XpmScreen.Width;col++){
265 	    int slot;
266 	    int color = xb[col];
267 	    if(!ttable[color].flag){
268 		fprintf(stderr, "Bad image data\n");
269 	    }
270 	    slot = ttable[color].slot;
271 	    for(planeno = 0; planeno<nplanes; planeno++){
272 		SETBIT(planeno, plane_offset, col, slot & (1<<planeno));
273 	    }
274 	}
275     }
276 }
277 
278 long *
alloc(unsigned int n)279 alloc( unsigned int n )
280 {
281     long *ret = malloc( n );
282     if(!ret){
283 	error("Can't allocate memory");
284 	exit(1);
285     }
286     return( ret );
287 }
288 
289 FILE *xpmfh = 0;
290 char initbuf[200];
291 char *xpmbuf = initbuf;
292 
293 /* version 1.  Reads the raw xpm file, NOT the compiled version.  This is
294  * not a particularly good idea but I don't have time to do the right thing
295  * at this point, even if I was absolutely sure what that was. */
fopen_xpm_file(const char * fn,const char * mode)296 fopen_xpm_file(const char *fn, const char *mode){
297 	int temp;
298 	char *xb;
299 	if(strcmp(mode, "r"))return FALSE;	/* no choice now */
300 	if(xpmfh)return FALSE;			/* one file at a time */
301 	xpmfh = fopen(fn, mode);
302 	if(!xpmfh)return FALSE;			/* I'm hard to please */
303 
304 		/* read the header */
305 	xb = xpmgetline();
306 	if(xb == 0)return FALSE;
307 	if(4 != sscanf(xb,"%d %d %d %d",
308 	  &XpmScreen.Width, &XpmScreen.Height,
309 	  &XpmScreen.Colors, &temp))return FALSE;	/* bad header */
310 		/* replace the original buffer with one big enough for
311 		 * the real data
312 		 */
313 /* XXX */
314 	xpmbuf = malloc(XpmScreen.Width * 2);
315 	if(!xpmbuf){
316 		error("Can't allocate line buffer");
317 		exit(1);
318 	}
319 	if(temp != 1)return FALSE;		/* limitation of this code */
320 
321 	{
322 		/* read the colormap and translation table */
323 	    int ccount = -1;
324 	    while(ccount++ < (XpmScreen.Colors-1)){
325 		char index;
326 		int r, g, b;
327 		xb = xpmgetline();
328 		if(xb==0)return FALSE;
329 		if(4 != sscanf(xb,"%c c #%2x%2x%2x",&index,&r,&g,&b)){
330 		    fprintf(stderr,"Bad color entry: %s\n",xb);
331 		    return FALSE;
332 		}
333 		ttable[index].flag = 1;	/* this color is valid */
334 		ttable[index].r = r;
335 		ttable[index].g = g;
336 		ttable[index].b = b;
337 		ttable[index].slot = ccount;
338 	    }
339 	}
340 	return TRUE;
341 }
342 
343 /* This deserves better.  Don't read it too closely - you'll get ill. */
344 #define bufsz 2048
345 char buf[bufsz];
xpmgetline()346 xpmgetline(){
347 	char *bp;
348 	do {
349 	    if(fgets(buf, bufsz, xpmfh) == 0)return 0;
350 	} while(buf[0] != '"');
351 		/* strip off the trailing <",> if any */
352 	for(bp = buf;*bp;bp++);
353 	bp--;
354 	while(isspace(*bp))bp--;
355 	if(*bp==',')bp--;
356 	if(*bp=='"')bp--;
357 	bp++;
358 	*bp = '\0';
359 
360 	return &buf[1];
361 }
362