Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * Portions of this file are copyright Rebirth contributors and licensed as |
||
3 | * described in COPYING.txt. |
||
4 | * Portions of this file are copyright Parallax Software and licensed |
||
5 | * according to the Parallax license below. |
||
6 | * See COPYING.txt for license details. |
||
7 | |||
8 | THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX |
||
9 | SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO |
||
10 | END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A |
||
11 | ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS |
||
12 | IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS |
||
13 | SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE |
||
14 | FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE |
||
15 | CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS |
||
16 | AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. |
||
17 | COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. |
||
18 | */ |
||
19 | |||
20 | /* |
||
21 | * |
||
22 | * Routines for reading and writing IFF files |
||
23 | * |
||
24 | */ |
||
25 | |||
26 | #define COMPRESS 1 //do the RLE or not? (for debugging mostly) |
||
27 | #define WRITE_TINY 0 //should we write a TINY chunk? |
||
28 | |||
29 | #define MIN_COMPRESS_WIDTH 65 //don't compress if less than this wide |
||
30 | |||
31 | #include <stdio.h> |
||
32 | #include <stdlib.h> |
||
33 | #include <string.h> |
||
34 | |||
35 | #include "u_mem.h" |
||
36 | #include "iff.h" |
||
37 | #include "dxxerror.h" |
||
38 | #include "makesig.h" |
||
39 | #include "physfsx.h" |
||
40 | #include "gr.h" |
||
41 | |||
42 | #include "dxxsconf.h" |
||
43 | #include "compiler-range_for.h" |
||
44 | #include "partial_range.h" |
||
45 | #include <memory> |
||
46 | |||
47 | //Internal constants and structures for this library |
||
48 | |||
49 | //Type values for bitmaps |
||
50 | #define TYPE_PBM 0 |
||
51 | #define TYPE_ILBM 1 |
||
52 | |||
53 | //Compression types |
||
54 | #define cmpNone 0 |
||
55 | #define cmpByteRun1 1 |
||
56 | |||
57 | //Masking types |
||
58 | #define mskNone 0 |
||
59 | #define mskHasMask 1 |
||
60 | #define mskHasTransparentColor 2 |
||
61 | |||
62 | //structure of the header in the file |
||
63 | struct iff_bitmap_header : prohibit_void_ptr<iff_bitmap_header> |
||
64 | { |
||
65 | short w,h; //width and height of this bitmap |
||
66 | short x,y; //generally unused |
||
67 | short type; //see types above |
||
68 | short transparentcolor; //which color is transparent (if any) |
||
69 | short pagewidth,pageheight; //width & height of source screen |
||
70 | sbyte nplanes; //number of planes (8 for 256 color image) |
||
71 | sbyte masking,compression; //see constants above |
||
72 | sbyte xaspect,yaspect; //aspect ratio (usually 5/6) |
||
73 | palette_array_t palette; //the palette for this bitmap |
||
74 | short row_size; //offset to next row |
||
75 | RAIIdmem<uint8_t[]> raw_data; //ptr to array of data |
||
76 | }; |
||
77 | |||
78 | ubyte iff_transparent_color; |
||
79 | ubyte iff_has_transparency; // 0=no transparency, 1=iff_transparent_color is valid |
||
80 | |||
81 | #define form_sig MAKE_SIG('F','O','R','M') |
||
82 | #define ilbm_sig MAKE_SIG('I','L','B','M') |
||
83 | #define body_sig MAKE_SIG('B','O','D','Y') |
||
84 | #define pbm_sig MAKE_SIG('P','B','M',' ') |
||
85 | #define bmhd_sig MAKE_SIG('B','M','H','D') |
||
86 | #define anhd_sig MAKE_SIG('A','N','H','D') |
||
87 | #define cmap_sig MAKE_SIG('C','M','A','P') |
||
88 | #define tiny_sig MAKE_SIG('T','I','N','Y') |
||
89 | #define anim_sig MAKE_SIG('A','N','I','M') |
||
90 | #define dlta_sig MAKE_SIG('D','L','T','A') |
||
91 | |||
92 | static int32_t get_sig(PHYSFS_File *f) |
||
93 | { |
||
94 | int s; |
||
95 | |||
96 | PHYSFS_readSBE32(f, &s); |
||
97 | return s; |
||
98 | } |
||
99 | |||
100 | #define put_sig(sig, f) PHYSFS_writeSBE32(f, sig) |
||
101 | |||
102 | static int parse_bmhd(PHYSFS_File *ifile,long len,iff_bitmap_header *bmheader) |
||
103 | { |
||
104 | len++; /* so no "parm not used" warning */ |
||
105 | |||
106 | // debug("parsing bmhd len=%ld\n",len); |
||
107 | |||
108 | PHYSFS_readSBE16(ifile, &bmheader->w); |
||
109 | PHYSFS_readSBE16(ifile, &bmheader->h); |
||
110 | PHYSFS_readSBE16(ifile, &bmheader->x); |
||
111 | PHYSFS_readSBE16(ifile, &bmheader->y); |
||
112 | |||
113 | bmheader->nplanes = PHYSFSX_readByte(ifile); |
||
114 | bmheader->masking = PHYSFSX_readByte(ifile); |
||
115 | bmheader->compression = PHYSFSX_readByte(ifile); |
||
116 | PHYSFSX_readByte(ifile); /* skip pad */ |
||
117 | |||
118 | PHYSFS_readSBE16(ifile, &bmheader->transparentcolor); |
||
119 | bmheader->xaspect = PHYSFSX_readByte(ifile); |
||
120 | bmheader->yaspect = PHYSFSX_readByte(ifile); |
||
121 | |||
122 | PHYSFS_readSBE16(ifile, &bmheader->pagewidth); |
||
123 | PHYSFS_readSBE16(ifile, &bmheader->pageheight); |
||
124 | |||
125 | iff_transparent_color = bmheader->transparentcolor; |
||
126 | |||
127 | iff_has_transparency = 0; |
||
128 | |||
129 | if (bmheader->masking == mskHasTransparentColor) |
||
130 | iff_has_transparency = 1; |
||
131 | |||
132 | else if (bmheader->masking != mskNone && bmheader->masking != mskHasMask) |
||
133 | return IFF_UNKNOWN_MASK; |
||
134 | |||
135 | // debug("w,h=%d,%d x,y=%d,%d\n",w,h,x,y); |
||
136 | // debug("nplanes=%d, masking=%d ,compression=%d, transcolor=%d\n",nplanes,masking,compression,transparentcolor); |
||
137 | |||
138 | return IFF_NO_ERROR; |
||
139 | } |
||
140 | |||
141 | |||
142 | // the buffer pointed to by raw_data is stuffed with a pointer to decompressed pixel data |
||
143 | namespace dsx { |
||
144 | static int parse_body(PHYSFS_File *ifile,long len,iff_bitmap_header *bmheader) |
||
145 | { |
||
146 | auto p = bmheader->raw_data.get(); |
||
147 | int width,depth; |
||
148 | signed char n; |
||
149 | int nn,wid_cnt,end_cnt,plane; |
||
150 | unsigned char *data_end; |
||
151 | int end_pos; |
||
152 | #ifndef NDEBUG |
||
153 | int row_count=0; |
||
154 | #endif |
||
155 | |||
156 | width=0; |
||
157 | depth=0; |
||
158 | |||
159 | end_pos = PHYSFS_tell(ifile) + len; |
||
160 | if (len&1) |
||
161 | end_pos++; |
||
162 | |||
163 | if (bmheader->type == TYPE_PBM) { |
||
164 | width=bmheader->w; |
||
165 | depth=1; |
||
166 | } else if (bmheader->type == TYPE_ILBM) { |
||
167 | width = (bmheader->w+7)/8; |
||
168 | depth=bmheader->nplanes; |
||
169 | } |
||
170 | |||
171 | end_cnt = (width&1)?-1:0; |
||
172 | |||
173 | data_end = p + width*bmheader->h*depth; |
||
174 | |||
175 | if (bmheader->compression == cmpNone) { /* no compression */ |
||
176 | int y; |
||
177 | |||
178 | for (y=bmheader->h;y;y--) { |
||
179 | PHYSFS_read(ifile, p, width, depth); |
||
180 | p += bmheader->w; |
||
181 | |||
182 | if (bmheader->masking == mskHasMask) |
||
183 | PHYSFSX_fseek(ifile, width, SEEK_CUR); //skip mask! |
||
184 | |||
185 | if (bmheader->w & 1) PHYSFSX_fgetc(ifile); |
||
186 | } |
||
187 | |||
188 | //cnt = len - bmheader->h * ((bmheader->w+1)&~1); |
||
189 | |||
190 | } |
||
191 | else if (bmheader->compression == cmpByteRun1) |
||
192 | for (wid_cnt=width,plane=0; PHYSFS_tell(ifile) < end_pos && p<data_end;) { |
||
193 | unsigned char c; |
||
194 | |||
195 | if (wid_cnt == end_cnt) { |
||
196 | wid_cnt = width; |
||
197 | plane++; |
||
198 | if ((bmheader->masking == mskHasMask && plane==depth+1) || |
||
199 | (bmheader->masking != mskHasMask && plane==depth)) |
||
200 | plane=0; |
||
201 | } |
||
202 | |||
203 | Assert(wid_cnt > end_cnt); |
||
204 | |||
205 | n=PHYSFSX_fgetc(ifile); |
||
206 | |||
207 | if (n >= 0) { // copy next n+1 bytes from source, they are not compressed |
||
208 | nn = static_cast<int>(n)+1; |
||
209 | wid_cnt -= nn; |
||
210 | if (wid_cnt==-1) {--nn; Assert(width&1);} |
||
211 | if (plane==depth) //masking row |
||
212 | PHYSFSX_fseek(ifile, nn, SEEK_CUR); |
||
213 | else |
||
214 | { |
||
215 | PHYSFS_read(ifile, p, nn, 1); |
||
216 | p += nn; |
||
217 | } |
||
218 | if (wid_cnt==-1) PHYSFSX_fseek(ifile, 1, SEEK_CUR); |
||
219 | } |
||
220 | else if (n>=-127) { // next -n + 1 bytes are following byte |
||
221 | c=PHYSFSX_fgetc(ifile); |
||
222 | const int negative_n = -n; |
||
223 | nn = negative_n + 1; |
||
224 | wid_cnt -= nn; |
||
225 | if (wid_cnt==-1) {--nn; Assert(width&1);} |
||
226 | if (plane!=depth) //not masking row |
||
227 | {memset(p,c,nn); p+=nn;} |
||
228 | } |
||
229 | |||
230 | #ifndef NDEBUG |
||
231 | if ((p - bmheader->raw_data.get()) % width == 0) |
||
232 | row_count++; |
||
233 | |||
234 | Assert((p - bmheader->raw_data.get()) - (width*row_count) < width); |
||
235 | #endif |
||
236 | |||
237 | } |
||
238 | |||
239 | #if defined(DXX_BUILD_DESCENT_I) |
||
240 | if (bmheader->masking==mskHasMask && p==data_end && PHYSFS_tell(ifile)==end_pos-2) //I don't know why... |
||
241 | PHYSFSX_fseek(ifile, 1, SEEK_CUR); //...but if I do this it works |
||
242 | |||
243 | if (p==data_end && PHYSFS_tell(ifile)==end_pos-1) //must be a pad byte |
||
244 | //ignore = PHYSFSX_fgetc(ifile); //get pad byte |
||
245 | PHYSFSX_fseek(ifile, 1, SEEK_CUR); |
||
246 | else |
||
247 | if (PHYSFS_tell(ifile)!=end_pos || p!=data_end) { |
||
248 | // debug("IFF Error: p=%x, data_end=%x, cnt=%d\n",p,data_end,cnt); |
||
249 | return IFF_CORRUPT; |
||
250 | } |
||
251 | #elif defined(DXX_BUILD_DESCENT_II) |
||
252 | if (p!=data_end) //if we don't have the whole bitmap... |
||
253 | return IFF_CORRUPT; //...the give an error |
||
254 | |||
255 | //Pretend we read the whole chuck, because if we didn't, it's because |
||
256 | //we didn't read the last mask like or the last rle record for padding |
||
257 | //or whatever and it's not important, because we check to make sure |
||
258 | //we got the while bitmap, and that's what really counts. |
||
259 | #endif |
||
260 | |||
261 | return IFF_NO_ERROR; |
||
262 | } |
||
263 | } |
||
264 | |||
265 | //modify passed bitmap |
||
266 | static int parse_delta(PHYSFS_File *ifile,long len,iff_bitmap_header *bmheader) |
||
267 | { |
||
268 | auto p = bmheader->raw_data.get(); |
||
269 | long chunk_end = PHYSFS_tell(ifile) + len; |
||
270 | |||
271 | PHYSFSX_fseek(ifile, 4, SEEK_CUR); //longword, seems to be equal to 4. Don't know what it is |
||
272 | |||
273 | for (int y=0;y<bmheader->h;y++) { |
||
274 | ubyte n_items; |
||
275 | int cnt = bmheader->w; |
||
276 | ubyte code; |
||
277 | |||
278 | n_items = PHYSFSX_readByte(ifile); |
||
279 | |||
280 | while (n_items--) { |
||
281 | |||
282 | code = PHYSFSX_readByte(ifile); |
||
283 | |||
284 | if (code==0) { //repeat |
||
285 | ubyte rep,val; |
||
286 | |||
287 | rep = PHYSFSX_readByte(ifile); |
||
288 | val = PHYSFSX_readByte(ifile); |
||
289 | |||
290 | cnt -= rep; |
||
291 | if (cnt==-1) |
||
292 | rep--; |
||
293 | while (rep--) |
||
294 | *p++ = val; |
||
295 | } |
||
296 | else if (code > 0x80) { //skip |
||
297 | cnt -= (code-0x80); |
||
298 | p += (code-0x80); |
||
299 | if (cnt==-1) |
||
300 | p--; |
||
301 | } |
||
302 | else { //literal |
||
303 | cnt -= code; |
||
304 | if (cnt==-1) |
||
305 | code--; |
||
306 | |||
307 | while (code--) |
||
308 | *p++ = PHYSFSX_readByte(ifile); |
||
309 | |||
310 | if (cnt==-1) |
||
311 | PHYSFSX_readByte(ifile); |
||
312 | } |
||
313 | |||
314 | } |
||
315 | |||
316 | if (cnt == -1) { |
||
317 | if (!bmheader->w) |
||
318 | return IFF_CORRUPT; |
||
319 | } |
||
320 | else if (cnt) |
||
321 | return IFF_CORRUPT; |
||
322 | } |
||
323 | |||
324 | if (PHYSFS_tell(ifile) == chunk_end-1) //pad |
||
325 | PHYSFSX_fseek(ifile, 1, SEEK_CUR); |
||
326 | |||
327 | if (PHYSFS_tell(ifile) != chunk_end) |
||
328 | return IFF_CORRUPT; |
||
329 | else |
||
330 | return IFF_NO_ERROR; |
||
331 | } |
||
332 | |||
333 | // the buffer pointed to by raw_data is stuffed with a pointer to bitplane pixel data |
||
334 | static void skip_chunk(PHYSFS_File *ifile,long len) |
||
335 | { |
||
336 | int ilen; |
||
337 | ilen = (len+1) & ~1; |
||
338 | |||
339 | PHYSFSX_fseek(ifile,ilen,SEEK_CUR); |
||
340 | } |
||
341 | |||
342 | //read an ILBM or PBM file |
||
343 | // Pass pointer to opened file, and to empty bitmap_header structure, and form length |
||
344 | static int iff_parse_ilbm_pbm(PHYSFS_File *ifile,long form_type,iff_bitmap_header *bmheader,int form_len,grs_bitmap *prev_bm) |
||
345 | { |
||
346 | int sig,len; |
||
347 | long start_pos,end_pos; |
||
348 | |||
349 | start_pos = PHYSFS_tell(ifile); |
||
350 | end_pos = start_pos-4+form_len; |
||
351 | |||
352 | if (form_type == pbm_sig) |
||
353 | bmheader->type = TYPE_PBM; |
||
354 | else |
||
355 | bmheader->type = TYPE_ILBM; |
||
356 | |||
357 | while ((PHYSFS_tell(ifile) < end_pos) && (sig=get_sig(ifile)) != EOF) { |
||
358 | |||
359 | PHYSFS_readSBE32(ifile, &len); |
||
360 | |||
361 | switch (sig) { |
||
362 | |||
363 | case bmhd_sig: { |
||
364 | int ret; |
||
365 | int save_w=bmheader->w,save_h=bmheader->h; |
||
366 | |||
367 | ret = parse_bmhd(ifile,len,bmheader); |
||
368 | |||
369 | if (ret != IFF_NO_ERROR) |
||
370 | return ret; |
||
371 | |||
372 | if (bmheader->raw_data) { |
||
373 | |||
374 | if (save_w != bmheader->w || save_h != bmheader->h) |
||
375 | return IFF_BM_MISMATCH; |
||
376 | |||
377 | } |
||
378 | else { |
||
379 | MALLOC(bmheader->raw_data, uint8_t[], bmheader->w * bmheader->h); |
||
380 | if (!bmheader->raw_data) |
||
381 | return IFF_NO_MEM; |
||
382 | } |
||
383 | |||
384 | break; |
||
385 | |||
386 | } |
||
387 | |||
388 | case anhd_sig: |
||
389 | |||
390 | if (!prev_bm) return IFF_CORRUPT; |
||
391 | |||
392 | bmheader->w = prev_bm->bm_w; |
||
393 | bmheader->h = prev_bm->bm_h; |
||
394 | bmheader->type = prev_bm->get_type(); |
||
395 | |||
396 | MALLOC(bmheader->raw_data, uint8_t[], bmheader->w * bmheader->h); |
||
397 | |||
398 | memcpy(bmheader->raw_data.get(), prev_bm->bm_data, bmheader->w * bmheader->h); |
||
399 | skip_chunk(ifile,len); |
||
400 | |||
401 | break; |
||
402 | |||
403 | case cmap_sig: |
||
404 | { |
||
405 | unsigned ncolors=(len/3); |
||
406 | |||
407 | range_for (auto &p, partial_range(bmheader->palette, ncolors)) |
||
408 | { |
||
409 | p.r = static_cast<unsigned char>(PHYSFSX_fgetc(ifile)) >> 2; |
||
410 | p.g = static_cast<unsigned char>(PHYSFSX_fgetc(ifile)) >> 2; |
||
411 | p.b = static_cast<unsigned char>(PHYSFSX_fgetc(ifile)) >> 2; |
||
412 | } |
||
413 | if (len & 1) PHYSFSX_fgetc(ifile); |
||
414 | |||
415 | break; |
||
416 | } |
||
417 | |||
418 | case body_sig: |
||
419 | { |
||
420 | int r; |
||
421 | if ((r=parse_body(ifile,len,bmheader))!=IFF_NO_ERROR) |
||
422 | return r; |
||
423 | break; |
||
424 | } |
||
425 | case dlta_sig: |
||
426 | { |
||
427 | int r; |
||
428 | if ((r=parse_delta(ifile,len,bmheader))!=IFF_NO_ERROR) |
||
429 | return r; |
||
430 | break; |
||
431 | } |
||
432 | default: |
||
433 | skip_chunk(ifile,len); |
||
434 | break; |
||
435 | } |
||
436 | } |
||
437 | |||
438 | if (PHYSFS_tell(ifile) != start_pos-4+form_len) |
||
439 | return IFF_CORRUPT; |
||
440 | |||
441 | return IFF_NO_ERROR; /* ok! */ |
||
442 | } |
||
443 | |||
444 | //convert an ILBM file to a PBM file |
||
445 | static int convert_ilbm_to_pbm(iff_bitmap_header *bmheader) |
||
446 | { |
||
447 | int x,p; |
||
448 | int bytes_per_row,byteofs; |
||
449 | ubyte checkmask,newbyte,setbit; |
||
450 | |||
451 | RAIIdmem<uint8_t[]> new_data; |
||
452 | MALLOC(new_data, uint8_t[], bmheader->w * bmheader->h); |
||
453 | if (new_data == NULL) return IFF_NO_MEM; |
||
454 | |||
455 | auto destptr = new_data.get(); |
||
456 | |||
457 | bytes_per_row = 2*((bmheader->w+15)/16); |
||
458 | |||
459 | for (int y=0;y<bmheader->h;y++) { |
||
460 | |||
461 | const auto rowptr = reinterpret_cast<int8_t *>(&bmheader->raw_data[y * bytes_per_row * bmheader->nplanes]); |
||
462 | |||
463 | for (x=0,checkmask=0x80;x<bmheader->w;x++) { |
||
464 | |||
465 | byteofs = x >> 3; |
||
466 | |||
467 | for (p=newbyte=0,setbit=1;p<bmheader->nplanes;p++) { |
||
468 | |||
469 | if (rowptr[bytes_per_row * p + byteofs] & checkmask) |
||
470 | newbyte |= setbit; |
||
471 | |||
472 | setbit <<= 1; |
||
473 | } |
||
474 | |||
475 | *destptr++ = newbyte; |
||
476 | |||
477 | if ((checkmask >>= 1) == 0) checkmask=0x80; |
||
478 | } |
||
479 | |||
480 | } |
||
481 | |||
482 | bmheader->raw_data = std::move(new_data); |
||
483 | |||
484 | bmheader->type = TYPE_PBM; |
||
485 | |||
486 | return IFF_NO_ERROR; |
||
487 | } |
||
488 | |||
489 | #define INDEX_TO_15BPP(i) (static_cast<short>((((palptr[(i)].r/2)&31)<<10)+(((palptr[(i)].g/2)&31)<<5)+((palptr[(i)].b/2 )&31))) |
||
490 | |||
491 | namespace dsx { |
||
492 | static int convert_rgb15(grs_bitmap &bm,iff_bitmap_header &bmheader) |
||
493 | { |
||
494 | palette_array_t::iterator palptr = begin(bmheader.palette); |
||
495 | |||
496 | #if defined(DXX_BUILD_DESCENT_I) |
||
497 | for (int y=0; y < bm.bm_h; y++) { |
||
498 | for (int x=0; x < bmheader.w; x++) |
||
499 | gr_bm_pixel(*grd_curcanv, bm, x, y, INDEX_TO_15BPP(bm.get_bitmap_data()[y * bmheader.w + x])); |
||
500 | } |
||
501 | #elif defined(DXX_BUILD_DESCENT_II) |
||
502 | uint16_t *new_data; |
||
503 | MALLOC(new_data, ushort, bm.bm_w * bm.bm_h * 2); |
||
504 | if (new_data == NULL) |
||
505 | return IFF_NO_MEM; |
||
506 | |||
507 | unsigned newptr = 0; |
||
508 | for (int y=0; y < bm.bm_h; y++) { |
||
509 | |||
510 | for (int x=0; x < bmheader.w; x++) |
||
511 | new_data[newptr++] = INDEX_TO_15BPP(bm.get_bitmap_data()[y * bmheader.w + x]); |
||
512 | |||
513 | } |
||
514 | |||
515 | d_free(bm.bm_mdata); //get rid of old-style data |
||
516 | bm.bm_mdata = reinterpret_cast<uint8_t *>(new_data); //..and point to new data |
||
517 | |||
518 | bm.bm_rowsize *= 2; //two bytes per row |
||
519 | #endif |
||
520 | return IFF_NO_ERROR; |
||
521 | |||
522 | } |
||
523 | } |
||
524 | |||
525 | //copy an iff header structure to a grs_bitmap structure |
||
526 | static void copy_iff_to_grs(grs_bitmap &bm,iff_bitmap_header &bmheader) |
||
527 | { |
||
528 | gr_init_bitmap(bm, static_cast<bm_mode>(bmheader.type), 0, 0, bmheader.w, bmheader.h, bmheader.w, bmheader.raw_data.release()); |
||
529 | } |
||
530 | |||
531 | //if bm->bm_data is set, use it (making sure w & h are correct), else |
||
532 | //allocate the memory |
||
533 | static int iff_parse_bitmap(PHYSFS_File *ifile, grs_bitmap &bm, int bitmap_type, palette_array_t *palette, grs_bitmap *prev_bm) |
||
534 | { |
||
535 | int ret; //return code |
||
536 | iff_bitmap_header bmheader; |
||
537 | int sig,form_len; |
||
538 | long form_type; |
||
539 | |||
540 | bmheader.raw_data.reset(bm.get_bitmap_data()); |
||
541 | |||
542 | if (bmheader.raw_data) { |
||
543 | bmheader.w = bm.bm_w; |
||
544 | bmheader.h = bm.bm_h; |
||
545 | }//added 05/17/99 Matt Mueller - don't just leave them unitialized |
||
546 | else{ |
||
547 | bmheader.w=bmheader.h=0; |
||
548 | } |
||
549 | |||
550 | sig=get_sig(ifile); |
||
551 | |||
552 | if (sig != form_sig) { |
||
553 | return IFF_NOT_IFF; |
||
554 | } |
||
555 | |||
556 | PHYSFS_readSBE32(ifile, &form_len); |
||
557 | |||
558 | form_type = get_sig(ifile); |
||
559 | |||
560 | if (form_type == anim_sig) |
||
561 | ret = IFF_FORM_ANIM; |
||
562 | else if ((form_type == pbm_sig) || (form_type == ilbm_sig)) |
||
563 | ret = iff_parse_ilbm_pbm(ifile,form_type,&bmheader,form_len,prev_bm); |
||
564 | else |
||
565 | ret = IFF_UNKNOWN_FORM; |
||
566 | |||
567 | if (ret != IFF_NO_ERROR) { //got an error parsing |
||
568 | return ret; |
||
569 | } |
||
570 | |||
571 | //If IFF file is ILBM, convert to PPB |
||
572 | if (bmheader.type == TYPE_ILBM) { |
||
573 | |||
574 | ret = convert_ilbm_to_pbm(&bmheader); |
||
575 | |||
576 | if (ret != IFF_NO_ERROR) |
||
577 | return ret; |
||
578 | } |
||
579 | |||
580 | //Copy data from iff_bitmap_header structure into grs_bitmap structure |
||
581 | |||
582 | copy_iff_to_grs(bm,bmheader); |
||
583 | |||
584 | if (palette) |
||
585 | *palette = bmheader.palette; |
||
586 | |||
587 | //Now do post-process if required |
||
588 | |||
589 | if (bitmap_type == bm_mode::rgb15) { |
||
590 | ret = convert_rgb15(bm, bmheader); |
||
591 | if (ret != IFF_NO_ERROR) |
||
592 | return ret; |
||
593 | } |
||
594 | |||
595 | return ret; |
||
596 | |||
597 | } |
||
598 | |||
599 | //returns error codes - see IFF.H. see GR.H for bitmap_type |
||
600 | int iff_read_bitmap(const char *const ifilename, grs_bitmap &bm, palette_array_t *const palette) |
||
601 | { |
||
602 | int ret; //return code |
||
603 | auto ifile = PHYSFSX_openReadBuffered(ifilename); |
||
604 | if (!ifile) |
||
605 | return IFF_NO_FILE; |
||
606 | |||
607 | bm.bm_data = nullptr; |
||
608 | ret = iff_parse_bitmap(ifile, bm, bm_mode::linear, palette, nullptr); |
||
609 | return ret; |
||
610 | } |
||
611 | |||
612 | #define BMHD_SIZE 20 |
||
613 | |||
614 | #if 0 |
||
615 | static int write_bmhd(PHYSFS_File *ofile,iff_bitmap_header *bitmap_header) |
||
616 | { |
||
617 | put_sig(bmhd_sig,ofile); |
||
618 | PHYSFS_writeSBE32(ofile, BMHD_SIZE); |
||
619 | |||
620 | PHYSFS_writeSBE16(ofile, bitmap_header->w); |
||
621 | PHYSFS_writeSBE16(ofile, bitmap_header->h); |
||
622 | PHYSFS_writeSBE16(ofile, bitmap_header->x); |
||
623 | PHYSFS_writeSBE16(ofile, bitmap_header->y); |
||
624 | |||
625 | PHYSFSX_writeU8(ofile, bitmap_header->nplanes); |
||
626 | PHYSFSX_writeU8(ofile, bitmap_header->masking); |
||
627 | PHYSFSX_writeU8(ofile, bitmap_header->compression); |
||
628 | PHYSFSX_writeU8(ofile, 0); /* pad */ |
||
629 | |||
630 | PHYSFS_writeSBE16(ofile, bitmap_header->transparentcolor); |
||
631 | PHYSFSX_writeU8(ofile, bitmap_header->xaspect); |
||
632 | PHYSFSX_writeU8(ofile, bitmap_header->yaspect); |
||
633 | |||
634 | PHYSFS_writeSBE16(ofile, bitmap_header->pagewidth); |
||
635 | PHYSFS_writeSBE16(ofile, bitmap_header->pageheight); |
||
636 | |||
637 | return IFF_NO_ERROR; |
||
638 | |||
639 | } |
||
640 | |||
641 | static int write_pal(PHYSFS_File *ofile,iff_bitmap_header *bitmap_header) |
||
642 | { |
||
643 | int n_colors = 1<<bitmap_header->nplanes; |
||
644 | |||
645 | put_sig(cmap_sig,ofile); |
||
646 | // PHYSFS_writeSBE32(sizeof(pal_entry) * n_colors,ofile); |
||
647 | PHYSFS_writeSBE32(ofile, 3 * n_colors); |
||
648 | |||
649 | range_for (auto &c, bitmap_header->palette) |
||
650 | { |
||
651 | unsigned char r,g,b; |
||
652 | r = c.r * 4 + (c.r?3:0); |
||
653 | g = c.g * 4 + (c.g?3:0); |
||
654 | b = c.b * 4 + (c.b?3:0); |
||
655 | PHYSFSX_writeU8(ofile, r); |
||
656 | PHYSFSX_writeU8(ofile, g); |
||
657 | PHYSFSX_writeU8(ofile, b); |
||
658 | } |
||
659 | |||
660 | return IFF_NO_ERROR; |
||
661 | } |
||
662 | |||
663 | static int rle_span(ubyte *dest,ubyte *src,int len) |
||
664 | { |
||
665 | int lit_cnt,rep_cnt; |
||
666 | ubyte last,*cnt_ptr,*dptr; |
||
667 | |||
668 | cnt_ptr=0; |
||
669 | |||
670 | dptr = dest; |
||
671 | |||
672 | last=src[0]; lit_cnt=1; |
||
673 | |||
674 | for (int n=1;n<len;n++) { |
||
675 | |||
676 | if (src[n] == last) { |
||
677 | |||
678 | rep_cnt = 2; |
||
679 | |||
680 | n++; |
||
681 | while (n<len && rep_cnt<128 && src[n]==last) {n++; rep_cnt++;} |
||
682 | |||
683 | if (rep_cnt > 2 || lit_cnt < 2) { |
||
684 | |||
685 | if (lit_cnt > 1) {*cnt_ptr = lit_cnt-2; --dptr;} //save old lit count |
||
686 | *dptr++ = -(rep_cnt-1); |
||
687 | *dptr++ = last; |
||
688 | last = src[n]; |
||
689 | lit_cnt = (n<len)?1:0; |
||
690 | |||
691 | continue; //go to next char |
||
692 | } else n--; |
||
693 | |||
694 | } |
||
695 | |||
696 | { |
||
697 | |||
698 | if (lit_cnt==1) { |
||
699 | cnt_ptr = dptr++; //save place for count |
||
700 | *dptr++=last; //store first char |
||
701 | } |
||
702 | |||
703 | *dptr++ = last = src[n]; |
||
704 | |||
705 | if (lit_cnt == 127) { |
||
706 | *cnt_ptr = lit_cnt; |
||
707 | //cnt_ptr = dptr++; |
||
708 | lit_cnt = 1; |
||
709 | last = src[++n]; |
||
710 | } |
||
711 | else lit_cnt++; |
||
712 | } |
||
713 | |||
714 | |||
715 | } |
||
716 | |||
717 | if (lit_cnt==1) { |
||
718 | *dptr++ = 0; |
||
719 | *dptr++=last; //store first char |
||
720 | } |
||
721 | else if (lit_cnt > 1) |
||
722 | *cnt_ptr = lit_cnt-1; |
||
723 | |||
724 | return dptr-dest; |
||
725 | } |
||
726 | |||
727 | #define EVEN(a) ((a+1)&0xfffffffel) |
||
728 | |||
729 | //returns length of chunk |
||
730 | static int write_body(PHYSFS_File *ofile,iff_bitmap_header *bitmap_header,int compression_on) |
||
731 | { |
||
732 | int w=bitmap_header->w,h=bitmap_header->h; |
||
733 | int y,odd=w&1; |
||
734 | long len = EVEN(w) * h,newlen,total_len=0; |
||
735 | uint8_t *p=bitmap_header->raw_data; |
||
736 | long save_pos; |
||
737 | |||
738 | put_sig(body_sig,ofile); |
||
739 | save_pos = PHYSFS_tell(ofile); |
||
740 | PHYSFS_writeSBE32(ofile, len); |
||
741 | |||
742 | RAIIdmem<uint8_t[]> new_span; |
||
743 | MALLOC( new_span, uint8_t[], bitmap_header->w + (bitmap_header->w/128+2)*2); |
||
744 | if (!new_span) return IFF_NO_MEM; |
||
745 | |||
746 | for (y=bitmap_header->h;y--;) { |
||
747 | |||
748 | if (compression_on) { |
||
749 | total_len += newlen = rle_span(new_span,p,bitmap_header->w+odd); |
||
750 | PHYSFS_write(ofile,new_span,newlen,1); |
||
751 | } |
||
752 | else |
||
753 | PHYSFS_write(ofile,p,bitmap_header->w+odd,1); |
||
754 | |||
755 | p+=bitmap_header->row_size; //bitmap_header->w; |
||
756 | } |
||
757 | |||
758 | if (compression_on) { //write actual data length |
||
759 | Assert(PHYSFSX_fseek(ofile,save_pos,SEEK_SET)==0); |
||
760 | (void)save_pos; |
||
761 | PHYSFS_writeSBE32(ofile, total_len); |
||
762 | Assert(PHYSFSX_fseek(ofile,total_len,SEEK_CUR)==0); |
||
763 | if (total_len&1) PHYSFSX_writeU8(ofile, 0); //pad to even |
||
764 | } |
||
765 | return ((compression_on) ? (EVEN(total_len)+8) : (len+8)); |
||
766 | |||
767 | } |
||
768 | |||
769 | #if WRITE_TINY |
||
770 | //write a small representation of a bitmap. returns size |
||
771 | int write_tiny(PHYSFS_File *ofile,iff_bitmap_header *bitmap_header,int compression_on) |
||
772 | { |
||
773 | int skip; |
||
774 | int new_w,new_h; |
||
775 | int len,total_len=0,newlen; |
||
776 | int x,xofs,odd; |
||
777 | uint8_t *p = bitmap_header->raw_data; |
||
778 | ubyte tspan[80],new_span[80*2]; |
||
779 | long save_pos; |
||
780 | |||
781 | skip = max((bitmap_header->w+79)/80,(bitmap_header->h+63)/64); |
||
782 | |||
783 | new_w = bitmap_header->w / skip; |
||
784 | new_h = bitmap_header->h / skip; |
||
785 | |||
786 | odd = new_w & 1; |
||
787 | |||
788 | len = new_w * new_h + 4; |
||
789 | |||
790 | put_sig(tiny_sig,ofile); |
||
791 | save_pos = PHYSFS_tell(ofile); |
||
792 | PHYSFS_writeSBE32(ofile, EVEN(len)); |
||
793 | |||
794 | PHYSFS_writeSBE16(ofile, new_w); |
||
795 | PHYSFS_writeSBE16(ofile, new_h); |
||
796 | |||
797 | for (int y=0;y<new_h;y++) { |
||
798 | for (x=xofs=0;x<new_w;x++,xofs+=skip) |
||
799 | tspan[x] = p[xofs]; |
||
800 | |||
801 | if (compression_on) { |
||
802 | total_len += newlen = rle_span(new_span,tspan,new_w+odd); |
||
803 | PHYSFS_write(ofile,new_span,newlen,1); |
||
804 | } |
||
805 | else |
||
806 | PHYSFS_write(ofile,p,new_w+odd,1); |
||
807 | |||
808 | p += skip * bitmap_header->row_size; //bitmap_header->w; |
||
809 | |||
810 | } |
||
811 | |||
812 | if (compression_on) { |
||
813 | Assert(PHYSFSX_fseek(ofile,save_pos,SEEK_SET)==0); |
||
814 | (void)save_pos; |
||
815 | PHYSFS_writeSBE32(ofile, 4+total_len); |
||
816 | Assert(PHYSFSX_fseek(ofile,4+total_len,SEEK_CUR)==0); |
||
817 | if (total_len&1) PHYSFSX_writeU8(ofile, 0); //pad to even |
||
818 | } |
||
819 | |||
820 | return ((compression_on) ? (EVEN(total_len)+8+4) : (len+8)); |
||
821 | } |
||
822 | #endif |
||
823 | |||
824 | static int write_pbm(PHYSFS_File *ofile,iff_bitmap_header *bitmap_header,int compression_on) /* writes a pbm iff file */ |
||
825 | { |
||
826 | int ret; |
||
827 | long raw_size = EVEN(bitmap_header->w) * bitmap_header->h; |
||
828 | long body_size,tiny_size,pbm_size = 4 + BMHD_SIZE + 8 + EVEN(raw_size) + sizeof(rgb_t)*(1<<bitmap_header->nplanes)+8; |
||
829 | long save_pos; |
||
830 | |||
831 | put_sig(form_sig,ofile); |
||
832 | save_pos = PHYSFS_tell(ofile); |
||
833 | PHYSFS_writeSBE32(ofile, pbm_size+8); |
||
834 | put_sig(pbm_sig,ofile); |
||
835 | |||
836 | ret = write_bmhd(ofile,bitmap_header); |
||
837 | if (ret != IFF_NO_ERROR) return ret; |
||
838 | |||
839 | ret = write_pal(ofile,bitmap_header); |
||
840 | if (ret != IFF_NO_ERROR) return ret; |
||
841 | |||
842 | #if WRITE_TINY |
||
843 | tiny_size = write_tiny(ofile,bitmap_header,compression_on); |
||
844 | #else |
||
845 | tiny_size = 0; |
||
846 | #endif |
||
847 | |||
848 | body_size = write_body(ofile,bitmap_header,compression_on); |
||
849 | |||
850 | pbm_size = 4 + BMHD_SIZE + body_size + tiny_size + sizeof(rgb_t)*(1<<bitmap_header->nplanes)+8; |
||
851 | |||
852 | Assert(PHYSFSX_fseek(ofile,save_pos,SEEK_SET)==0); |
||
853 | (void)save_pos; |
||
854 | PHYSFS_writeSBE32(ofile, pbm_size+8); |
||
855 | Assert(PHYSFSX_fseek(ofile,pbm_size+8,SEEK_CUR)==0); |
||
856 | |||
857 | return ret; |
||
858 | |||
859 | } |
||
860 | |||
861 | //writes an IFF file from a grs_bitmap structure. writes palette if not null |
||
862 | //returns error codes - see IFF.H. |
||
863 | int iff_write_bitmap(const char *ofilename,grs_bitmap *bm,palette_array_t *palette) |
||
864 | { |
||
865 | iff_bitmap_header bmheader; |
||
866 | int ret; |
||
867 | int compression_on; |
||
868 | |||
869 | if (bm->bm_type == bm_mode::rgb15) return IFF_BAD_BM_TYPE; |
||
870 | |||
871 | #if COMPRESS |
||
872 | compression_on = (bm->bm_w>=MIN_COMPRESS_WIDTH); |
||
873 | #else |
||
874 | compression_on = 0; |
||
875 | #endif |
||
876 | |||
877 | //fill in values in bmheader |
||
878 | |||
879 | bmheader.x = bmheader.y = 0; |
||
880 | bmheader.w = bm->bm_w; |
||
881 | bmheader.h = bm->bm_h; |
||
882 | bmheader.type = TYPE_PBM; |
||
883 | bmheader.transparentcolor = iff_transparent_color; |
||
884 | bmheader.pagewidth = bm->bm_w; //I don't think it matters what I write |
||
885 | bmheader.pageheight = bm->bm_h; |
||
886 | bmheader.nplanes = 8; |
||
887 | bmheader.masking = mskNone; |
||
888 | if (iff_has_transparency) { |
||
889 | bmheader.masking |= mskHasTransparentColor; |
||
890 | } |
||
891 | bmheader.compression = (compression_on?cmpByteRun1:cmpNone); |
||
892 | |||
893 | bmheader.xaspect = bmheader.yaspect = 1; //I don't think it matters what I write |
||
894 | bmheader.raw_data = bm->get_bitmap_data(); |
||
895 | bmheader.row_size = bm->bm_rowsize; |
||
896 | |||
897 | if (palette) |
||
898 | bmheader.palette = *palette; |
||
899 | |||
900 | //open file and write |
||
901 | |||
902 | RAIIPHYSFS_File ofile{PHYSFS_openWrite(ofilename)}; |
||
903 | if (!ofile) |
||
904 | return IFF_NO_FILE; |
||
905 | |||
906 | ret = write_pbm(ofile,&bmheader,compression_on); |
||
907 | return ret; |
||
908 | } |
||
909 | #endif |
||
910 | |||
911 | //read in many brushes. fills in array of pointers, and n_bitmaps. |
||
912 | //returns iff error codes |
||
913 | int iff_read_animbrush(const char *ifilename,std::array<std::unique_ptr<grs_main_bitmap>, MAX_BITMAPS_PER_BRUSH> &bm_list,unsigned *n_bitmaps,palette_array_t &palette) |
||
914 | { |
||
915 | int ret = IFF_NO_ERROR; //return code |
||
916 | int sig,form_len; |
||
917 | long form_type; |
||
918 | |||
919 | *n_bitmaps=0; |
||
920 | |||
921 | auto ifile = PHYSFSX_openReadBuffered(ifilename); |
||
922 | if (!ifile) |
||
923 | return IFF_NO_FILE; |
||
924 | |||
925 | sig=get_sig(ifile); |
||
926 | PHYSFS_readSBE32(ifile, &form_len); |
||
927 | |||
928 | if (sig != form_sig) { |
||
929 | ret = IFF_NOT_IFF; |
||
930 | goto done; |
||
931 | } |
||
932 | |||
933 | form_type = get_sig(ifile); |
||
934 | |||
935 | if ((form_type == pbm_sig) || (form_type == ilbm_sig)) |
||
936 | ret = IFF_FORM_BITMAP; |
||
937 | else if (form_type == anim_sig) { |
||
938 | int anim_end = PHYSFS_tell(ifile) + form_len - 4; |
||
939 | |||
940 | while (PHYSFS_tell(ifile) < anim_end && *n_bitmaps < bm_list.size()) { |
||
941 | |||
942 | grs_bitmap *prev_bm; |
||
943 | |||
944 | prev_bm = *n_bitmaps>0?bm_list[*n_bitmaps-1].get() : nullptr; |
||
945 | |||
946 | auto &n = bm_list[*n_bitmaps]; |
||
947 | n = std::make_unique<grs_main_bitmap>(); |
||
948 | |||
949 | ret = iff_parse_bitmap(ifile, *n.get(), form_type, *n_bitmaps > 0 ? nullptr : &palette, prev_bm); |
||
950 | |||
951 | if (ret != IFF_NO_ERROR) |
||
952 | goto done; |
||
953 | |||
954 | (*n_bitmaps)++; |
||
955 | } |
||
956 | |||
957 | if (PHYSFS_tell(ifile) < anim_end) //ran out of room |
||
958 | ret = IFF_TOO_MANY_BMS; |
||
959 | |||
960 | } |
||
961 | else |
||
962 | ret = IFF_UNKNOWN_FORM; |
||
963 | |||
964 | done: |
||
965 | return ret; |
||
966 | } |
||
967 | |||
968 | //text for error messges |
||
969 | constexpr char error_messages[] = { |
||
970 | "No error.\0" |
||
971 | "Not enough mem for loading or processing bitmap.\0" |
||
972 | "IFF file has unknown FORM type.\0" |
||
973 | "Not an IFF file.\0" |
||
974 | "Cannot open file.\0" |
||
975 | "Tried to save invalid type, like bm_mode::rgb15.\0" |
||
976 | "Bad data in file.\0" |
||
977 | "ANIM file cannot be loaded with normal bitmap loader.\0" |
||
978 | "Normal bitmap file cannot be loaded with anim loader.\0" |
||
979 | "Array not big enough on anim brush read.\0" |
||
980 | "Unknown mask type in bitmap header.\0" |
||
981 | "Error reading file.\0" |
||
982 | }; |
||
983 | |||
984 | |||
985 | //function to return pointer to error message |
||
986 | const char *iff_errormsg(int error_number) |
||
987 | { |
||
988 | const char *p = error_messages; |
||
989 | |||
990 | while (error_number--) { |
||
991 | |||
992 | if (!p) return NULL; |
||
993 | |||
994 | p += strlen(p)+1; |
||
995 | |||
996 | } |
||
997 | |||
998 | return p; |
||
999 | |||
1000 | } |
||
1001 | |||
1002 |