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