1 /*	SCCS Id: @(#)txt2iff.c	3.2	95/07/28	*/
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 <proto/exec.h>
22 # include <proto/iffparse.h>
23 # include <proto/dos.h>
24 #endif
25 
26 void panic(const char *);
27 void map_colors(void);
28 int BestMatch(int, int, int);
29 
30 extern pixval ColorMap[3][MAXCOLORMAPSIZE];
31 extern int colorsinmap;
32 
33 /*
34  * WARNING:
35  * This program carries forth the assumption that the colormaps in all
36  * of the .txt files are the same.  This is a bug.
37  */
38 
39 struct {
40 	int Height;
41 	int Width;
42 } IFFScreen;
43 
44 /*
45  * We are using a hybrid form of our own design which we call a BMAP (for
46  * bitmap) form.  It is an ILBM with the bitmaps already deinterleaved,
47  * completely uncompressed.
48  * This speeds the loading of the images from the games point of view because it
49  * does not have to deinterleave and uncompress them.
50  */
51 #define	ID_BMAP	MAKE_ID( 'B', 'M', 'A', 'P' )	/* instead of ILBM */
52 #define	ID_BMHD	MAKE_ID( 'B', 'M', 'H', 'D' )	/* Same as ILBM */
53 #define	ID_CAMG	MAKE_ID( 'C', 'A', 'M', 'G' )	/* Same as ILBM */
54 #define	ID_CMAP	MAKE_ID( 'C', 'M', 'A', 'P' )	/* Same as ILBM */
55 #define	ID_PDAT	MAKE_ID( 'P', 'D', 'A', 'T' )	/* Extra data describing plane
56 						 * size due to graphics.library
57 						 * rounding requirements.
58 						 */
59 #define	ID_PLNE	MAKE_ID( 'P', 'L', 'N', 'E' )	/* The planes of the image */
60 
61 
62 #ifndef _DCC
63 extern
64 #endif
65 struct Library *IFFParseBase;
66 
67 int nplanes;
68 
69 
70 /* BMHD from IFF documentation */
71 typedef struct {
72     UWORD w, h;
73     WORD x, y;
74     UBYTE nPlanes;
75     UBYTE masking;
76     UBYTE compression;
77     UBYTE reserved1;
78     UWORD transparentColor;
79     UBYTE xAspect, yAspect;
80     WORD pageWidth, pageHeight;
81 } BitMapHeader;
82 
83 typedef struct {
84     UBYTE r, g, b;
85 } AmiColorMap;
86 
87 pixel pixels[TILE_Y][TILE_X];
88 AmiColorMap *cmap;
89 
90 
91 int findcolor( register pixel *pix );
92 void packwritebody(  pixel (*tile)[TILE_X], char **planes, int tileno );
93 
94 
95 void
error(char * str)96 error( char *str )
97 {
98     fprintf( stderr, "ERROR: %s\n", str );
99 }
100 
101 /*
102  * This array maps the image colors to the amiga's first 16 colors.  The colors
103  * are reordered to help with maintaining dripen settings.
104  */
105 int colrmap[] = { 0, 6, 9, 15, 4, 10, 2, 3, 5, 11, 7, 13, 8, 1, 14, 12 };
106 
107 /* How many tiles fit across and down. */
108 
109 #define COLS 20
110 #define ROWS ((tiles + COLS-1) / COLS)
111 
main(int argc,char ** argv)112 main( int argc, char **argv )
113 {
114     int colors;
115     struct {
116 	long nplanes;
117 	long pbytes;
118 	long across;
119 	long down;
120 	long npics;
121 	long xsize;
122 	long ysize;
123     } pdat;
124     long pbytes;	/* Bytes of data in a plane */
125     int i, cnt;
126     BitMapHeader bmhd;
127     struct IFFHandle *iff;
128     long camg = HIRES|LACE;
129     int tiles=0;
130     char **planes;
131 
132     if(argc != 3){
133 	fprintf(stderr, "Usage: %s source destination\n", argv[0]);
134 	exit(1);
135     }
136 
137 #if defined(_DCC) || defined(__GNUC__)
138     IFFParseBase = OpenLibrary( "iffparse.library", 0 );
139     if( !IFFParseBase ) {
140 	error( "unable to open iffparse.library" );
141 	exit( 1 );
142     }
143 #endif
144 
145     /* First, count the files in the file */
146     if( fopen_text_file( argv[1], "r" ) != TRUE )
147     {
148 	perror( argv[1] );
149 	return( 1 );
150     }
151 
152     nplanes = 0;
153     i = colorsinmap-1; /*IFFScreen.Colors - 1; */
154     while( i != 0 )
155     {
156 	nplanes++;
157 	i >>= 1;
158     }
159 
160     planes = malloc( nplanes * sizeof( char * ) );
161     if( planes == 0 )
162     {
163 	error( "can not allocate planes pointer" );
164 	exit( 1 );
165     }
166 
167     while( read_text_tile( pixels ) == TRUE )
168 	++tiles;
169     fclose_text_file();
170 
171     IFFScreen.Width = COLS * TILE_X;
172     IFFScreen.Height = ROWS * TILE_Y;
173 
174     pbytes = (COLS * ROWS * TILE_X + 15) / 16 * 2 * TILE_Y;
175 
176     for( i = 0; i < nplanes; ++i )
177     {
178 	planes[ i ] = calloc( 1, pbytes );
179 	if( planes[ i ] == 0 )
180 	{
181 	    error( "can not allocate planes pointer" );
182 	    exit( 1 );
183 	}
184     }
185 
186     /* Now, process it */
187     if( fopen_text_file( argv[1], "r" ) != TRUE )
188     {
189 	perror( argv[1] );
190 	return( 1 );
191     }
192 
193     iff = AllocIFF();
194     if( !iff )
195     {
196 	error( "Can not allocate IFFHandle" );
197 	return( 1 );
198     }
199 
200     iff->iff_Stream = Open( argv[2], MODE_NEWFILE );
201     if( !iff->iff_Stream )
202     {
203 	error( "Can not open output file" );
204 	return( 1 );
205     }
206 
207     InitIFFasDOS( iff );
208     OpenIFF( iff, IFFF_WRITE );
209 
210     PushChunk( iff, ID_BMAP, ID_FORM, IFFSIZE_UNKNOWN );
211 
212     bmhd.w = IFFScreen.Width;
213     bmhd.h = IFFScreen.Height;
214     bmhd.x = 0;
215     bmhd.y = 0;
216     bmhd.nPlanes = nplanes;
217     bmhd.masking = 0;
218     bmhd.compression = 0;
219     bmhd.reserved1 = 0;
220     bmhd.transparentColor = 0;
221     bmhd.xAspect = 100;
222     bmhd.yAspect = 100;
223     bmhd.pageWidth = TILE_X;
224     bmhd.pageHeight = TILE_Y;
225 
226     PushChunk( iff, ID_BMAP, ID_BMHD, sizeof( bmhd ) );
227     WriteChunkBytes( iff, &bmhd, sizeof( bmhd ) );
228     PopChunk( iff );
229 
230     PushChunk( iff, ID_BMAP, ID_CAMG, sizeof( camg ) );
231     WriteChunkBytes( iff, &camg, sizeof( camg ) );
232     PopChunk( iff );
233 
234     /* We need to reorder the colors to get reasonable default pens but
235      * we also need to know where some of the colors are - so go find out.
236      */
237     map_colors();
238 
239     cmap = malloc( (colors = (1L<<nplanes)) * sizeof(AmiColorMap) );
240     for( i = 0; i < colors; ++i )
241     {
242 	cmap[ colrmap[ i ] ].r = ColorMap[ CM_RED ][ i ];
243 	cmap[ colrmap[ i ] ].g = ColorMap[ CM_GREEN ][ i ];
244 	cmap[ colrmap[ i ] ].b = ColorMap[ CM_BLUE ][ i ];
245     }
246 
247     PushChunk( iff, ID_BMAP, ID_CMAP, IFFSIZE_UNKNOWN );
248     for (i = 0; i < colors; ++i)
249 	WriteChunkBytes(iff, &cmap[i], 3);
250     PopChunk( iff );
251 
252     cnt = 0;
253     while( read_text_tile( pixels ) == TRUE )
254     {
255 	packwritebody( pixels, planes, cnt );
256 	if( cnt % 20 == 0 )
257 	    printf( "%d..", cnt );
258 	++cnt;
259 	fflush( stdout );
260     }
261 
262     pdat.nplanes = nplanes;
263     pdat.pbytes = pbytes;
264     pdat.xsize = TILE_X;
265     pdat.ysize = TILE_Y;
266     pdat.across = COLS;
267     pdat.down = ROWS;
268     pdat.npics = cnt;
269 
270     PushChunk( iff, ID_BMAP, ID_PDAT, IFFSIZE_UNKNOWN );
271     WriteChunkBytes( iff, &pdat, sizeof( pdat ) );
272     PopChunk( iff );
273 
274     PushChunk( iff, ID_BMAP, ID_PLNE, IFFSIZE_UNKNOWN );
275     for( i = 0; i < nplanes; ++i )
276 	WriteChunkBytes( iff, planes[i], pbytes );
277     PopChunk( iff );
278 
279     CloseIFF( iff );
280     Close( iff->iff_Stream );
281     FreeIFF( iff );
282 
283     printf( "\n%d tiles converted\n", cnt );
284 
285 #if defined(_DCC) || defined(__GNUC__)
286     CloseLibrary( IFFParseBase );
287 #endif
288     exit( 0 );
289 }
290 
findcolor(register pixel * pix)291 findcolor( register pixel *pix )
292 {
293     register int i;
294 
295     for( i = 0; i < MAXCOLORMAPSIZE; ++i )
296     {
297 	if( (pix->r == ColorMap[ CM_RED ][i] ) &&
298 	    (pix->g == ColorMap[ CM_GREEN ][i] ) &&
299 	    (pix->b == ColorMap[ CM_BLUE ][i] ) )
300 	{
301 	    return( i );
302 	}
303     }
304     return( -1 );
305 }
306 
307 void
packwritebody(pixel (* tile)[TILE_X],char ** planes,int tileno)308 packwritebody( pixel (*tile)[TILE_X], char **planes, int tileno )
309 {
310     register int i, j, k, col;
311     register char *buf;
312     register int across, rowbytes, xoff, yoff;
313 
314     /* how many tiles fit across? */
315     across = COLS;
316 
317     /* How many bytes per pixel row */
318     rowbytes = ((IFFScreen.Width + 15)/16)*2;
319 
320     /* How many bytes to account for y distance in planes */
321     yoff = ((tileno / across) * TILE_Y) * rowbytes;
322 
323     /* How many bytes to account for x distance in planes */
324     xoff = (tileno % across) * (TILE_X/8);
325 
326     /* For each row... */
327     for( i = 0; i < TILE_Y; ++i )
328     {
329 	/* For each bitplane... */
330 	for( k = 0; k < nplanes; ++k )
331 	{
332 	    const int mask = 1l<<k;
333 
334 	    /* Go across the row */
335 	    for( j = 0; j < TILE_X; j++ )
336 	    {
337 		col = findcolor( &tile[ i ][ j ] );
338 		if( col == -1 )
339 		{
340 		    error( "can not convert pixel color to colormap index" );
341 		    return;
342 		}
343 		/* Shift the colors around to have good complements and to
344 		 * know the dripen values.
345 		 */
346 		col = colrmap[ col ];
347 
348 		/* To top left corner of tile */
349 		buf = planes[ k ] + yoff + xoff;
350 
351 		/*To i'th row of tile and the correct byte for the j'th pixel*/
352 		buf += ( i * rowbytes ) + (j/8);
353 
354 		/* Or in the bit for this color */
355 		*buf |= (((col & mask)!=0)<<(7-(j%8)));
356 	    }
357 	}
358     }
359 }
360 
361 /* #define DBG */
362 
363 /* map_colors
364  * The incoming colormap is in arbitrary order and has arbitrary colors in
365  * it, but we need (some) specific colors in specific places.  Find the
366  * colors we need and fix the mapping table to match.
367  */
368 /* What we are aiming for: */
369 /* XXX was 0-7 */
370 #define CX_BLACK	0
371 #define CX_WHITE	1
372 #define CX_BROWN	11
373 #define CX_CYAN		2
374 #define CX_GREEN	5
375 #define CX_MAGENTA	10
376 #define CX_BLUE		4
377 #define CX_RED		7
378 /* we don't care about the rest, at least now */
379 /* should get: black white blue red grey greyblue ltgrey */
380 void
map_colors()381 map_colors(){
382 	int x;
383 #if 1
384 int tmpmap[]={0,2,3,7,4,5,8,9,10,11,13,15,12,1,14,6};
385 /* still not right: gray green yellow lost somewhere? */
386 #else
387 	int tmpmap[16];
388 	int x,y;
389 	for(x=0;x<16;x++)tmpmap[x]=-1;	/* set not assigned yet */
390 
391 	tmpmap[BestMatch(0,0,0)] = CX_BLACK;
392 	tmpmap[BestMatch(255,255,255)] = CX_WHITE;
393 	tmpmap[BestMatch(255,0,0)] = CX_RED;
394 	tmpmap[BestMatch(0,255,0)] = CX_GREEN;
395 	tmpmap[BestMatch(0,0,255)] = CX_BLUE;
396 
397 		/* clean up the rest */
398 	for(x=0;x<16;x++){
399 		for(y=0;y<16;y++)
400 			if(tmpmap[y]==x)goto outer_cont;
401 		for(y=0;y<16;y++)
402 			if(tmpmap[y]==-1){
403 				tmpmap[y]=x;
404 				break;
405 			}
406 		if(y==16)panic("too many colors?");
407 outer_cont: ;
408 	}
409 	for(x=0;x<16;x++)
410 		if(tmpmap[y]==-1)panic("lost color?");
411 #endif
412 	for(x=0;x<16;x++){
413 #ifdef DBG
414 		printf("final: c[%d]=%d (target: %d)\n",x,tmpmap[x],colrmap[x]);
415 #endif
416 		colrmap[x]=tmpmap[x];
417 	}
418 }
BestMatch(r,g,b)419 BestMatch(r,g,b)
420 	int r,g,b;
421 {
422 	int x;
423 	int bestslot;
424 	int bestrate=99999999L;
425 	for(x=0;x<16;x++){
426 		int rr = r-ColorMap[CM_RED][x];
427 		int gg = g-ColorMap[CM_GREEN][x];
428 		int bb = b-ColorMap[CM_BLUE][x];
429 		int rate = rr*rr + gg*gg + bb*bb;
430 		if(bestrate > rate){
431 			bestrate = rate;
432 			bestslot = x;
433 		}
434 	}
435 #ifdef DBG
436 	printf("map (%d,%d,%d) -> %d (error=%d)\n",r,g,b,bestslot,bestrate);
437 #endif
438 	return bestslot;
439 }
440 
441 
442 long *
alloc(unsigned int n)443 alloc( unsigned int n )
444 {
445     long *ret = malloc( n );
446     if(!ret){
447 	error("Can't allocate memory");
448 	exit(1);
449     }
450     return( ret );
451 }
452 
453 void
panic(const char * msg)454 panic(const char *msg){
455     fprintf(stderr,"PANIC: %s\n",msg);
456     exit(1);
457 }
458