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