1 /*  Copyright (C) 1996-1997  Id Software, Inc.
2 
3     This program is free software; you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation; either version 2 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     You should have received a copy of the GNU General Public License
14     along with this program; if not, write to the Free Software
15     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 
17     See file, 'COPYING', for details.
18 */
19 
20 // lbmlib.c
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 
29 #include "QF/qendian.h"
30 #include "QF/quakeio.h"
31 #include "QF/sys.h"
32 
33 #include "lbmlib.h"
34 
35 static int
LoadFile(const char * fname,byte ** buf)36 LoadFile (const char *fname, byte **buf)
37 {
38 	QFile      *file;
39 	byte       *src;
40 	int         len;
41 
42 	*buf = 0;
43 	file = Qopen (fname, "rt");
44 	if (!file)
45 		return 0;
46 	len = Qfilesize (file);
47 	src = malloc (len + 1);
48 	src[Qfilesize (file)] = 0;
49 	Qread (file, src, len);
50 	Qclose (file);
51 	*buf = src;
52 	return len;
53 }
54 
55 static QFile *
SafeOpenWrite(const char * filename)56 SafeOpenWrite (const char *filename)
57 {
58 	QFile	*f;
59 
60 	f = Qopen(filename, "wb");
61 
62 	if (!f)
63 		Sys_Error ("Error opening %s: %s",filename,strerror(errno));
64 
65 	return f;
66 }
67 
68 static void
SafeWrite(QFile * f,void * buffer,int count)69 SafeWrite (QFile *f, void *buffer, int count)
70 {
71 	if (Qwrite (f, buffer, count) != count)
72 		Sys_Error ("File read failure");
73 }
74 
75 static void
SaveFile(const char * filename,void * buffer,int count)76 SaveFile (const char *filename, void *buffer, int count)
77 {
78 	QFile	*f;
79 
80 	f = SafeOpenWrite (filename);
81 	SafeWrite (f, buffer, count);
82 	Qclose (f);
83 }
84 
85 /*
86 ============================================================================
87 
88 						LBM STUFF
89 
90 ============================================================================
91 */
92 
93 #define FORMID ('F'+('O'<<8)+((int)'R'<<16)+((int)'M'<<24))
94 #define ILBMID ('I'+('L'<<8)+((int)'B'<<16)+((int)'M'<<24))
95 #define PBMID  ('P'+('B'<<8)+((int)'M'<<16)+((int)' '<<24))
96 #define BMHDID ('B'+('M'<<8)+((int)'H'<<16)+((int)'D'<<24))
97 #define BODYID ('B'+('O'<<8)+((int)'D'<<16)+((int)'Y'<<24))
98 #define CMAPID ('C'+('M'<<8)+((int)'A'<<16)+((int)'P'<<24))
99 
100 
101 bmhd_t  bmhd;
102 
103 static int
Align(int l)104 Align (int l)
105 {
106 	if (l&1)
107 		return l+1;
108 	return l;
109 }
110 
111 /*
112 ================
113 LBMRLEdecompress
114 
115 Source must be evenly aligned!
116 ================
117 */
118 static byte *
LBMRLEDecompress(byte * source,byte * unpacked,int bpwidth)119 LBMRLEDecompress (byte *source, byte *unpacked, int bpwidth)
120 {
121 	int     count;
122 	byte    b, rept;
123 
124 	count = 0;
125 
126 	do {
127 		rept = *source++;
128 
129 		if (rept > 0x80) {
130 			rept = (rept ^ 0xff) + 2;
131 			b = *source++;
132 			memset (unpacked, b, rept);
133 			unpacked += rept;
134 		} else if (rept < 0x80) {
135 			rept++;
136 			memcpy (unpacked, source, rept);
137 			unpacked += rept;
138 			source += rept;
139 		} else
140 			rept = 0;               // rept of 0x80 is NOP
141 
142 		count += rept;
143 
144 	} while (count < bpwidth);
145 
146 	if (count > bpwidth)
147 		Sys_Error ("Decompression exceeded width!\n");
148 
149 
150 	return source;
151 }
152 
153 #define BPLANESIZE      128
154 byte    bitplanes[9][BPLANESIZE];       // max size 1024 by 9 bit planes
155 
156 /*
157 =================
158 MungeBitPlanes8
159 
160 Asm version destroys the bit plane data!
161 =================
162 */
163 static void
MungeBitPlanes8(int width,byte * dest)164 MungeBitPlanes8 (int width, byte *dest)
165 {
166 	int		i, ind = 0;
167 
168 	while (width--) {
169 		for (i = 0; i < 8; i++) {
170 			*dest++ = (((bitplanes[7][ind] << i) & 128) >> 0)
171 				| (((bitplanes[6][ind] << i) & 128) >> 1)
172 				| (((bitplanes[5][ind] << i) & 128) >> 2)
173 				| (((bitplanes[4][ind] << i) & 128) >> 3)
174 				| (((bitplanes[3][ind] << i) & 128) >> 4)
175 				| (((bitplanes[2][ind] << i) & 128) >> 5)
176 				| (((bitplanes[1][ind] << i) & 128) >> 6)
177 				| (((bitplanes[0][ind] << i) & 128) >> 7);
178 		}
179 		ind++;
180 	}
181 #if 0
182 asm     les     di,[dest]
183 asm     mov     si,-1
184 asm     mov     cx,[width]
185 mungebyte:
186 asm     inc     si
187 asm     mov     dx,8
188 mungebit:
189 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*7 +si],1
190 asm     rcl     al,1
191 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*6 +si],1
192 asm     rcl     al,1
193 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*5 +si],1
194 asm     rcl     al,1
195 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*4 +si],1
196 asm     rcl     al,1
197 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*3 +si],1
198 asm     rcl     al,1
199 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*2 +si],1
200 asm     rcl     al,1
201 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*1 +si],1
202 asm     rcl     al,1
203 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*0 +si],1
204 asm     rcl     al,1
205 asm     stosb
206 asm     dec     cx
207 asm     jz      done
208 asm     dec     dx
209 asm     jnz     mungebit
210 asm     jmp     mungebyte
211 
212 done:
213 #endif
214 }
215 
216 static void
MungeBitPlanes4(int width,byte * dest)217 MungeBitPlanes4 (int width, byte *dest)
218 {
219 	int		i, ind = 0;
220 
221 	while (width--) {
222 		for (i = 0; i < 8; i++) {
223 			*dest++ = (((bitplanes[3][ind] << i) & 128) >> 4)
224 				| (((bitplanes[2][ind] << i) & 128) >> 5)
225 				| (((bitplanes[1][ind] << i) & 128) >> 6)
226 				| (((bitplanes[0][ind] << i) & 128) >> 7);
227 		}
228 		ind++;
229 	}
230 #if 0
231 
232 asm     les     di,[dest]
233 asm     mov     si,-1
234 asm     mov     cx,[width]
235 mungebyte:
236 asm     inc     si
237 asm     mov     dx,8
238 mungebit:
239 asm     xor     al,al
240 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*3 +si],1
241 asm     rcl     al,1
242 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*2 +si],1
243 asm     rcl     al,1
244 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*1 +si],1
245 asm     rcl     al,1
246 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*0 +si],1
247 asm     rcl     al,1
248 asm     stosb
249 asm     dec     cx
250 asm     jz      done
251 asm     dec     dx
252 asm     jnz     mungebit
253 asm     jmp     mungebyte
254 
255 done:
256 #endif
257 }
258 
259 static void
MungeBitPlanes2(int width,byte * dest)260 MungeBitPlanes2 (int width, byte *dest)
261 {
262 	int		i, ind = 0;
263 
264 	while (width--) {
265 		for (i = 0; i < 8; i++) {
266 			*dest++ = (((bitplanes[1][ind] << i) & 128) >> 6)
267 					 | (((bitplanes[0][ind] << i) & 128) >> 7);
268 		}
269 		ind++;
270 	}
271 #if 0
272 asm     les     di,[dest]
273 asm     mov     si,-1
274 asm     mov     cx,[width]
275 mungebyte:
276 asm     inc     si
277 asm     mov     dx,8
278 mungebit:
279 asm     xor     al,al
280 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*1 +si],1
281 asm     rcl     al,1
282 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*0 +si],1
283 asm     rcl     al,1
284 asm     stosb
285 asm     dec     cx
286 asm     jz      done
287 asm     dec     dx
288 asm     jnz     mungebit
289 asm     jmp     mungebyte
290 
291 done:
292 #endif
293 }
294 
295 static void
MungeBitPlanes1(int width,byte * dest)296 MungeBitPlanes1 (int width, byte *dest)
297 {
298 	int		i, ind = 0;
299 
300 	while (width--) {
301 		for (i = 0; i < 8; i++) {
302 			*dest++ = (((bitplanes[0][ind] << i) & 128) >> 7);
303 		}
304 		ind++;
305 	}
306 #if 0
307 asm     les     di,[dest]
308 asm     mov     si,-1
309 asm     mov     cx,[width]
310 mungebyte:
311 asm     inc     si
312 asm     mov     dx,8
313 mungebit:
314 asm     xor     al,al
315 asm     shl     [BYTE PTR bitplanes + BPLANESIZE*0 +si],1
316 asm     rcl     al,1
317 asm     stosb
318 asm     dec     cx
319 asm     jz      done
320 asm     dec     dx
321 asm     jnz     mungebit
322 asm     jmp     mungebyte
323 
324 done:
325 #endif
326 }
327 
328 void
LoadLBM(char * filename,byte ** picture,byte ** palette)329 LoadLBM (char *filename, byte **picture, byte **palette)
330 {
331 	byte    *LBMbuffer, *picbuffer, *cmapbuffer;
332 	byte    *LBM_P, *LBMEND_P;
333 	byte    *pic_p;
334 	byte    *body_p;
335 	unsigned rowsize;
336 	int		 y, p, planes;
337 	int		 formtype, formlength;
338 	int		 chunktype, chunklength;
339 
340 	void (*mungecall) (int, byte *);
341 
342 // quiet compiler warnings
343 	picbuffer = NULL;
344 	cmapbuffer = NULL;
345 	mungecall = NULL;
346 
347 // load the LBM
348 	LoadFile (filename, &LBMbuffer);
349 
350 // parse the LBM header
351 	LBM_P = LBMbuffer;
352 	if (*(uint32_t *) LBMbuffer != LittleLong (FORMID))
353 	   Sys_Error ("No FORM ID at start of file!\n");
354 
355 	LBM_P += 4;
356 	formlength = BigLong (*(uint32_t *) LBM_P );
357 	LBM_P += 4;
358 	LBMEND_P = LBM_P + Align (formlength);
359 
360 	formtype = LittleLong (*(uint32_t *) LBM_P);
361 
362 	if (formtype != ILBMID && formtype != PBMID)
363 		Sys_Error ("Unrecognized form type: %c%c%c%c\n", formtype & 0xff,
364 			   (formtype >> 8) & 0xff, (formtype >> 16) & 0xff,
365 			   (formtype >> 24) & 0xff);
366 
367 	LBM_P += 4;
368 
369 // parse chunks
370 	while (LBM_P < LBMEND_P) {
371 		chunktype = LBM_P[0] + (LBM_P[1] << 8) + (LBM_P[2] << 16)
372 			+ (LBM_P[3] << 24);
373 		LBM_P += 4;
374 		chunklength = LBM_P[3] + (LBM_P[2] << 8) + (LBM_P[1] << 16)
375 			+ (LBM_P[0] << 24);
376 		LBM_P += 4;
377 
378 		switch (chunktype) {
379 		case BMHDID:
380 			memcpy (&bmhd, LBM_P, sizeof (bmhd));
381 			bmhd.w = BigShort (bmhd.w);
382 			bmhd.h = BigShort (bmhd.h);
383 			bmhd.x = BigShort (bmhd.x);
384 			bmhd.y = BigShort (bmhd.y);
385 			bmhd.pageWidth = BigShort (bmhd.pageWidth);
386 			bmhd.pageHeight = BigShort (bmhd.pageHeight);
387 			break;
388 
389 		case CMAPID:
390 			cmapbuffer = malloc (768);
391 			memset (cmapbuffer, 0, 768);
392 			memcpy (cmapbuffer, LBM_P, chunklength);
393 			break;
394 
395 		case BODYID:
396 			body_p = LBM_P;
397 
398 			pic_p = picbuffer = malloc (bmhd.w * bmhd.h);
399 			if (formtype == PBMID) {
400 			// unpack PBM
401 				for (y = 0; y < bmhd.h; y++, pic_p += bmhd.w) {
402 					if (bmhd.compression == cm_rle1)
403 						body_p = LBMRLEDecompress ((byte *) body_p , pic_p,
404 												   bmhd.w);
405 					else if (bmhd.compression == cm_none) {
406 						memcpy (pic_p,body_p,bmhd.w);
407 						body_p += Align(bmhd.w);
408 					}
409 				}
410 			} else {
411 			// unpack ILBM
412 				planes = bmhd.nPlanes;
413 				if (bmhd.masking == ms_mask)
414 					planes++;
415 				rowsize = (bmhd.w + 15) / 16 * 2;
416 				switch (bmhd.nPlanes) {
417 				case 1:
418 					mungecall = MungeBitPlanes1;
419 					break;
420 				case 2:
421 					mungecall = MungeBitPlanes2;
422 					break;
423 				case 4:
424 					mungecall = MungeBitPlanes4;
425 					break;
426 				case 8:
427 					mungecall = MungeBitPlanes8;
428 					break;
429 				default:
430 					Sys_Error ("Can't munge %i bit planes!\n",bmhd.nPlanes);
431 				}
432 
433 				for (y = 0; y < bmhd.h; y++, pic_p += bmhd.w) {
434 					for (p = 0; p < planes; p++)
435 						if (bmhd.compression == cm_rle1)
436 							body_p = LBMRLEDecompress ((byte *) body_p,
437 													   bitplanes[p], rowsize);
438 						else if (bmhd.compression == cm_none) {
439 							memcpy (bitplanes[p], body_p, rowsize);
440 							body_p += rowsize;
441 						}
442 
443 					mungecall (bmhd.w , pic_p);
444 				}
445 			}
446 			break;
447 		}
448 
449 		LBM_P += Align (chunklength);
450 	}
451 
452 	free (LBMbuffer);
453 
454 	*picture = picbuffer;
455 	*palette = cmapbuffer;
456 }
457 
458 /*
459 ============================================================================
460 
461 							WRITE LBM
462 
463 ============================================================================
464 */
465 
466 void
WriteLBMfile(char * filename,byte * data,int width,int height,byte * palette)467 WriteLBMfile (char *filename, byte *data, int width, int height, byte *palette)
468 {
469 	byte    *lbm, *lbmptr;
470 	uint32_t    *formlength, *bmhdlength, *cmaplength, *bodylength;
471 	int    length;
472 	bmhd_t  basebmhd;
473 
474 	lbm = lbmptr = malloc (width*height+1000);
475 
476 // start FORM
477 	*lbmptr++ = 'F';
478 	*lbmptr++ = 'O';
479 	*lbmptr++ = 'R';
480 	*lbmptr++ = 'M';
481 
482 	formlength = (uint32_t*)lbmptr;
483 	lbmptr+=4;                      // leave space for length
484 
485 	*lbmptr++ = 'P';
486 	*lbmptr++ = 'B';
487 	*lbmptr++ = 'M';
488 	*lbmptr++ = ' ';
489 
490 // write BMHD
491 	*lbmptr++ = 'B';
492 	*lbmptr++ = 'M';
493 	*lbmptr++ = 'H';
494 	*lbmptr++ = 'D';
495 
496 	bmhdlength = (uint32_t *) lbmptr;
497 	lbmptr += 4;						// leave space for length
498 
499 	memset (&basebmhd, 0, sizeof (basebmhd));
500 	basebmhd.w = BigShort ((short) width);
501 	basebmhd.h = BigShort ((short) height);
502 	basebmhd.nPlanes = 8;
503 	basebmhd.xAspect = 5;
504 	basebmhd.yAspect = 6;
505 	basebmhd.pageWidth = BigShort ((short) width);
506 	basebmhd.pageHeight = BigShort ((short) height);
507 
508 	memcpy (lbmptr, &basebmhd, sizeof (basebmhd));
509 	lbmptr += sizeof (basebmhd);
510 
511 	length = lbmptr - (byte *) bmhdlength - 4;
512 	*bmhdlength = BigLong (length);
513 	if (length & 1)
514 		*lbmptr++ = 0;						// pad chunk to even offset
515 
516 // write CMAP
517 	*lbmptr++ = 'C';
518 	*lbmptr++ = 'M';
519 	*lbmptr++ = 'A';
520 	*lbmptr++ = 'P';
521 
522 	cmaplength = (uint32_t *) lbmptr;
523 	lbmptr += 4;							// leave space for length
524 
525 	memcpy (lbmptr, palette, 768);
526 	lbmptr += 768;
527 
528 	length = lbmptr - (byte *) cmaplength - 4;
529 	*cmaplength = BigLong (length);
530 	if (length & 1)
531 		*lbmptr++ = 0;						// pad chunk to even offset
532 
533 // write BODY
534 	*lbmptr++ = 'B';
535 	*lbmptr++ = 'O';
536 	*lbmptr++ = 'D';
537 	*lbmptr++ = 'Y';
538 
539 	bodylength = (uint32_t *) lbmptr;
540 	lbmptr += 4;							// leave space for length
541 
542 	memcpy (lbmptr, data, width * height);
543 	lbmptr += width * height;
544 
545 	length = lbmptr - (byte *) bodylength - 4;
546 	*bodylength = BigLong(length);
547 	if (length & 1)
548 		*lbmptr++ = 0;          // pad chunk to even offset
549 
550 // done
551 	length = lbmptr - (byte *) formlength - 4;
552 	*formlength = BigLong (length);
553 	if (length & 1)
554 		*lbmptr++ = 0;          // pad chunk to even offset
555 
556 // write output file
557 	SaveFile (filename, lbm, lbmptr - lbm);
558 	free (lbm);
559 }
560 
561