1 /* vim:expandtab:ts=2 sw=2:
2 */
3 /* Grafx2 - The Ultimate 256-color bitmap paint program
4
5 Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.
6
7 Grafx2 is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2
10 of the License.
11
12 Grafx2 is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Grafx2; if not, see <http://www.gnu.org/licenses/>
19 */
20
21 ///@file fileformats.c
22 /// Saving and loading different picture formats.
23
24 #include <string.h>
25 #ifndef __no_pnglib__
26 // just for png_sig_cmp()
27 #include <png.h>
28 #endif
29
30 #include <stdlib.h>
31 #if defined(_MSC_VER)
32 #include <stdio.h>
33 #if _MSC_VER < 1900
34 #define snprintf _snprintf
35 #endif
36 #endif
37
38 #if !defined(WIN32) && !defined(USE_SDL) && !defined(USE_SDL2)
39 #if defined(__macosx__)
40 #include <machine/endian.h>
41 #elif defined(__FreeBSD__)
42 #include <sys/endian.h>
43 #else
44 #include <endian.h>
45 #endif
46 #endif
47
48 #include "gfx2log.h"
49 #include "errors.h"
50 #include "global.h"
51 #include "loadsave.h"
52 #include "loadsavefuncs.h"
53 #include "struct.h"
54 #include "io.h"
55 #include "pages.h"
56 #include "windows.h" // Best_color()
57 #include "unicode.h"
58 #include "fileformats.h"
59 #include "oldies.h"
60 #include "bitcount.h"
61
62 #ifndef MIN
63 #define MIN(a,b) ((a)<(b)?(a):(b))
64 #endif
65
66 void Draw_IFF_line(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes);
67
68 //////////////////////////////////// IMG ////////////////////////////////////
69
70 // -- Tester si un fichier est au format IMG --------------------------------
Test_IMG(T_IO_Context * context,FILE * file)71 void Test_IMG(T_IO_Context * context, FILE * file)
72 {
73 T_IMG_Header IMG_header;
74 static const byte signature[6]={0x01,0x00,0x47,0x12,0x6D,0xB0};
75
76 (void)context;
77 File_error=1;
78
79 // Lecture et vérification de la signature
80 if (Read_bytes(file,IMG_header.Filler1,6)
81 && Read_word_le(file,&(IMG_header.Width))
82 && Read_word_le(file,&(IMG_header.Height))
83 && Read_bytes(file,IMG_header.Filler2,118)
84 && Read_bytes(file,IMG_header.Palette,sizeof(T_Palette))
85 )
86 {
87 if ( (!memcmp(IMG_header.Filler1,signature,6))
88 && IMG_header.Width && IMG_header.Height)
89 File_error=0;
90 }
91 }
92
93
94 // -- Lire un fichier au format IMG -----------------------------------------
Load_IMG(T_IO_Context * context)95 void Load_IMG(T_IO_Context * context)
96 {
97 byte * buffer;
98 FILE *file;
99 word x_pos,y_pos;
100 long file_size;
101 T_IMG_Header IMG_header;
102
103 File_error=0;
104
105 if ((file=Open_file_read(context)))
106 {
107 file_size=File_length_file(file);
108
109 if (Read_bytes(file,IMG_header.Filler1,6)
110 && Read_word_le(file,&(IMG_header.Width))
111 && Read_word_le(file,&(IMG_header.Height))
112 && Read_bytes(file,IMG_header.Filler2,118)
113 && Read_bytes(file,IMG_header.Palette,sizeof(T_Palette))
114 )
115 {
116
117 buffer=(byte *)malloc(IMG_header.Width);
118
119 Pre_load(context, IMG_header.Width,IMG_header.Height,file_size,FORMAT_IMG,PIXEL_SIMPLE,0);
120 if (File_error==0)
121 {
122 memcpy(context->Palette,IMG_header.Palette,sizeof(T_Palette));
123
124 for (y_pos=0;(y_pos<context->Height) && (!File_error);y_pos++)
125 {
126 if (Read_bytes(file,buffer,context->Width))
127 {
128 for (x_pos=0; x_pos<context->Width;x_pos++)
129 Set_pixel(context, x_pos,y_pos,buffer[x_pos]);
130 }
131 else
132 File_error=2;
133 }
134 }
135
136 free(buffer);
137 buffer = NULL;
138 }
139 else
140 File_error=1;
141
142 fclose(file);
143 }
144 else
145 File_error=1;
146 }
147
148 // -- Sauver un fichier au format IMG ---------------------------------------
Save_IMG(T_IO_Context * context)149 void Save_IMG(T_IO_Context * context)
150 {
151 FILE *file;
152 short x_pos,y_pos;
153 T_IMG_Header IMG_header;
154 byte signature[6]={0x01,0x00,0x47,0x12,0x6D,0xB0};
155
156 File_error=0;
157
158 // Ouverture du fichier
159 if ((file=Open_file_write(context)))
160 {
161 setvbuf(file, NULL, _IOFBF, 64*1024);
162
163 memcpy(IMG_header.Filler1,signature,6);
164
165 IMG_header.Width=context->Width;
166 IMG_header.Height=context->Height;
167
168 memset(IMG_header.Filler2,0,118);
169 IMG_header.Filler2[4]=0xFF;
170 IMG_header.Filler2[22]=64; // Lo(Longueur de la signature)
171 IMG_header.Filler2[23]=0; // Hi(Longueur de la signature)
172 memcpy(IMG_header.Filler2+23,"GRAFX2 by SunsetDesign (IMG format taken from PV (c)W.Wiedmann)",64);
173
174 memcpy(IMG_header.Palette,context->Palette,sizeof(T_Palette));
175
176 if (Write_bytes(file,IMG_header.Filler1,6)
177 && Write_word_le(file,IMG_header.Width)
178 && Write_word_le(file,IMG_header.Height)
179 && Write_bytes(file,IMG_header.Filler2,118)
180 && Write_bytes(file,IMG_header.Palette,sizeof(T_Palette))
181 )
182
183 {
184 for (y_pos=0; ((y_pos<context->Height) && (!File_error)); y_pos++)
185 for (x_pos=0; x_pos<context->Width; x_pos++)
186 Write_one_byte(file,Get_pixel(context, x_pos,y_pos));
187
188 fclose(file);
189
190 if (File_error)
191 Remove_file(context);
192 }
193 else // Error d'écriture (disque plein ou protégé)
194 {
195 fclose(file);
196 Remove_file(context);
197 File_error=1;
198 }
199 }
200 else
201 {
202 File_error=1;
203 }
204 }
205
206
207 /////////////////////////// .info (Amiga Icons) /////////////////////////////
208 typedef struct
209 { // offset
210 word Magic; // 0
211 word Version;
212 dword NextGadget;
213 word LeftEdge; // 8
214 word TopEdge;
215 word Width;
216 word Height;
217 word Flags; // 16
218 word Activation;
219 word GadgetType; // 20
220 dword GadgetRender; // 22
221 dword SelectRender;
222 dword GadgetText; // 30
223 dword MutualExclude;
224 dword SpecialInfo; // 38
225 word GadgetID;
226 dword UserData; // 44 icon revision : 0 = OS 1.x, 1 = OS 2.x/3.x
227 byte Type;
228 byte padding;
229 dword DefaultTool;
230 dword ToolTypes;
231 dword CurrentX;
232 dword CurrentY;
233 dword DrawerData;
234 dword ToolWindow;
235 dword StackSize;
236 } T_INFO_Header;
237
Read_INFO_Header(FILE * file,T_INFO_Header * header)238 static int Read_INFO_Header(FILE * file, T_INFO_Header * header)
239 {
240 return (Read_word_be(file, &header->Magic)
241 && Read_word_be(file, &header->Version)
242 && Read_dword_be(file, &header->NextGadget)
243 && Read_word_be(file, &header->LeftEdge)
244 && Read_word_be(file, &header->TopEdge)
245 && Read_word_be(file, &header->Width)
246 && Read_word_be(file, &header->Height)
247 && Read_word_be(file, &header->Flags)
248 && Read_word_be(file, &header->Activation)
249 && Read_word_be(file, &header->GadgetType)
250 && Read_dword_be(file, &header->GadgetRender)
251 && Read_dword_be(file, &header->SelectRender)
252 && Read_dword_be(file, &header->GadgetText)
253 && Read_dword_be(file, &header->MutualExclude)
254 && Read_dword_be(file, &header->SpecialInfo)
255 && Read_word_be(file, &header->GadgetID)
256 && Read_dword_be(file, &header->UserData)
257 && Read_byte(file, &header->Type)
258 && Read_byte(file, &header->padding)
259 && Read_dword_be(file, &header->DefaultTool)
260 && Read_dword_be(file, &header->ToolTypes)
261 && Read_dword_be(file, &header->CurrentX)
262 && Read_dword_be(file, &header->CurrentY)
263 && Read_dword_be(file, &header->DrawerData)
264 && Read_dword_be(file, &header->ToolWindow)
265 && Read_dword_be(file, &header->StackSize)
266 );
267 }
268
269 typedef struct
270 {
271 short LeftEdge;
272 short TopEdge;
273 word Width;
274 word Height;
275 word Depth;
276 dword ImageData;
277 byte PlanePick;
278 byte PlaneOnOff;
279 dword NextImage;
280 } T_INFO_ImageHeader;
281
Read_INFO_ImageHeader(FILE * file,T_INFO_ImageHeader * header)282 static int Read_INFO_ImageHeader(FILE * file, T_INFO_ImageHeader * header)
283 {
284 return (Read_word_be(file, (word *)&header->LeftEdge)
285 && Read_word_be(file, (word *)&header->TopEdge)
286 && Read_word_be(file, &header->Width)
287 && Read_word_be(file, &header->Height)
288 && Read_word_be(file, &header->Depth)
289 && Read_dword_be(file, &header->ImageData)
290 && Read_byte(file, &header->PlanePick)
291 && Read_byte(file, &header->PlaneOnOff)
292 && Read_dword_be(file, &header->NextImage)
293 );
294 }
295
Test_INFO(T_IO_Context * context,FILE * file)296 void Test_INFO(T_IO_Context * context, FILE * file)
297 {
298 T_INFO_Header header;
299
300 (void)context;
301 File_error=1;
302
303 if (Read_INFO_Header(file, &header))
304 {
305 if (header.Magic == 0xe310 && header.Version == 1)
306 File_error = 0;
307 }
308 }
309
Read_INFO_String(FILE * file)310 static char * Read_INFO_String(FILE * file)
311 {
312 dword size;
313 char * p;
314 if (!Read_dword_be(file, &size))
315 return NULL;
316 p = malloc(size);
317 if (p == NULL)
318 return NULL;
319 if (!Read_bytes(file, p, size))
320 {
321 free(p);
322 p = NULL;
323 }
324 return p;
325 }
326
Decode_NewIcons(const byte * p,int bits,unsigned int * len)327 static byte * Decode_NewIcons(const byte * p, int bits, unsigned int * len)
328 {
329 int alloc_size;
330 unsigned int i;
331 byte * buffer;
332 dword current_byte = 0;
333 int current_bits = 0;
334
335 alloc_size = 16*1024;
336 buffer = malloc(alloc_size);
337 if (buffer == NULL)
338 return NULL;
339 i = 0;
340 while (*p)
341 {
342 byte value = *p++;
343 if (0xd1 <= value)
344 {
345 value -= 0xd0; // RLE count
346 while (value-- > 0)
347 {
348 current_byte = (current_byte << 7);
349 current_bits += 7;
350 while (current_bits >= bits)
351 {
352 buffer[i++] = (current_byte >> (current_bits - bits)) & ((1 << bits) - 1);
353 current_bits -= bits;
354 }
355 }
356 continue;
357 }
358 if (0x20 <= value && value <= 0x6f)
359 value = value - 0x20;
360 else if (0xa1 <= value && value <= 0xd0)
361 value = value - 0x51;
362 else
363 {
364 GFX2_Log(GFX2_WARNING, "Decode_NewIcons(): Invalid value 0x%02x\n", value);
365 break;
366 }
367 current_byte = (current_byte << 7) | value;
368 current_bits += 7;
369 while (current_bits >= bits)
370 {
371 buffer[i++] = (current_byte >> (current_bits - bits)) & ((1 << bits) - 1);
372 current_bits -= bits;
373 }
374 }
375 //buffer[i++] = (current_byte << (bits - current_bits)) & ((1 << bits) - 1);
376 *len = i;
377 return buffer;
378 }
379
380
Load_INFO(T_IO_Context * context)381 void Load_INFO(T_IO_Context * context)
382 {
383 static const T_Components amigaOS1x_pal[] = {
384 // { 85, 170, 255 },
385 { 0, 85, 170 },
386 { 255, 255, 255 },
387 { 0, 0, 0 },
388 { 255, 136, 0 },
389 };
390 static const T_Components amigaOS2x_pal[] = {
391 { 149, 149, 149 },
392 { 0, 0, 0 },
393 { 255, 255, 255 },
394 { 59, 103, 162 },
395 { 123, 123, 123 }, // MagicWB extended colors
396 { 175, 175, 175 },
397 { 170, 144, 124 },
398 { 255, 169, 151 },
399 { 255, 0, 0 }, //
400 { 160, 0, 0 }, //
401 { 0, 246, 255 }, //
402 { 0, 0, 255 }, //
403 { 0, 160, 0 }, //
404 { 0, 255, 0 }, //
405 { 255, 255, 0 }, //
406 { 255, 160, 0 }, //
407 };
408 T_INFO_Header header;
409 T_INFO_ImageHeader imgheaders[2];
410 FILE *file;
411 long file_size;
412 word plane_line_size = 0;
413 word line_size = 0;
414 int plane;
415 short x_pos = 0, y_pos = 0;
416 int has_NewIcons = 0;
417 int img_count = 0; // 1 or 2
418 byte * buffers[2];
419
420 File_error = 0;
421
422 file = Open_file_read(context);
423 if (file == NULL)
424 {
425 File_error=1;
426 return;
427 }
428 file_size = File_length_file(file);
429
430 if (Read_INFO_Header(file, &header) && header.Magic == 0xe310)
431 {
432 if (header.GadgetRender == 0)
433 File_error = 1;
434 if (header.DrawerData) // Skip DrawerData
435 if (fseek(file, 56, SEEK_CUR) != 0)
436 File_error = 1;
437
438 // icons
439 for (img_count = 0; File_error == 0 && img_count < 2; img_count++)
440 {
441 buffers[img_count] = NULL;
442 if (img_count == 0 && header.GadgetRender == 0)
443 continue;
444 if (img_count == 1 && header.SelectRender == 0)
445 break;
446 if (!Read_INFO_ImageHeader(file, &imgheaders[img_count]))
447 File_error = 1;
448 else
449 {
450 plane_line_size = ((imgheaders[img_count].Width + 15) >> 4) * 2;
451 line_size = plane_line_size * imgheaders[img_count].Depth;
452 buffers[img_count] = malloc(line_size * imgheaders[img_count].Height);
453 for (plane = 0; plane < imgheaders[img_count].Depth; plane++)
454 {
455 for (y_pos = 0; y_pos < imgheaders[img_count].Height; y_pos++)
456 {
457 if (!Read_bytes(file, buffers[img_count] + y_pos * line_size + plane * plane_line_size, plane_line_size))
458 {
459 File_error = 2;
460 break;
461 }
462 }
463 }
464 }
465 }
466 if (File_error == 0 && header.DefaultTool)
467 {
468 char * DefaultTool = Read_INFO_String(file);
469 if (DefaultTool == NULL)
470 File_error = 2;
471 else
472 {
473 free(DefaultTool);
474 }
475 }
476 if (File_error == 0 && header.ToolTypes)
477 {
478 int i;
479 int current_img = -1;
480 int width = 0;
481 int height = 0;
482 int palette_continues = 0;
483 unsigned int color_count = 0;
484 unsigned int palette_color_count = 0; // used colors in global palette
485 int bpp = 0;
486 byte palette_conv[256]; // to translate sub icon specific palette to global palette
487
488 dword count;
489 char * ToolType;
490 if (Read_dword_be(file, &count))
491 {
492 int look_for_comments = 1;
493 for (i = 0; i < 256; i++)
494 palette_conv[i] = (byte)i;
495
496 while(count > 0)
497 {
498 ToolType = Read_INFO_String(file);
499 if (ToolType == NULL)
500 break;
501 else
502 {
503 // NewIcons
504 if (strlen(ToolType) > 4 && ToolType[0] == 'I' && ToolType[1] == 'M' && ToolType[3] == '=')
505 {
506 const byte * p;
507 int img_index = ToolType[2] - '0';
508 p = (byte *)ToolType + 4;
509 if (img_index != current_img)
510 {
511 // image info + palette
512 T_Components * palette;
513 unsigned int palette_len;
514
515 current_img = img_index;
516 if (current_img > 1 && (context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE))
517 break; // don't load 2nd image in preview mode
518 if (*p++ == 'B')
519 {
520 context->Transparent_color = 0;
521 context->Background_transparent = 1;
522 }
523 else
524 context->Background_transparent = 0;
525 width = *p++ - 0x21;
526 height = *p++ - 0x21;
527 color_count = *p++ - 0x21;
528 color_count = (color_count << 6) + *p++ - 0x21;
529 for (bpp = 1; color_count > (unsigned)(1 << bpp); bpp++) { }
530 palette = (T_Components *)Decode_NewIcons(p, 8, &palette_len);
531 if (palette)
532 {
533 if (img_index == 1)
534 { // Set palette
535 if (Config.Clear_palette)
536 memset(context->Palette,0,sizeof(T_Palette));
537 memcpy(context->Palette, palette, MIN(palette_len,sizeof(T_Palette)));
538 if (palette_len < color_count * 3)
539 {
540 palette_color_count = palette_len / 3;
541 palette_continues = 1;
542 }
543 else
544 {
545 palette_color_count = color_count;
546 palette_continues = 0;
547 }
548 }
549 else
550 {
551 // merge palette with the existing one
552 for (i = 0; (unsigned)i < MIN(color_count,palette_len/3); i++)
553 {
554 unsigned int j;
555 for (j = 0; j < palette_color_count; j++)
556 {
557 if (0 == memcmp(context->Palette + j, palette + i, sizeof(T_Components)))
558 break;
559 }
560 if (j == palette_color_count)
561 {
562 if (palette_color_count < 256)
563 { // Add color to palette
564 memcpy(context->Palette + palette_color_count, palette + i, sizeof(T_Components));
565 palette_color_count++;
566 }
567 else
568 GFX2_Log(GFX2_WARNING, "Too much colors in new icons\n");
569 }
570 palette_conv[i] = (byte)j;
571 }
572 if (palette_len < color_count * 3)
573 palette_continues = palette_len / 3;
574 else
575 palette_continues = 0;
576 }
577 free(palette);
578 }
579 if (img_index == 1)
580 {
581 Pre_load(context, width, height,file_size,FORMAT_INFO,PIXEL_SIMPLE, bpp);
582 Set_image_mode(context, IMAGE_MODE_ANIMATION);
583 has_NewIcons = 1;
584 }
585 else
586 Set_loading_layer(context, img_index - 1);
587 x_pos = 0; y_pos = 0;
588 }
589 else if (palette_continues)
590 {
591 T_Components * palette;
592 unsigned int palette_len;
593 palette = (T_Components *)Decode_NewIcons(p, 8, &palette_len);
594 if (palette)
595 {
596 if (img_index == 1)
597 {
598 memcpy(context->Palette + palette_color_count, palette, MIN(palette_len,sizeof(T_Palette) - 3*palette_color_count));
599 if (palette_color_count * 3 + palette_len < color_count * 3)
600 palette_color_count += palette_len / 3;
601 else
602 {
603 palette_color_count = color_count;
604 palette_continues = 0;
605 }
606 }
607 else
608 {
609 // merge palette with the existing one
610 for (i = 0; (unsigned)i < MIN(color_count-palette_continues,palette_len/3); i++)
611 {
612 unsigned int j;
613 for (j = 0; j < palette_color_count; j++)
614 {
615 if (0 == memcmp(context->Palette + j, palette + i, sizeof(T_Components)))
616 break;
617 }
618 if (j == palette_color_count)
619 {
620 if (palette_color_count < 256)
621 { // Add color to palette
622 memcpy(context->Palette + palette_color_count, palette + i, sizeof(T_Components));
623 palette_color_count++;
624 }
625 else
626 GFX2_Log(GFX2_WARNING, "Too much colors in new icons\n");
627 }
628 palette_conv[i+palette_continues] = (byte)j;
629 }
630 if (palette_continues * 3 + palette_len < color_count * 3)
631 palette_continues += palette_len / 3;
632 else
633 palette_continues = 0;
634 }
635 free(palette);
636 }
637 }
638 else
639 {
640 byte * pixels;
641 unsigned int pixel_count;
642
643 pixels = Decode_NewIcons(p, bpp, &pixel_count);
644 if (pixels)
645 {
646 for (i = 0; (unsigned)i < pixel_count; i++)
647 {
648 Set_pixel(context, x_pos++, y_pos, palette_conv[pixels[i]]);
649 if (x_pos >= width)
650 {
651 x_pos = 0;
652 y_pos++;
653 }
654 }
655 free(pixels);
656 }
657 }
658 }
659 else if (look_for_comments)
660 {
661 if (strlen(ToolType) > 1)
662 {
663 if (strcmp(ToolType, "*** DON'T EDIT THE FOLLOWING LINES!! ***") == 0)
664 {
665 // The following lines are NewIcons data!
666 look_for_comments = 0;
667 }
668 else if (context->Comment[0] == '\0')
669 {
670 strncpy(context->Comment, ToolType, COMMENT_SIZE);
671 context->Comment[COMMENT_SIZE] = '\0';
672 }
673 }
674 }
675 free(ToolType);
676 }
677 count -= 4;
678 }
679 }
680 }
681 if (File_error == 0 && header.ToolWindow != 0)
682 {
683 char * ToolWindow = Read_INFO_String(file);
684 if (ToolWindow == NULL)
685 File_error = 2;
686 else
687 {
688 free(ToolWindow);
689 }
690 }
691 if (File_error == 0 && header.UserData == 1)
692 {
693 // Skip extended Drawer Data for OS 2.x
694 if (fseek(file, 8, SEEK_CUR) != 0)
695 File_error = 1;
696 }
697 // OS 3.5 (Glow Icon) can follow :
698 // it is IFF data
699
700 if (!has_NewIcons && img_count > 0)
701 {
702 if (Config.Clear_palette)
703 memset(context->Palette,0,sizeof(T_Palette));
704 if (header.UserData == 0)
705 memcpy(context->Palette, amigaOS1x_pal, sizeof(amigaOS1x_pal));
706 else
707 memcpy(context->Palette, amigaOS2x_pal, sizeof(amigaOS2x_pal));
708
709 Pre_load(context, header.Width, header.Height,file_size,FORMAT_INFO,PIXEL_SIMPLE, imgheaders[0].Depth);
710 Set_image_mode(context, IMAGE_MODE_ANIMATION);
711 for (img_count = 0; img_count < 2 && buffers[img_count] != NULL; img_count++)
712 {
713 if (img_count > 0)
714 {
715 if (context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE)
716 break; // don't load 2nd image in preview mode
717 Set_loading_layer(context, img_count);
718 }
719 for (y_pos = 0; y_pos < imgheaders[img_count].Height; y_pos++)
720 Draw_IFF_line(context, buffers[img_count] + y_pos * line_size, y_pos, plane_line_size << 3, imgheaders[img_count].Depth);
721 }
722 }
723 for (img_count = 0; img_count < 2; img_count++)
724 if (buffers[img_count] != NULL)
725 {
726 free(buffers[img_count]);
727 buffers[img_count] = NULL;
728 }
729 }
730 else
731 File_error=1;
732 fclose(file);
733 }
734
735
736
737 //////////////////////////////////// BMP ////////////////////////////////////
738 /**
739 * @defgroup BMP Bitmap and icon files
740 * @ingroup loadsaveformats
741 * .BMP/.ICO/.CUR files from OS/2 or Windows
742 *
743 * We support OS/2 files and windows BITMAPINFOHEADER, BITMAPV4HEADER,
744 * BITMAPV5HEADER files.
745 *
746 * .ICO with PNG content are also supported
747 *
748 * @{
749 */
750
751 /// BMP file header
752 typedef struct
753 {
754 byte Signature[2]; ///< ='BM' = 0x4D42
755 dword Size_1; ///< file size
756 word Reserved_1; ///< 0
757 word Reserved_2; ///< 0
758 dword Offset; ///< Offset of bitmap data start
759 dword Size_2; ///< BITMAPINFOHEADER size
760 dword Width; ///< Image Width
761 int32_t Height; ///< Image Height. signed: negative means a top-down bitmap (rare)
762 word Planes; ///< Should be 1
763 word Nb_bits; ///< Bits per pixel : 1,2,4,8,16,24 or 32
764 dword Compression; ///< Known values : 0=BI_RGB, 1=BI_RLE8, 2=BI_RLE4, 3=BI_BITFIELDS, 4=BI_JPEG, 5=BI_PNG
765 dword Size_3; ///< (optional) byte size of bitmap data
766 dword XPM; ///< (optional) horizontal pixels-per-meter
767 dword YPM; ///< (optional) vertical pixels-per-meter
768 dword Nb_Clr; ///< number of color indexes used in the table. 0 for default (1 << Nb_bits)
769 dword Clr_Imprt; ///< number of color indexes that are required for displaying the bitmap. 0 : all colors are required.
770 } T_BMP_Header;
771
772 /// Test for BMP format
Test_BMP(T_IO_Context * context,FILE * file)773 void Test_BMP(T_IO_Context * context, FILE * file)
774 {
775 T_BMP_Header header;
776
777 (void)context;
778 File_error=1;
779
780 if (Read_bytes(file,&(header.Signature),2) // "BM"
781 && Read_dword_le(file,&(header.Size_1))
782 && Read_word_le(file,&(header.Reserved_1))
783 && Read_word_le(file,&(header.Reserved_2))
784 && Read_dword_le(file,&(header.Offset))
785 && Read_dword_le(file,&(header.Size_2))
786 )
787 {
788 if (header.Signature[0]=='B' && header.Signature[1]=='M' &&
789 ((header.Size_1 == (header.Size_2 + 14)) || // Size_1 is fixed to 14 + header Size in some OS/2 BMPs
790 (header.Offset < header.Size_1 && header.Offset >= (14 + header.Size_2))))
791 {
792 GFX2_Log(GFX2_DEBUG, "BMP : Size_1=%u Offset=%u Size_2=%u\n",
793 header.Size_1, header.Offset, header.Size_2);
794 if ( header.Size_2==40 /* WINDOWS BITMAPINFOHEADER */
795 || header.Size_2==12 /* OS/2 */
796 || header.Size_2==64 /* OS/2 v2 */
797 || header.Size_2==16 /* OS/2 v2 - short - */
798 || header.Size_2==108 /* Windows BITMAPV4HEADER */
799 || header.Size_2==124 /* Windows BITMAPV5HEADER */ )
800 {
801 File_error=0;
802 }
803 }
804 }
805 }
806
807 /// extract component value and properly shift it.
Bitmap_mask(dword pixel,dword mask,int bits,int shift)808 static byte Bitmap_mask(dword pixel, dword mask, int bits, int shift)
809 {
810 dword value = (pixel & mask) >> shift;
811 if (bits < 8)
812 value = (value << (8 - bits)) | (value >> (2 * bits - 8));
813 else if (bits > 8)
814 value >>= (bits - 8);
815 return (byte)value;
816 }
817
818 /// Load the Palette for 1 to 8bpp BMP's
Load_BMP_Palette(T_IO_Context * context,FILE * file,unsigned int nb_colors,int is_rgb24)819 static void Load_BMP_Palette(T_IO_Context * context, FILE * file, unsigned int nb_colors, int is_rgb24)
820 {
821 byte local_palette[256*4]; // R,G,B,0 or RGB
822 unsigned int i, j;
823
824 if (Read_bytes(file, local_palette, MIN(nb_colors, 256) * (is_rgb24?3:4)))
825 {
826 if (Config.Clear_palette)
827 memset(context->Palette,0,sizeof(T_Palette));
828
829 // We can now load the new palette
830 for (i = 0, j = 0; i < nb_colors && i < 256; i++)
831 {
832 context->Palette[i].B = local_palette[j++];
833 context->Palette[i].G = local_palette[j++];
834 context->Palette[i].R = local_palette[j++];
835 if (!is_rgb24) j++;
836 }
837 if (nb_colors > 256) // skip additional entries
838 fseek(file, (nb_colors - 256) * (is_rgb24?3:4), SEEK_CUR);
839 }
840 else
841 {
842 File_error=1;
843 }
844 }
845
846 /// rows are stored from the top to the bottom (standard for BMP is from bottom to the top)
847 #define LOAD_BMP_PIXEL_FLAG_TOP_DOWN 0x01
848 /// We are decoding the AND-mask plane (transparency) of a .ICO file
849 #define LOAD_BMP_PIXEL_FLAG_TRANSP_PLANE 0x02
850
Load_BMP_Pixels(T_IO_Context * context,FILE * file,unsigned int compression,unsigned int nbbits,int flags,const dword * mask)851 static void Load_BMP_Pixels(T_IO_Context * context, FILE * file, unsigned int compression, unsigned int nbbits, int flags, const dword * mask)
852 {
853 unsigned int index;
854 short x_pos;
855 short y_pos;
856 byte value;
857 byte a,b;
858 int bits[4];
859 int shift[4];
860 int i;
861
862 // compute bit count and shift for masks
863 for (i = 0; i < 4; i++)
864 {
865 if (mask[i] == 0)
866 {
867 bits[i] = 0;
868 shift[i] = 0;
869 }
870 else
871 {
872 bits[i] = count_set_bits(mask[i]);
873 shift[i] = count_trailing_zeros(mask[i]);
874 }
875 }
876
877 switch (compression)
878 {
879 case 0 : // BI_RGB : No compression
880 case 3 : // BI_BITFIELDS
881 for (y_pos=0; (y_pos < context->Height && !File_error); y_pos++)
882 {
883 short target_y;
884 target_y = (flags & LOAD_BMP_PIXEL_FLAG_TOP_DOWN) ? y_pos : context->Height-1-y_pos;
885
886 switch (nbbits)
887 {
888 case 8 :
889 for (x_pos = 0; x_pos < context->Width; x_pos++)
890 {
891 if (!Read_byte(file, &value))
892 File_error = 2;
893 Set_pixel(context, x_pos, target_y, value);
894 }
895 break;
896 case 4 :
897 for (x_pos = 0; x_pos < context->Width; )
898 {
899 if (!Read_byte(file, &value))
900 File_error = 2;
901 Set_pixel(context, x_pos++, target_y, (value >> 4) & 0x0F);
902 Set_pixel(context, x_pos++, target_y, value & 0x0F);
903 }
904 break;
905 case 2:
906 for (x_pos = 0; x_pos < context->Width; x_pos++)
907 {
908 if ((x_pos & 3) == 0)
909 {
910 if (!Read_byte(file, &value))
911 File_error = 2;
912 }
913 Set_pixel(context, x_pos, target_y, (value >> 6) & 3);
914 value <<= 2;
915 }
916 break;
917 case 1 :
918 for (x_pos = 0; x_pos < context->Width; x_pos++)
919 {
920 if ((x_pos & 7) == 0)
921 {
922 if (!Read_byte(file, &value))
923 File_error = 2;
924 }
925 if (flags & LOAD_BMP_PIXEL_FLAG_TRANSP_PLANE)
926 {
927 if (value & 0x80) // transparent pixel !
928 Set_pixel(context, x_pos, target_y, context->Transparent_color);
929 }
930 else
931 Set_pixel(context, x_pos, target_y, (value >> 7) & 1);
932 value <<= 1;
933 }
934 break;
935 case 24:
936 for (x_pos = 0; x_pos < context->Width; x_pos++)
937 {
938 byte bgr[3];
939 if (!Read_bytes(file, bgr, 3))
940 File_error = 2;
941 Set_pixel_24b(context, x_pos, target_y, bgr[2], bgr[1], bgr[0]);
942 }
943 break;
944 case 32:
945 for (x_pos = 0; x_pos < context->Width; x_pos++)
946 {
947 dword pixel;
948 if (!Read_dword_le(file, &pixel))
949 File_error = 2;
950 Set_pixel_24b(context, x_pos, target_y,
951 Bitmap_mask(pixel,mask[0],bits[0],shift[0]),
952 Bitmap_mask(pixel,mask[1],bits[1],shift[1]),
953 Bitmap_mask(pixel,mask[2],bits[2],shift[2]));
954 }
955 break;
956 case 16:
957 for (x_pos = 0; x_pos < context->Width; x_pos++)
958 {
959 word pixel;
960 if (!Read_word_le(file, &pixel))
961 File_error = 2;
962 Set_pixel_24b(context, x_pos, target_y,
963 Bitmap_mask(pixel,mask[0],bits[0],shift[0]),
964 Bitmap_mask(pixel,mask[1],bits[1],shift[1]),
965 Bitmap_mask(pixel,mask[2],bits[2],shift[2]));
966 }
967 break;
968 }
969 // lines are padded to dword sizes
970 if (((context->Width * nbbits + 7) >> 3) & 3)
971 fseek(file, 4 - (((context->Width * nbbits + 7) >> 3) & 3), SEEK_CUR);
972 }
973 break;
974
975 case 1 : // BI_RLE8 Compression
976 x_pos=0;
977
978 y_pos=context->Height-1;
979
980 /*Init_lecture();*/
981 if(Read_byte(file, &a)!=1 || Read_byte(file, &b)!=1)
982 File_error=2;
983 while (!File_error)
984 {
985 if (a) // Encoded mode
986 for (index=1; index<=a; index++)
987 Set_pixel(context, x_pos++,y_pos,b);
988 else // Absolute mode
989 switch (b)
990 {
991 case 0 : // End of line
992 x_pos=0;
993 y_pos--;
994 break;
995 case 1 : // End of bitmap
996 break;
997 case 2 : // Delta
998 if(Read_byte(file, &a)!=1 || Read_byte(file, &b)!=1)
999 File_error=2;
1000 x_pos+=a;
1001 y_pos-=b;
1002 break;
1003 default: // Nouvelle série
1004 while (b)
1005 {
1006 if(Read_byte(file, &a)!=1)
1007 File_error=2;
1008 //Read_one_byte(file, &c);
1009 Set_pixel(context, x_pos++,y_pos,a);
1010 //if (--c)
1011 //{
1012 // Set_pixel(context, x_pos++,y_pos,c);
1013 // b--;
1014 //}
1015 b--;
1016 }
1017 if (ftell(file) & 1) fseek(file, 1, SEEK_CUR);
1018 }
1019 if (a==0 && b==1)
1020 break;
1021 if(Read_byte(file, &a) !=1 || Read_byte(file, &b)!=1)
1022 {
1023 File_error=2;
1024 }
1025 }
1026 break;
1027
1028 case 2 : // BI_RLE4 Compression
1029 x_pos = 0;
1030 y_pos = context->Height-1;
1031
1032 while (!File_error)
1033 {
1034 if(!(Read_byte(file, &a) && Read_byte(file, &b)))
1035 {
1036 File_error = 2;
1037 break;
1038 }
1039 if (a > 0) // Encoded mode : pixel count = a
1040 {
1041 //GFX2_Log(GFX2_DEBUG, "BI_RLE4: %d &%02X\n", a, b);
1042 for (index = 0; index < a; index++)
1043 Set_pixel(context, x_pos++, y_pos, ((index & 1) ? b : (b >> 4)) & 0x0f);
1044 }
1045 else
1046 {
1047 // a == 0 : Escape code
1048 byte c = 0;
1049
1050 //GFX2_Log(GFX2_DEBUG, "BI_RLE4: %d %d\n", a, b);
1051 if (b == 1) // end of bitmap
1052 break;
1053 switch (b)
1054 {
1055 case 0 : //End of line
1056 x_pos = 0;
1057 y_pos--;
1058 break;
1059 case 2 : // Delta
1060 if(Read_byte(file, &a)!=1 || Read_byte(file, &b)!=1)
1061 File_error=2;
1062 x_pos += a;
1063 y_pos -= b;
1064 break;
1065 default: // Absolute mode : pixel count = b
1066 for (index = 0; index < b && !File_error; index++, x_pos++)
1067 {
1068 if (index&1)
1069 Set_pixel(context, x_pos,y_pos,c&0xF);
1070 else
1071 {
1072 if (!Read_byte(file, &c))
1073 File_error=2;
1074 else
1075 Set_pixel(context, x_pos,y_pos,c>>4);
1076 }
1077 }
1078 if ((b + 1) & 2)
1079 {
1080 // read a pad byte to enforce word alignment
1081 if (!Read_byte(file, &c))
1082 File_error = 2;
1083 }
1084 }
1085 }
1086 }
1087 break;
1088 case 5: // BI_PNG
1089 {
1090 byte png_header[8];
1091 // Load header (8 first bytes)
1092 if (!Read_bytes(file,png_header,8))
1093 File_error = 1;
1094 else
1095 {
1096 // Do we recognize a png file signature ?
1097 #ifndef __no_pnglib__
1098 if (png_sig_cmp(png_header, 0, 8) == 0)
1099 {
1100 Load_PNG_Sub(context, file, NULL, 0);
1101 }
1102 #else
1103 if (0 == memcmp(png_header, "\x89PNG", 4))
1104 {
1105 // NO PNG Support
1106 GFX2_Log(GFX2_WARNING, "PNG Signature : Compiled without libpng support\n");
1107 File_error = 2;
1108 }
1109 #endif
1110 else
1111 {
1112 GFX2_Log(GFX2_WARNING, "No PNG signature in BI_PNG BMP\n");
1113 File_error = 1;
1114 }
1115 }
1116 }
1117 break;
1118 default:
1119 GFX2_Log(GFX2_WARNING, "BMP: Unknown compression type %d\n", compression);
1120 }
1121 }
1122
1123 /// Load BMP file
Load_BMP(T_IO_Context * context)1124 void Load_BMP(T_IO_Context * context)
1125 {
1126 FILE *file;
1127 T_BMP_Header header;
1128 word nb_colors = 0;
1129 long file_size;
1130 byte negative_height; // top_down
1131 byte true_color = 0;
1132 dword mask[4]; // R G B A
1133
1134 file = Open_file_read(context);
1135 if (file == NULL)
1136 {
1137 File_error = 1;
1138 return;
1139 }
1140
1141 File_error = 0;
1142
1143 file_size = File_length_file(file);
1144
1145 /* Read header */
1146 if (!(Read_bytes(file,header.Signature,2)
1147 && Read_dword_le(file,&(header.Size_1))
1148 && Read_word_le(file,&(header.Reserved_1))
1149 && Read_word_le(file,&(header.Reserved_2))
1150 && Read_dword_le(file,&(header.Offset))
1151 && Read_dword_le(file,&(header.Size_2))
1152 ))
1153 {
1154 File_error = 1;
1155 }
1156 else
1157 {
1158 if (header.Size_2 == 40 /* WINDOWS BITMAPINFOHEADER*/
1159 || header.Size_2 == 64 /* OS/2 v2 */
1160 || header.Size_2 == 108 /* Windows BITMAPV4HEADER */
1161 || header.Size_2 == 124 /* Windows BITMAPV5HEADER */)
1162 {
1163 if (!(Read_dword_le(file,&(header.Width))
1164 && Read_dword_le(file,(dword *)&(header.Height))
1165 && Read_word_le(file,&(header.Planes))
1166 && Read_word_le(file,&(header.Nb_bits))
1167 && Read_dword_le(file,&(header.Compression))
1168 && Read_dword_le(file,&(header.Size_3))
1169 && Read_dword_le(file,&(header.XPM))
1170 && Read_dword_le(file,&(header.YPM))
1171 && Read_dword_le(file,&(header.Nb_Clr))
1172 && Read_dword_le(file,&(header.Clr_Imprt))
1173 ))
1174 File_error = 1;
1175 else
1176 GFX2_Log(GFX2_DEBUG, "%s BMP %ux%d planes=%u bpp=%u compression=%u %u/%u\n",
1177 header.Size_2 == 64 ? "OS/2 v2" : "Windows",
1178 header.Width, header.Height, header.Planes, header.Nb_bits, header.Compression,
1179 header.XPM, header.YPM);
1180 }
1181 else if (header.Size_2 == 16) /* OS/2 v2 *short* */
1182 {
1183 if (!(Read_dword_le(file,&(header.Width))
1184 && Read_dword_le(file,(dword *)&(header.Height))
1185 && Read_word_le(file,&(header.Planes))
1186 && Read_word_le(file,&(header.Nb_bits))
1187 ))
1188 File_error = 1;
1189 else
1190 {
1191 GFX2_Log(GFX2_DEBUG, "OS/2 v2 BMP %ux%d planes=%u bpp=%u *short header*\n",
1192 header.Width, header.Height, header.Planes, header.Nb_bits);
1193 header.Compression = 0;
1194 header.Size_3 = 0;
1195 header.XPM = 0;
1196 header.YPM = 0;
1197 header.Nb_Clr = 0;
1198 header.Clr_Imprt = 0;
1199 }
1200 }
1201 else if (header.Size_2 == 12 /* OS/2 */)
1202 {
1203 word tmp_width = 0, tmp_height = 0;
1204 if (Read_word_le(file,&tmp_width)
1205 && Read_word_le(file,&tmp_height)
1206 && Read_word_le(file,&(header.Planes))
1207 && Read_word_le(file,&(header.Nb_bits)))
1208 {
1209 GFX2_Log(GFX2_DEBUG, "OS/2 BMP %ux%u planes=%u bpp=%u\n", tmp_width, tmp_height, header.Planes, header.Nb_bits);
1210 header.Width = tmp_width;
1211 header.Height = tmp_height;
1212 header.Compression = 0;
1213 header.Size_3 = 0;
1214 header.XPM = 0;
1215 header.YPM = 0;
1216 header.Nb_Clr = 0;
1217 header.Clr_Imprt = 0;
1218 }
1219 else
1220 File_error = 1;
1221 }
1222 else
1223 {
1224 GFX2_Log(GFX2_WARNING, "Unknown BMP type Size_2=%u\n", header.Size_2);
1225 File_error = 1;
1226 }
1227 }
1228
1229 if (File_error == 0)
1230 {
1231 /* header was read */
1232 switch (header.Nb_bits)
1233 {
1234 case 1 :
1235 case 2 :
1236 case 4 :
1237 case 8 :
1238 if (header.Nb_Clr)
1239 nb_colors = header.Nb_Clr;
1240 else
1241 nb_colors = 1 << header.Nb_bits;
1242 break;
1243 case 16:
1244 case 24:
1245 case 32:
1246 true_color = 1;
1247 break;
1248 case 0:
1249 if (header.Compression == 5) // Nb_bits is 0 with BI_PNG
1250 break;
1251 #if defined(__GNUC__) && (__GNUC__ >= 7)
1252 __attribute__ ((fallthrough));
1253 #endif
1254 default:
1255 GFX2_Log(GFX2_WARNING, "BMP: Unsupported bit per pixel %u\n", header.Nb_bits);
1256 File_error = 1;
1257 }
1258
1259 if (header.Height < 0)
1260 {
1261 negative_height = 1;
1262 header.Height = -header.Height;
1263 }
1264 else
1265 {
1266 negative_height = 0;
1267 }
1268
1269 // Image 16/24/32 bits
1270 if (header.Nb_bits == 16)
1271 {
1272 mask[0] = 0x00007C00;
1273 mask[1] = 0x000003E0;
1274 mask[2] = 0x0000001F;
1275 }
1276 else
1277 {
1278 mask[0] = 0x00FF0000;
1279 mask[1] = 0x0000FF00;
1280 mask[2] = 0x000000FF;
1281 }
1282 mask[3] = 0;
1283 if (File_error == 0)
1284 {
1285 enum PIXEL_RATIO ratio = PIXEL_SIMPLE;
1286
1287 if (header.XPM > 0 && header.YPM > 0)
1288 {
1289 if (header.XPM * 10 > header.YPM * 15) // XPM/YPM > 1.5
1290 ratio = PIXEL_TALL;
1291 else if (header.XPM * 15 < header.YPM * 10) // XPM/YPM < 1/1.5
1292 ratio = PIXEL_WIDE;
1293 }
1294
1295 Pre_load(context, header.Width, header.Height, file_size, FORMAT_BMP, ratio, header.Nb_bits);
1296 if (File_error==0)
1297 {
1298 if (header.Size_2 == 64)
1299 fseek(file, header.Size_2 - 40, SEEK_CUR);
1300 if (header.Size_2 >= 108 || (true_color && header.Compression == 3)) // BI_BITFIELDS
1301 {
1302 if (!Read_dword_le(file,&mask[0]) ||
1303 !Read_dword_le(file,&mask[1]) ||
1304 !Read_dword_le(file,&mask[2]))
1305 File_error=2;
1306 if (header.Size_2 >= 108)
1307 {
1308 Read_dword_le(file,&mask[3]); // Alpha mask
1309 fseek(file, header.Size_2 - 40 - 16, SEEK_CUR); // skip extended v4/v5 header fields
1310 }
1311 GFX2_Log(GFX2_DEBUG, "BMP masks : R=%08x G=%08x B=%08x A=%08x\n", mask[0], mask[1], mask[2], mask[3]);
1312 }
1313 if (nb_colors > 0)
1314 Load_BMP_Palette(context, file, nb_colors, header.Size_2 == 12);
1315
1316 if (File_error==0)
1317 {
1318 if (fseek(file, header.Offset, SEEK_SET))
1319 File_error=2;
1320 else
1321 Load_BMP_Pixels(context, file, header.Compression, header.Nb_bits, negative_height ? LOAD_BMP_PIXEL_FLAG_TOP_DOWN : 0, mask);
1322 }
1323 }
1324 }
1325 }
1326 fclose(file);
1327 }
1328
1329
1330 /// Save BMP file
Save_BMP(T_IO_Context * context)1331 void Save_BMP(T_IO_Context * context)
1332 {
1333 FILE *file;
1334 T_BMP_Header header;
1335 short x_pos;
1336 short y_pos;
1337 long line_size;
1338 word index;
1339 byte local_palette[256][4]; // R,G,B,0
1340
1341
1342 File_error=0;
1343
1344 // Ouverture du fichier
1345 if ((file=Open_file_write(context)))
1346 {
1347 setvbuf(file, NULL, _IOFBF, 64*1024);
1348
1349 // Image width must be a multiple of 4 bytes
1350 line_size = context->Width;
1351 if (line_size & 3)
1352 line_size += (4 - (line_size & 3));
1353
1354 header.Signature[0] = 'B';
1355 header.Signature[1] = 'M';
1356 header.Size_1 =(line_size*context->Height)+1078;
1357 header.Reserved_1 =0;
1358 header.Reserved_2 =0;
1359 header.Offset =1078; // Size of header data (including palette)
1360 header.Size_2 =40; // Size of header
1361 header.Width =context->Width;
1362 header.Height =context->Height;
1363 header.Planes =1;
1364 header.Nb_bits =8;
1365 header.Compression=0;
1366 header.Size_3 =0;
1367 header.XPM =0;
1368 header.YPM =0;
1369 header.Nb_Clr =0;
1370 header.Clr_Imprt =0;
1371
1372 if (Write_bytes(file,header.Signature,2)
1373 && Write_dword_le(file,header.Size_1)
1374 && Write_word_le(file,header.Reserved_1)
1375 && Write_word_le(file,header.Reserved_2)
1376 && Write_dword_le(file,header.Offset)
1377 && Write_dword_le(file,header.Size_2)
1378 && Write_dword_le(file,header.Width)
1379 && Write_dword_le(file,header.Height)
1380 && Write_word_le(file,header.Planes)
1381 && Write_word_le(file,header.Nb_bits)
1382 && Write_dword_le(file,header.Compression)
1383 && Write_dword_le(file,header.Size_3)
1384 && Write_dword_le(file,header.XPM)
1385 && Write_dword_le(file,header.YPM)
1386 && Write_dword_le(file,header.Nb_Clr)
1387 && Write_dword_le(file,header.Clr_Imprt))
1388 {
1389 // Chez Bill, ils ont dit: "On va mettre les couleur dans l'ordre
1390 // inverse, et pour faire chier, on va les mettre sur une échelle de
1391 // 0 à 255 parce que le standard VGA c'est de 0 à 63 (logique!). Et
1392 // puis comme c'est pas assez débile, on va aussi y rajouter un octet
1393 // toujours à 0 pour forcer les gens à s'acheter des gros disques
1394 // durs... Comme ça, ça fera passer la pillule lorsqu'on sortira
1395 // Windows 95." ...
1396 for (index=0; index<256; index++)
1397 {
1398 local_palette[index][0]=context->Palette[index].B;
1399 local_palette[index][1]=context->Palette[index].G;
1400 local_palette[index][2]=context->Palette[index].R;
1401 local_palette[index][3]=0;
1402 }
1403
1404 if (Write_bytes(file,local_palette,1024))
1405 {
1406 // ... Et Bill, il a dit: "OK les gars! Mais seulement si vous rangez
1407 // les pixels dans l'ordre inverse, mais que sur les Y quand-même
1408 // parce que faut pas pousser."
1409 for (y_pos=context->Height-1; ((y_pos>=0) && (!File_error)); y_pos--)
1410 for (x_pos=0; x_pos<line_size; x_pos++)
1411 Write_one_byte(file,Get_pixel(context, x_pos,y_pos));
1412
1413 fclose(file);
1414
1415 if (File_error)
1416 Remove_file(context);
1417 }
1418 else
1419 {
1420 fclose(file);
1421 Remove_file(context);
1422 File_error=1;
1423 }
1424
1425 }
1426 else
1427 {
1428 fclose(file);
1429 Remove_file(context);
1430 File_error=1;
1431 }
1432 }
1433 else
1434 File_error=1;
1435 }
1436
1437
1438 //////////////////////////////////// ICO ////////////////////////////////////
1439 typedef struct {
1440 byte width; //Specifies image width in pixels. Value 0 means image width is 256 pixels.
1441 byte height; //Specifies image height in pixels. Value 0 means image height is 256 pixels.
1442 byte ncolors; //0 for image without palette
1443 byte reserved;
1444 word planes; // (hotspot X for CUR files)
1445 word bpp; // (hotspot Y for CUR files)
1446 dword bytecount;
1447 dword offset;
1448 } T_ICO_ImageEntry;
1449
Test_ICO(T_IO_Context * context,FILE * file)1450 void Test_ICO(T_IO_Context * context, FILE * file)
1451 {
1452 struct {
1453 word Reserved;
1454 word Type; // Specifies image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid.
1455 word Count; // Specifies number of images in the file.
1456 } header;
1457
1458 (void)context;
1459 File_error=1;
1460
1461 if (Read_word_le(file,&(header.Reserved))
1462 && Read_word_le(file,&(header.Type))
1463 && Read_word_le(file,&(header.Count)))
1464 {
1465 if (header.Reserved == 0 && (header.Type == 1 || header.Type == 2) && header.Count > 0)
1466 File_error=0;
1467 }
1468 }
1469
Load_ICO(T_IO_Context * context)1470 void Load_ICO(T_IO_Context * context)
1471 {
1472 FILE *file;
1473 struct {
1474 word Reserved;
1475 word Type; // Specifies image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid.
1476 word Count; // Specifies number of images in the file.
1477 } header;
1478 T_ICO_ImageEntry * images;
1479 T_ICO_ImageEntry * entry;
1480 unsigned int i;
1481 word width, max_width = 0;
1482 word max_bpp = 0;
1483 word min_bpp = 0xffff;
1484 dword mask[4]; // R G B A
1485
1486 File_error=0;
1487
1488 if ((file=Open_file_read(context)))
1489 {
1490 if (Read_word_le(file,&(header.Reserved))
1491 && Read_word_le(file,&(header.Type))
1492 && Read_word_le(file,&(header.Count)))
1493 {
1494 images = malloc(sizeof(T_ICO_ImageEntry) * header.Count);
1495 if (images == NULL)
1496 {
1497 fclose(file);
1498 File_error=1;
1499 return;
1500 }
1501 for (i = 0; i < header.Count; i++) {
1502 entry = images + i;
1503 if (!Read_byte(file,&entry->width)
1504 || !Read_byte(file,&entry->height)
1505 || !Read_byte(file,&entry->ncolors)
1506 || !Read_byte(file,&entry->reserved)
1507 || !Read_word_le(file,&entry->planes)
1508 || !Read_word_le(file,&entry->bpp)
1509 || !Read_dword_le(file,&entry->bytecount)
1510 || !Read_dword_le(file,&entry->offset))
1511 {
1512 free(images);
1513 fclose(file);
1514 File_error=1;
1515 return;
1516 }
1517 width = entry->width;
1518 if (width == 0) width = 256;
1519 if (width > max_width) max_width = width;
1520 // For various reasons, 256x256 icons are all in PNG format,
1521 // and Microsoft decided PNG inside ICO should be in 32bit ARGB format...
1522 // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473
1523 GFX2_Log(GFX2_DEBUG, "%s #%02u %3ux%3u %ucols %ux%ubpp %u bytes at 0x%06x\n",
1524 (header.Type == 2) ? "CUR" : "ICO",
1525 i, width, entry->height, entry->ncolors, entry->planes, entry->bpp,
1526 entry->bytecount, entry->offset);
1527 }
1528 // select the picture with the maximum width and 256 colors or less
1529 for (i = 0; i < header.Count; i++)
1530 {
1531 if (images[i].width == (max_width & 0xff))
1532 {
1533 if (header.Type == 2) // .CUR files have hotspot instead of planes/bpp in header
1534 break;
1535 if (images[i].bpp == 8)
1536 break;
1537 if (images[i].bpp < 8 && images[i].bpp > max_bpp)
1538 max_bpp = images[i].bpp;
1539 if (images[i].bpp < min_bpp)
1540 min_bpp = images[i].bpp;
1541 }
1542 }
1543 if (i >= header.Count && header.Type == 1)
1544 {
1545 // 256 color not found, select another one
1546 for (i = 0; i < header.Count; i++)
1547 {
1548 if (images[i].width == (max_width & 0xff))
1549 {
1550 if ((max_bpp != 0 && images[i].bpp == max_bpp)
1551 || (images[i].bpp == min_bpp))
1552 {
1553 break;
1554 }
1555 }
1556 }
1557 }
1558 if (i >= header.Count)
1559 {
1560 File_error=2;
1561 }
1562 else
1563 {
1564 byte png_header[8];
1565
1566 entry = images + i;
1567 GFX2_Log(GFX2_DEBUG, "Selected icon #%u at offset 0x%06x\n", i, entry->offset);
1568 fseek(file, entry->offset, SEEK_SET);
1569
1570 // detect PNG icons
1571 // Load header (8 first bytes)
1572 if (!Read_bytes(file,png_header,8))
1573 {
1574 File_error = 1;
1575 }
1576 else
1577 {
1578 // Do we recognize a png file signature ?
1579 #ifndef __no_pnglib__
1580 if (png_sig_cmp(png_header, 0, 8) == 0)
1581 {
1582 Load_PNG_Sub(context, file, NULL, 0);
1583 }
1584 #else
1585 if (0 == memcmp(png_header, "\x89PNG", 4))
1586 {
1587 // NO PNG Support
1588 GFX2_Log(GFX2_WARNING, "PNG Signature : Compiled without libpng support\n");
1589 File_error = 2;
1590 }
1591 #endif
1592 else
1593 {
1594 T_BMP_Header bmpheader;
1595
1596 fseek(file, -8, SEEK_CUR); // back
1597 // BMP
1598 if (Read_dword_le(file,&(bmpheader.Size_2)) // 40
1599 && Read_dword_le(file,&(bmpheader.Width))
1600 && Read_dword_le(file,(dword *)&(bmpheader.Height))
1601 && Read_word_le(file,&(bmpheader.Planes))
1602 && Read_word_le(file,&(bmpheader.Nb_bits))
1603 && Read_dword_le(file,&(bmpheader.Compression))
1604 && Read_dword_le(file,&(bmpheader.Size_3))
1605 && Read_dword_le(file,&(bmpheader.XPM))
1606 && Read_dword_le(file,&(bmpheader.YPM))
1607 && Read_dword_le(file,&(bmpheader.Nb_Clr))
1608 && Read_dword_le(file,&(bmpheader.Clr_Imprt)) )
1609 {
1610 short real_height;
1611 word nb_colors = 0;
1612
1613 GFX2_Log(GFX2_DEBUG, " BITMAPINFOHEADER %u %dx%d %ux%ubpp comp=%u\n",
1614 bmpheader.Size_2, bmpheader.Width, bmpheader.Height, bmpheader.Planes,
1615 bmpheader.Nb_bits, bmpheader.Compression);
1616 if (bmpheader.Nb_Clr != 0)
1617 nb_colors=bmpheader.Nb_Clr;
1618 else
1619 nb_colors=1<<bmpheader.Nb_bits;
1620
1621 real_height = bmpheader.Height / 2;
1622 if (bmpheader.Height < 0) real_height = - real_height;
1623 // check that real_height == entry->height
1624 if (real_height != entry->height)
1625 {
1626 GFX2_Log(GFX2_WARNING, "Load_ICO() : real_height(%hd) != entry->height(%hd)\n", real_height, entry->height);
1627 }
1628
1629 // Image 16/24/32 bits
1630 if (bmpheader.Nb_bits == 16)
1631 {
1632 mask[0] = 0x00007C00;
1633 mask[1] = 0x000003E0;
1634 mask[2] = 0x0000001F;
1635 }
1636 else
1637 {
1638 mask[0] = 0x00FF0000;
1639 mask[1] = 0x0000FF00;
1640 mask[2] = 0x000000FF;
1641 }
1642 mask[3] = 0;
1643 Pre_load(context, bmpheader.Width,real_height,File_length_file(file),FORMAT_ICO,PIXEL_SIMPLE,bmpheader.Nb_bits);
1644 if (bmpheader.Nb_bits <= 8)
1645 Load_BMP_Palette(context, file, nb_colors, 0);
1646 else
1647 {
1648 if (bmpheader.Compression == 3) // BI_BITFIELDS
1649 {
1650 if (!Read_dword_le(file,&mask[0]) ||
1651 !Read_dword_le(file,&mask[1]) ||
1652 !Read_dword_le(file,&mask[2]))
1653 File_error=2;
1654 }
1655 }
1656 if (File_error == 0)
1657 {
1658 Load_BMP_Pixels(context, file, bmpheader.Compression, bmpheader.Nb_bits, (bmpheader.Height < 0) ? LOAD_BMP_PIXEL_FLAG_TOP_DOWN : 0, mask);
1659 // load transparency
1660 // TODO : load transparency for True color images too
1661 if (bmpheader.Nb_bits <= 8)
1662 {
1663 context->Transparent_color = 0xff; // TODO : pick an unused color if possible
1664 context->Background_transparent = 1;
1665 Load_BMP_Pixels(context, file, bmpheader.Compression, 1, (bmpheader.Height < 0) ? (LOAD_BMP_PIXEL_FLAG_TOP_DOWN|LOAD_BMP_PIXEL_FLAG_TRANSP_PLANE) : LOAD_BMP_PIXEL_FLAG_TRANSP_PLANE, mask);
1666 }
1667 }
1668 }
1669 }
1670 }
1671 }
1672 free(images);
1673 }
1674 fclose(file);
1675 } else {
1676 File_error=1;
1677 }
1678 }
1679
Save_ICO(T_IO_Context * context)1680 void Save_ICO(T_IO_Context * context)
1681 {
1682 FILE *file;
1683 short x_pos;
1684 short y_pos;
1685 long row_size;
1686 long row_size_mask;
1687
1688 if (context->Width > 256 || context->Height > 256)
1689 {
1690 File_error=1;
1691 GFX2_Log(GFX2_WARNING, ".ICO files can handle images up to 256x256\n");
1692 return;
1693 }
1694
1695 File_error=0;
1696
1697 if ((file=Open_file_write(context)) == NULL)
1698 File_error=1;
1699 else
1700 {
1701 row_size = (context->Width + 3) & ~3; // 8bpp (=1Byte) rounded up to dword
1702 row_size_mask = (((context->Width + 7) >> 3) + 3) & ~3; // 1bpp rounded up to dword
1703 // ICO Header
1704 if (!(Write_word_le(file,0) && // 0
1705 Write_word_le(file,1) && // TYPE 1 = .ICO (2=.CUR)
1706 Write_word_le(file,1))) // Image count
1707 File_error=1;
1708 if (File_error == 0)
1709 {
1710 T_ICO_ImageEntry entry;
1711 // ICO image entry
1712 entry.width = context->Width & 0xff; //Specifies image width in pixels. Value 0 means image width is 256 pixels.
1713 entry.height = context->Height & 0xff;//Specifies image height in pixels. Value 0 means image height is 256 pixels.
1714 entry.ncolors = 0;
1715 entry.reserved = 0;
1716 entry.planes = 1;
1717 entry.bpp = 8;
1718 entry.bytecount = (row_size + row_size_mask) * context->Height + 40 + 1024;
1719 entry.offset = 6 + 16;
1720 if (!(Write_byte(file,entry.width) &&
1721 Write_byte(file,entry.height) &&
1722 Write_byte(file,entry.ncolors) &&
1723 Write_byte(file,entry.reserved) &&
1724 Write_word_le(file,entry.planes) &&
1725 Write_word_le(file,entry.bpp) &&
1726 Write_dword_le(file,entry.bytecount) &&
1727 Write_dword_le(file,entry.offset)))
1728 File_error=1;
1729 }
1730 if (File_error == 0)
1731 {
1732 T_BMP_Header bmpheader;
1733 // BMP Header
1734 bmpheader.Size_2 = 40;
1735 bmpheader.Width = context->Width;
1736 bmpheader.Height = context->Height * 2; // *2 because of mask (transparency) data added after the pixel data
1737 bmpheader.Planes = 1;
1738 bmpheader.Nb_bits = 8;
1739 bmpheader.Compression = 0;
1740 bmpheader.Size_3 = 0;
1741 bmpheader.XPM = 0;
1742 bmpheader.YPM = 0;
1743 bmpheader.Nb_Clr = 0;
1744 bmpheader.Clr_Imprt = 0;
1745 if (!(Write_dword_le(file,bmpheader.Size_2) // 40
1746 && Write_dword_le(file,bmpheader.Width)
1747 && Write_dword_le(file,bmpheader.Height)
1748 && Write_word_le(file,bmpheader.Planes)
1749 && Write_word_le(file,bmpheader.Nb_bits)
1750 && Write_dword_le(file,bmpheader.Compression)
1751 && Write_dword_le(file,bmpheader.Size_3)
1752 && Write_dword_le(file,bmpheader.XPM)
1753 && Write_dword_le(file,bmpheader.YPM)
1754 && Write_dword_le(file,bmpheader.Nb_Clr)
1755 && Write_dword_le(file,bmpheader.Clr_Imprt)) )
1756 File_error=1;
1757 }
1758 if (File_error == 0)
1759 {
1760 int i;
1761 // palette
1762 for (i = 0; i < 256; i++)
1763 {
1764 if (!Write_dword_le(file, context->Palette[i].R << 16 | context->Palette[i].G << 8 | context->Palette[i].B))
1765 {
1766 File_error=1;
1767 break;
1768 }
1769 }
1770 }
1771 if (File_error == 0)
1772 {
1773 // Image Data
1774 for (y_pos=context->Height-1; ((y_pos>=0) && (!File_error)); y_pos--)
1775 for (x_pos=0; x_pos<row_size; x_pos++)
1776 Write_one_byte(file,Get_pixel(context, x_pos,y_pos));
1777 }
1778 if (File_error == 0)
1779 {
1780 byte value = 0;
1781 // Mask (Transparency) Data
1782 for (y_pos=context->Height-1; ((y_pos>=0) && (!File_error)); y_pos--)
1783 for (x_pos=0; x_pos<row_size_mask*8; x_pos++)
1784 {
1785 value = value << 1;
1786 if (context->Background_transparent && Get_pixel(context, x_pos,y_pos) == context->Transparent_color)
1787 value |= 1; // 1 = Transparent pixel
1788 if ((x_pos & 7) == 7)
1789 {
1790 Write_one_byte(file,value);
1791 }
1792 }
1793 }
1794 fclose(file);
1795 if (File_error != 0)
1796 Remove_file(context);
1797 }
1798 }
1799 /** @} */
1800
1801
1802 //////////////////////////////////// PCX ////////////////////////////////////
1803 typedef struct
1804 {
1805 byte Manufacturer; // |_ Il font chier ces cons! Ils auraient pu
1806 byte Version; // | mettre une vraie signature!
1807 byte Compression; // L'image est-elle compressée?
1808 byte Depth; // Nombre de bits pour coder un pixel (inutile puisqu'on se sert de Plane)
1809 word X_min; // |_ Coin haut-gauche |
1810 word Y_min; // | de l'image |_ (Crétin!)
1811 word X_max; // |_ Coin bas-droit |
1812 word Y_max; // | de l'image |
1813 word X_dpi; // |_ Densité de |_ (Presque inutile parce que
1814 word Y_dpi; // | l'image | aucun moniteur n'est pareil!)
1815 byte Palette_16c[48]; // Palette 16 coul (inutile pour 256c) (débile!)
1816 byte Reserved; // Ca me plait ça aussi!
1817 byte Plane; // 4 => 16c , 1 => 256c , ...
1818 word Bytes_per_plane_line;// Doit toujours être pair
1819 word Palette_info; // 1 => color , 2 => Gris (ignoré à partir de la version 4)
1820 word Screen_X; // |_ Dimensions de
1821 word Screen_Y; // | l'écran d'origine
1822 byte Filler[54]; // Ca... J'adore!
1823 } T_PCX_Header;
1824
1825 T_PCX_Header PCX_header;
1826
1827 // -- Tester si un fichier est au format PCX --------------------------------
1828
Test_PCX(T_IO_Context * context,FILE * file)1829 void Test_PCX(T_IO_Context * context, FILE * file)
1830 {
1831 (void)context;
1832 File_error=0;
1833
1834 if (Read_byte(file,&(PCX_header.Manufacturer)) &&
1835 Read_byte(file,&(PCX_header.Version)) &&
1836 Read_byte(file,&(PCX_header.Compression)) &&
1837 Read_byte(file,&(PCX_header.Depth)) &&
1838 Read_word_le(file,&(PCX_header.X_min)) &&
1839 Read_word_le(file,&(PCX_header.Y_min)) &&
1840 Read_word_le(file,&(PCX_header.X_max)) &&
1841 Read_word_le(file,&(PCX_header.Y_max)) &&
1842 Read_word_le(file,&(PCX_header.X_dpi)) &&
1843 Read_word_le(file,&(PCX_header.Y_dpi)) &&
1844 Read_bytes(file,&(PCX_header.Palette_16c),48) &&
1845 Read_byte(file,&(PCX_header.Reserved)) &&
1846 Read_byte(file,&(PCX_header.Plane)) &&
1847 Read_word_le(file,&(PCX_header.Bytes_per_plane_line)) &&
1848 Read_word_le(file,&(PCX_header.Palette_info)) &&
1849 Read_word_le(file,&(PCX_header.Screen_X)) &&
1850 Read_word_le(file,&(PCX_header.Screen_Y)) &&
1851 Read_bytes(file,&(PCX_header.Filler),54) )
1852 {
1853 // Vu que ce header a une signature de merde et peu significative, il
1854 // va falloir que je teste différentes petites valeurs dont je connais
1855 // l'intervalle. Grrr!
1856 if ( (PCX_header.Manufacturer!=10)
1857 || (PCX_header.Compression>1)
1858 || ( (PCX_header.Depth!=1) && (PCX_header.Depth!=2) && (PCX_header.Depth!=4) && (PCX_header.Depth!=8) )
1859 || ( (PCX_header.Plane!=1) && (PCX_header.Plane!=2) && (PCX_header.Plane!=4) && (PCX_header.Plane!=8) && (PCX_header.Plane!=3) )
1860 || (PCX_header.X_max<PCX_header.X_min)
1861 || (PCX_header.Y_max<PCX_header.Y_min)
1862 || (PCX_header.Bytes_per_plane_line&1) )
1863 File_error=1;
1864 }
1865 else
1866 File_error=1;
1867
1868 }
1869
1870
1871 // -- Lire un fichier au format PCX -----------------------------------------
1872
1873 // -- Afficher une ligne PCX codée sur 1 seul plan avec moins de 256 c. --
Draw_PCX_line(T_IO_Context * context,const byte * buffer,short y_pos,byte depth)1874 static void Draw_PCX_line(T_IO_Context *context, const byte * buffer, short y_pos, byte depth)
1875 {
1876 short x_pos;
1877 byte color;
1878 byte reduction=8/depth;
1879 byte byte_mask=(1<<depth)-1;
1880 byte reduction_minus_one=reduction-1;
1881
1882 for (x_pos=0; x_pos<context->Width; x_pos++)
1883 {
1884 color=(buffer[x_pos/reduction]>>((reduction_minus_one-(x_pos%reduction))*depth)) & byte_mask;
1885 Set_pixel(context, x_pos,y_pos,color);
1886 }
1887 }
1888
1889 // generate CGA RGBI colors.
Set_CGA_Color(int i,T_Components * comp)1890 static void Set_CGA_Color(int i, T_Components * comp)
1891 {
1892 int intensity = (i & 8) ? 85 : 0;
1893 comp->R = ((i & 4) ? 170 : 0) + intensity;
1894 if (i == 6)
1895 comp->G = 85; // color 6 is brown instead of yellow on IBM CGA display
1896 else
1897 comp->G = ((i & 2) ? 170 : 0) + intensity;
1898 comp->B = ((i & 1) ? 170 : 0) + intensity;
1899 }
1900
Load_PCX(T_IO_Context * context)1901 void Load_PCX(T_IO_Context * context)
1902 {
1903 FILE *file;
1904
1905 short line_size;
1906 short real_line_size; // width de l'image corrigée
1907 short width_read;
1908 short x_pos;
1909 short y_pos;
1910 byte byte1;
1911 byte byte2;
1912 byte index;
1913 dword nb_colors;
1914 long file_size;
1915
1916 long position;
1917 long image_size;
1918 byte * buffer;
1919
1920 File_error=0;
1921
1922 if ((file=Open_file_read(context)))
1923 {
1924 file_size=File_length_file(file);
1925 if (Read_byte(file,&(PCX_header.Manufacturer)) &&
1926 Read_byte(file,&(PCX_header.Version)) &&
1927 Read_byte(file,&(PCX_header.Compression)) &&
1928 Read_byte(file,&(PCX_header.Depth)) &&
1929 Read_word_le(file,&(PCX_header.X_min)) &&
1930 Read_word_le(file,&(PCX_header.Y_min)) &&
1931 Read_word_le(file,&(PCX_header.X_max)) &&
1932 Read_word_le(file,&(PCX_header.Y_max)) &&
1933 Read_word_le(file,&(PCX_header.X_dpi)) &&
1934 Read_word_le(file,&(PCX_header.Y_dpi)) &&
1935 Read_bytes(file,&(PCX_header.Palette_16c),48) &&
1936 Read_byte(file,&(PCX_header.Reserved)) &&
1937 Read_byte(file,&(PCX_header.Plane)) &&
1938 Read_word_le(file,&(PCX_header.Bytes_per_plane_line)) &&
1939 Read_word_le(file,&(PCX_header.Palette_info)) &&
1940 Read_word_le(file,&(PCX_header.Screen_X)) &&
1941 Read_word_le(file,&(PCX_header.Screen_Y)) &&
1942 Read_bytes(file,&(PCX_header.Filler),54) )
1943 {
1944 Pre_load(context,
1945 PCX_header.X_max - PCX_header.X_min + 1,
1946 PCX_header.Y_max - PCX_header.Y_min + 1,
1947 file_size, FORMAT_PCX, PIXEL_SIMPLE,
1948 PCX_header.Plane * PCX_header.Depth);
1949
1950 Original_screen_X = PCX_header.Screen_X;
1951 Original_screen_Y = PCX_header.Screen_Y;
1952
1953 if (!(PCX_header.Plane==3 && PCX_header.Depth==8))
1954 {
1955 if (File_error==0)
1956 {
1957 // On prépare la palette à accueillir les valeurs du fichier PCX
1958 if (Config.Clear_palette)
1959 memset(context->Palette,0,sizeof(T_Palette));
1960 nb_colors=(dword)(1<<PCX_header.Plane)<<(PCX_header.Depth-1);
1961
1962 memcpy(context->Palette,PCX_header.Palette_16c,48);
1963
1964 if (nb_colors<=4)
1965 {
1966 // CGA !
1967 int i;
1968
1969 if (PCX_header.Version < 5 // Detect if the palette is usable
1970 || (nb_colors == 4
1971 && (PCX_header.Palette_16c[6]&15) == 0
1972 && (PCX_header.Palette_16c[7]&15) == 0
1973 && (PCX_header.Palette_16c[8]&15) == 0
1974 && (PCX_header.Palette_16c[9]&15) == 0
1975 && (PCX_header.Palette_16c[10]&15) == 0
1976 && (PCX_header.Palette_16c[11]&15) == 0)
1977 || (nb_colors == 2
1978 && PCX_header.Palette_16c[1] == 0
1979 && PCX_header.Palette_16c[2] == 0))
1980 {
1981 // special CGA palette meaning :
1982 if (nb_colors == 2)
1983 {
1984 // Background : BLACK
1985 context->Palette[0].R=0;
1986 context->Palette[0].G=0;
1987 context->Palette[0].B=0;
1988 // Foreground : 4 MSB of palette[0] is index of the CGA color to use.
1989 i = (PCX_header.Palette_16c[0] >> 4);
1990 if (i==0) i = 15; // Bright White by default
1991 Set_CGA_Color(i, &context->Palette[1]);
1992 }
1993 else
1994 {
1995 // Color CGA
1996 // background color : 4 MSB of palette[0]
1997 Set_CGA_Color((PCX_header.Palette_16c[0] >> 4), &context->Palette[0]);
1998 // Palette_16c[3] : 8 bits CPIx xxxx
1999 // C bit : Color burst enabled => disable it to set 3rd palette
2000 // P bit : palette : 0 = yellow/ 1 = white
2001 // I bit : intensity
2002 // CGA Palette 0 : 2 green, 4 red, 6 brown
2003 // CGA Palette 1 : 3 cyan, 5 magenta, 7 white
2004 // CGA 3rd palette : 3 cyan, 4 red, 7 white
2005 // After some tests in PC Paintbrush 3.11, it looks like
2006 // the Color burst bit is not taken into acount.
2007 i = 2; // 2 - CGA Green
2008 if (PCX_header.Palette_16c[3] & 0x40)
2009 i++; // Palette 1 (3 = cyan)
2010 if (PCX_header.Palette_16c[3] & 0x20)
2011 i += 8; // High intensity
2012 Set_CGA_Color(i++, &context->Palette[1]);
2013 i++; // Skip 1 color
2014 Set_CGA_Color(i++, &context->Palette[2]);
2015 i++; // Skip 1 color
2016 Set_CGA_Color(i, &context->Palette[3]);
2017 }
2018 }
2019 }
2020
2021 // On se positionne à la fin du fichier - 769 octets pour voir s'il y
2022 // a une palette.
2023 if ( (PCX_header.Depth==8) && (PCX_header.Version>=5) && (file_size>(256*3+128)) )
2024 {
2025 fseek(file,file_size-((256*3)+1),SEEK_SET);
2026 // On regarde s'il y a une palette après les données de l'image
2027 if (Read_byte(file,&byte1))
2028 if (byte1==12) // Lire la palette si c'est une image en 256 couleurs
2029 {
2030 int index;
2031 // On lit la palette 256c que ces crétins ont foutue à la fin du fichier
2032 for(index=0;index<256;index++)
2033 if ( ! Read_byte(file,&(context->Palette[index].R))
2034 || ! Read_byte(file,&(context->Palette[index].G))
2035 || ! Read_byte(file,&(context->Palette[index].B)) )
2036 {
2037 File_error=2;
2038 GFX2_Log(GFX2_ERROR, "ERROR READING PCX PALETTE ! index=%d\n", index);
2039 break;
2040 }
2041 }
2042 }
2043
2044 // Maintenant qu'on a lu la palette que ces crétins sont allés foutre
2045 // à la fin, on retourne juste après le header pour lire l'image.
2046 fseek(file,128,SEEK_SET);
2047 if (!File_error)
2048 {
2049 line_size=PCX_header.Bytes_per_plane_line*PCX_header.Plane;
2050 real_line_size=(short)PCX_header.Bytes_per_plane_line<<3;
2051 // On se sert de données ILBM car le dessin de ligne en moins de 256
2052 // couleurs se fait comme avec la structure ILBM.
2053 buffer=(byte *)malloc(line_size);
2054
2055 // Chargement de l'image
2056 if (PCX_header.Compression) // Image compressée
2057 {
2058 /*Init_lecture();*/
2059
2060 image_size=(long)PCX_header.Bytes_per_plane_line*context->Height;
2061
2062 if (PCX_header.Depth==8) // 256 couleurs (1 plan)
2063 {
2064 for (position=0; ((position<image_size) && (!File_error));)
2065 {
2066 // Lecture et décompression de la ligne
2067 if(Read_byte(file,&byte1) !=1) File_error=2;
2068 if (!File_error)
2069 {
2070 if ((byte1&0xC0)==0xC0)
2071 {
2072 byte1-=0xC0; // facteur de répétition
2073 if(Read_byte(file,&byte2)!=1) File_error = 2; // octet à répéter
2074 if (!File_error)
2075 {
2076 for (index=0; index<byte1; index++,position++)
2077 if (position<image_size)
2078 Set_pixel(context, position%line_size,
2079 position/line_size,
2080 byte2);
2081 else
2082 File_error=2;
2083 }
2084 }
2085 else
2086 {
2087 Set_pixel(context, position%line_size,
2088 position/line_size,
2089 byte1);
2090 position++;
2091 }
2092 }
2093 }
2094 }
2095 else // couleurs rangées par plans
2096 {
2097 for (y_pos=0; ((y_pos<context->Height) && (!File_error)); y_pos++)
2098 {
2099 for (x_pos=0; ((x_pos<line_size) && (!File_error)); )
2100 {
2101 if(Read_byte(file,&byte1)!=1) File_error = 2;
2102 if (!File_error)
2103 {
2104 if ((byte1&0xC0)==0xC0)
2105 {
2106 byte1-=0xC0; // facteur de répétition
2107 if(Read_byte(file,&byte2)!=1) File_error=2; // octet à répéter
2108 if (!File_error)
2109 {
2110 for (index=0; index<byte1; index++)
2111 if (x_pos<line_size)
2112 buffer[x_pos++]=byte2;
2113 else
2114 File_error=2;
2115 }
2116 }
2117 else
2118 buffer[x_pos++]=byte1;
2119 }
2120 }
2121 // Affichage de la ligne par plan du buffer
2122 if (PCX_header.Depth==1)
2123 Draw_IFF_line(context, buffer, y_pos,real_line_size,PCX_header.Plane);
2124 else
2125 Draw_PCX_line(context, buffer, y_pos,PCX_header.Depth);
2126 }
2127 }
2128
2129 /*Close_lecture();*/
2130 }
2131 else // Image non compressée
2132 {
2133 for (y_pos=0;(y_pos<context->Height) && (!File_error);y_pos++)
2134 {
2135 if ((width_read=Read_bytes(file,buffer,line_size)))
2136 {
2137 if (PCX_header.Plane==1)
2138 for (x_pos=0; x_pos<context->Width;x_pos++)
2139 Set_pixel(context, x_pos,y_pos,buffer[x_pos]);
2140 else
2141 {
2142 if (PCX_header.Depth==1)
2143 Draw_IFF_line(context, buffer, y_pos,real_line_size,PCX_header.Plane);
2144 else
2145 Draw_PCX_line(context, buffer, y_pos,PCX_header.Depth);
2146 }
2147 }
2148 else
2149 File_error=2;
2150 }
2151 }
2152
2153 free(buffer);
2154 }
2155 }
2156 }
2157 else
2158 {
2159 // Image 24 bits!!!
2160 if (File_error==0)
2161 {
2162 line_size=PCX_header.Bytes_per_plane_line*3;
2163 buffer=(byte *)malloc(line_size);
2164
2165 if (!PCX_header.Compression)
2166 {
2167 for (y_pos=0;(y_pos<context->Height) && (!File_error);y_pos++)
2168 {
2169 if (Read_bytes(file,buffer,line_size))
2170 {
2171 for (x_pos=0; x_pos<context->Width; x_pos++)
2172 Set_pixel_24b(context, x_pos,y_pos,buffer[x_pos+(PCX_header.Bytes_per_plane_line*0)],buffer[x_pos+(PCX_header.Bytes_per_plane_line*1)],buffer[x_pos+(PCX_header.Bytes_per_plane_line*2)]);
2173 }
2174 else
2175 File_error=2;
2176 }
2177 }
2178 else
2179 {
2180 /*Init_lecture();*/
2181
2182 for (y_pos=0,position=0;(y_pos<context->Height) && (!File_error);)
2183 {
2184 // Lecture et décompression de la ligne
2185 if(Read_byte(file,&byte1)!=1) File_error=2;
2186 if (!File_error)
2187 {
2188 if ((byte1 & 0xC0)==0xC0)
2189 {
2190 byte1-=0xC0; // facteur de répétition
2191 if(Read_byte(file,&byte2)!=1) File_error=2; // octet à répéter
2192 if (!File_error)
2193 {
2194 for (index=0; (index<byte1) && (!File_error); index++)
2195 {
2196 buffer[position++]=byte2;
2197 if (position>=line_size)
2198 {
2199 for (x_pos=0; x_pos<context->Width; x_pos++)
2200 Set_pixel_24b(context, x_pos,y_pos,buffer[x_pos+(PCX_header.Bytes_per_plane_line*0)],buffer[x_pos+(PCX_header.Bytes_per_plane_line*1)],buffer[x_pos+(PCX_header.Bytes_per_plane_line*2)]);
2201 y_pos++;
2202 position=0;
2203 }
2204 }
2205 }
2206 }
2207 else
2208 {
2209 buffer[position++]=byte1;
2210 if (position>=line_size)
2211 {
2212 for (x_pos=0; x_pos<context->Width; x_pos++)
2213 Set_pixel_24b(context, x_pos,y_pos,buffer[x_pos+(PCX_header.Bytes_per_plane_line*0)],buffer[x_pos+(PCX_header.Bytes_per_plane_line*1)],buffer[x_pos+(PCX_header.Bytes_per_plane_line*2)]);
2214 y_pos++;
2215 position=0;
2216 }
2217 }
2218 }
2219 }
2220 if (position!=0)
2221 File_error=2;
2222
2223 /*Close_lecture();*/
2224 }
2225 free(buffer);
2226 buffer = NULL;
2227 }
2228 }
2229 }
2230 else
2231 {
2232 File_error=1;
2233 }
2234
2235 fclose(file);
2236 }
2237 else
2238 File_error=1;
2239 }
2240
2241
2242 // -- Ecrire un fichier au format PCX ---------------------------------------
2243
Save_PCX(T_IO_Context * context)2244 void Save_PCX(T_IO_Context * context)
2245 {
2246 FILE *file;
2247
2248 short line_size;
2249 short x_pos;
2250 short y_pos;
2251 byte counter;
2252 byte last_pixel;
2253 byte pixel_read;
2254
2255
2256
2257 File_error=0;
2258
2259 if ((file=Open_file_write(context)))
2260 {
2261 setvbuf(file, NULL, _IOFBF, 64*1024);
2262
2263 PCX_header.Manufacturer=10;
2264 PCX_header.Version=5;
2265 PCX_header.Compression=1;
2266 PCX_header.Depth=8;
2267 PCX_header.X_min=0;
2268 PCX_header.Y_min=0;
2269 PCX_header.X_max=context->Width-1;
2270 PCX_header.Y_max=context->Height-1;
2271 PCX_header.X_dpi=0;
2272 PCX_header.Y_dpi=0;
2273 memcpy(PCX_header.Palette_16c,context->Palette,48);
2274 PCX_header.Reserved=0;
2275 PCX_header.Plane=1;
2276 PCX_header.Bytes_per_plane_line=(context->Width&1)?context->Width+1:context->Width;
2277 PCX_header.Palette_info=1;
2278 PCX_header.Screen_X=Screen_width;
2279 PCX_header.Screen_Y=Screen_height;
2280 memset(PCX_header.Filler,0,54);
2281
2282 if (Write_bytes(file,&(PCX_header.Manufacturer),1) &&
2283 Write_bytes(file,&(PCX_header.Version),1) &&
2284 Write_bytes(file,&(PCX_header.Compression),1) &&
2285 Write_bytes(file,&(PCX_header.Depth),1) &&
2286 Write_word_le(file,PCX_header.X_min) &&
2287 Write_word_le(file,PCX_header.Y_min) &&
2288 Write_word_le(file,PCX_header.X_max) &&
2289 Write_word_le(file,PCX_header.Y_max) &&
2290 Write_word_le(file,PCX_header.X_dpi) &&
2291 Write_word_le(file,PCX_header.Y_dpi) &&
2292 Write_bytes(file,&(PCX_header.Palette_16c),48) &&
2293 Write_bytes(file,&(PCX_header.Reserved),1) &&
2294 Write_bytes(file,&(PCX_header.Plane),1) &&
2295 Write_word_le(file,PCX_header.Bytes_per_plane_line) &&
2296 Write_word_le(file,PCX_header.Palette_info) &&
2297 Write_word_le(file,PCX_header.Screen_X) &&
2298 Write_word_le(file,PCX_header.Screen_Y) &&
2299 Write_bytes(file,&(PCX_header.Filler),54) )
2300 {
2301 line_size=PCX_header.Bytes_per_plane_line*PCX_header.Plane;
2302
2303 for (y_pos=0; ((y_pos<context->Height) && (!File_error)); y_pos++)
2304 {
2305 pixel_read=Get_pixel(context, 0,y_pos);
2306
2307 // Compression et écriture de la ligne
2308 for (x_pos=0; ((x_pos<line_size) && (!File_error)); )
2309 {
2310 x_pos++;
2311 last_pixel=pixel_read;
2312 pixel_read=Get_pixel(context, x_pos,y_pos);
2313 counter=1;
2314 while ( (counter<63) && (x_pos<line_size) && (pixel_read==last_pixel) )
2315 {
2316 counter++;
2317 x_pos++;
2318 pixel_read=Get_pixel(context, x_pos,y_pos);
2319 }
2320
2321 if ( (counter>1) || (last_pixel>=0xC0) )
2322 Write_one_byte(file,counter|0xC0);
2323 Write_one_byte(file,last_pixel);
2324
2325 }
2326 }
2327
2328 // Ecriture de l'octet (12) indiquant que la palette arrive
2329 if (!File_error)
2330 Write_one_byte(file,12);
2331
2332 // Ecriture de la palette
2333 if (!File_error)
2334 {
2335 if (! Write_bytes(file,context->Palette,sizeof(T_Palette)))
2336 File_error=1;
2337 }
2338 }
2339 else
2340 File_error=1;
2341
2342 fclose(file);
2343
2344 if (File_error)
2345 Remove_file(context);
2346
2347 }
2348 else
2349 File_error=1;
2350 }
2351
2352
2353 //////////////////////////////////// SCx ////////////////////////////////////
2354 /**
2355 * @defgroup SCx SCx format
2356 * @ingroup loadsaveformats
2357 * ColoRix VGA Paint SCx File Format
2358 *
2359 * file extensions are sci, scq, scf, scn, sco
2360 * @{
2361 */
2362
2363 /// SCx header data
2364 typedef struct
2365 {
2366 byte Filler1[4]; ///< "RIX3"
2367 word Width; ///< Image Width
2368 word Height; ///< Image Height
2369 byte PaletteType; ///< M P RGB PIX 0xAF = VGA
2370 byte StorageType; ///< 00 = Linear (1 byte per pixel) 01,02 Planar 03 text 80 Compressed 40 extension block 20 encrypted
2371 } T_SCx_Header;
2372
2373 /// Test if a file is SCx format
Test_SCx(T_IO_Context * context,FILE * file)2374 void Test_SCx(T_IO_Context * context, FILE * file)
2375 {
2376 T_SCx_Header SCx_header;
2377
2378 (void)context;
2379 File_error=1;
2380
2381 // read and check header
2382 if (Read_bytes(file,SCx_header.Filler1,4)
2383 && Read_word_le(file, &(SCx_header.Width))
2384 && Read_word_le(file, &(SCx_header.Height))
2385 && Read_byte(file, &(SCx_header.PaletteType))
2386 && Read_byte(file, &(SCx_header.StorageType))
2387 )
2388 {
2389 if ( (!memcmp(SCx_header.Filler1,"RIX",3))
2390 && SCx_header.Width && SCx_header.Height)
2391 File_error=0;
2392 }
2393 }
2394
2395
2396 /// Read a SCx file
Load_SCx(T_IO_Context * context)2397 void Load_SCx(T_IO_Context * context)
2398 {
2399 FILE *file;
2400 word x_pos,y_pos;
2401 long size,real_size;
2402 long file_size;
2403 T_SCx_Header SCx_header;
2404 T_Palette SCx_Palette;
2405 byte * buffer;
2406 byte bpp;
2407
2408 File_error=0;
2409
2410 if ((file=Open_file_read(context)))
2411 {
2412 file_size=File_length_file(file);
2413
2414 if (Read_bytes(file,SCx_header.Filler1,4)
2415 && Read_word_le(file, &(SCx_header.Width))
2416 && Read_word_le(file, &(SCx_header.Height))
2417 && Read_byte(file, &(SCx_header.PaletteType))
2418 && Read_byte(file, &(SCx_header.StorageType))
2419 )
2420 {
2421 bpp = (SCx_header.PaletteType & 7) + 1;
2422 // Bit per RGB component in palette = ((SCx_header.PaletteType >> 3) & 7)
2423 Pre_load(context, SCx_header.Width,SCx_header.Height,file_size,FORMAT_SCx,PIXEL_SIMPLE,bpp);
2424 size=sizeof(T_Components)*(1 << bpp);
2425
2426 if (SCx_header.PaletteType & 0x80)
2427 {
2428 if (!Read_bytes(file, SCx_Palette, size))
2429 File_error = 2;
2430 else
2431 {
2432 if (Config.Clear_palette)
2433 memset(context->Palette,0,sizeof(T_Palette));
2434
2435 Palette_64_to_256(SCx_Palette);
2436 memcpy(context->Palette,SCx_Palette,size);
2437 }
2438 }
2439 if (File_error == 0)
2440 {
2441 if (SCx_header.StorageType == 0x80)
2442 {
2443 GFX2_Log(GFX2_WARNING, "Compressed SCx files are not supported\n");
2444 File_error = 2;
2445 }
2446 else
2447 {
2448 if (SCx_header.StorageType == 0)
2449 { // 256 couleurs (raw)
2450 buffer=(byte *)malloc(context->Width);
2451
2452 for (y_pos=0;(y_pos<context->Height) && (!File_error);y_pos++)
2453 {
2454 if (Read_bytes(file,buffer,context->Width))
2455 for (x_pos=0; x_pos<context->Width;x_pos++)
2456 Set_pixel(context, x_pos,y_pos,buffer[x_pos]);
2457 else
2458 File_error=2;
2459 }
2460 }
2461 else
2462 { // moins de 256 couleurs (planar)
2463 size=((context->Width+7)>>3)*bpp;
2464 real_size=(size/bpp)<<3;
2465 buffer=(byte *)malloc(size);
2466
2467 for (y_pos=0;(y_pos<context->Height) && (!File_error);y_pos++)
2468 {
2469 if (Read_bytes(file,buffer,size))
2470 Draw_IFF_line(context, buffer, y_pos,real_size,bpp);
2471 else
2472 File_error=2;
2473 }
2474 }
2475 free(buffer);
2476 }
2477 }
2478 }
2479 else
2480 File_error=1;
2481
2482 fclose(file);
2483 }
2484 else
2485 File_error=1;
2486 }
2487
2488 /// Save a SCx file
Save_SCx(T_IO_Context * context)2489 void Save_SCx(T_IO_Context * context)
2490 {
2491 FILE *file;
2492 short x_pos,y_pos;
2493 T_SCx_Header SCx_header;
2494 size_t last_char;
2495
2496 // replace the '?' in file extension with the right letter
2497 last_char = strlen(context->File_name) - 1;
2498 if (context->File_name[last_char] == '?')
2499 {
2500 if (context->Width<=320)
2501 context->File_name[last_char]='I';
2502 else
2503 {
2504 if (context->Width<=360)
2505 context->File_name[last_char]='Q';
2506 else
2507 {
2508 if (context->Width<=640)
2509 context->File_name[last_char]='F';
2510 else
2511 {
2512 if (context->Width<=800)
2513 context->File_name[last_char]='N';
2514 else
2515 context->File_name[last_char]='O';
2516 }
2517 }
2518 }
2519 // makes it same case as the previous character
2520 if (last_char > 0)
2521 context->File_name[last_char] |= (context->File_name[last_char - 1] & 32);
2522 // also fix the unicode file name
2523 if (context->File_name_unicode != NULL && context->File_name_unicode[0] != 0)
2524 {
2525 size_t ulen = Unicode_strlen(context->File_name_unicode);
2526 if (ulen > 1)
2527 context->File_name_unicode[ulen - 1] = context->File_name[last_char];
2528 }
2529 }
2530
2531
2532 file = Open_file_write(context);
2533
2534 if (file != NULL)
2535 {
2536 T_Palette palette_64;
2537
2538 File_error = 0;
2539 memcpy(palette_64, context->Palette, sizeof(T_Palette));
2540 Palette_256_to_64(palette_64);
2541
2542 memcpy(SCx_header.Filler1,"RIX3",4);
2543 SCx_header.Width=context->Width;
2544 SCx_header.Height=context->Height;
2545 SCx_header.PaletteType=0xAF;
2546 SCx_header.StorageType=0x00;
2547
2548 if (Write_bytes(file,SCx_header.Filler1, 4)
2549 && Write_word_le(file, SCx_header.Width)
2550 && Write_word_le(file, SCx_header.Height)
2551 && Write_byte(file, SCx_header.PaletteType)
2552 && Write_byte(file, SCx_header.StorageType)
2553 && Write_bytes(file,&palette_64,sizeof(T_Palette))
2554 )
2555 {
2556 for (y_pos=0; ((y_pos<context->Height) && (!File_error)); y_pos++)
2557 for (x_pos=0; x_pos<context->Width; x_pos++)
2558 Write_one_byte(file, Get_pixel(context, x_pos, y_pos));
2559 }
2560 else
2561 {
2562 File_error = 1;
2563 }
2564 fclose(file);
2565 if (File_error)
2566 Remove_file(context);
2567 }
2568 else
2569 {
2570 File_error=1;
2571 }
2572 }
2573
2574 /** @} */
2575
2576 //////////////////////////////////// XPM ////////////////////////////////////
Save_XPM(T_IO_Context * context)2577 void Save_XPM(T_IO_Context* context)
2578 {
2579 // XPM are unix files, so use LF '\n' line endings
2580 FILE* file;
2581 int i,j;
2582 byte max_color = 0;
2583 word color_count;
2584
2585 File_error = 0;
2586
2587 file = Open_file_write(context);
2588 if (file == NULL)
2589 {
2590 File_error = 1;
2591 return;
2592 }
2593 setvbuf(file, NULL, _IOFBF, 64*1024);
2594 // in case there are less colors than 256, we could
2595 // optimize, and use only 1 character per pixel if possible
2596 // printable characters are from 0x20 to 0x7e, minus " 0x22 and \ 0x5c
2597 #define XPM_USABLE_CHARS (0x7f - 0x20 - 2)
2598 for (j = 0; j < context->Height; j++)
2599 for (i = 0; i < context->Width; i++)
2600 {
2601 byte value = Get_pixel(context, i, j);
2602 if (value > max_color)
2603 max_color = value;
2604 }
2605 color_count = (word)max_color + 1;
2606
2607 fprintf(file, "/* XPM */\nstatic char* pixmap[] = {\n");
2608 fprintf(file, "\"%d %d %d %d\",\n", context->Width, context->Height, color_count, color_count > XPM_USABLE_CHARS ? 2 : 1);
2609
2610 if (color_count > XPM_USABLE_CHARS)
2611 {
2612 for (i = 0; i < color_count; i++)
2613 {
2614 if (context->Background_transparent && (i == context->Transparent_color))
2615 fprintf(file, "\"%2.2X\tc None\",\n", i); // None is for transparent color
2616 else
2617 fprintf(file,"\"%2.2X\tc #%2.2x%2.2x%2.2x\",\n", i, context->Palette[i].R, context->Palette[i].G,
2618 context->Palette[i].B);
2619 }
2620
2621 for (j = 0; j < context->Height; j++)
2622 {
2623 fprintf(file, "\"");
2624 for (i = 0; i < context->Width; i++)
2625 fprintf(file, "%2.2X", Get_pixel(context, i, j));
2626 if (j == (context->Height - 1))
2627 fprintf(file,"\"\n");
2628 else
2629 fprintf(file,"\",\n");
2630 }
2631 }
2632 else
2633 {
2634 int c;
2635 for (i = 0; i < color_count; i++)
2636 {
2637 c = (i < 2) ? i + 0x20 : i + 0x21;
2638 if (c >= 0x5c) c++;
2639 if (context->Background_transparent && (i == context->Transparent_color))
2640 fprintf(file, "\"%c\tc None\",\n", c); // None is for transparent color
2641 else
2642 fprintf(file,"\"%c\tc #%2.2x%2.2x%2.2x\",\n", c, context->Palette[i].R, context->Palette[i].G,
2643 context->Palette[i].B);
2644 }
2645
2646 for (j = 0; j < context->Height; j++)
2647 {
2648 fprintf(file, "\"");
2649 for (i = 0; i < context->Width; i++)
2650 {
2651 c = Get_pixel(context, i, j);
2652 c = (c < 2) ? c + 0x20 : c + 0x21;
2653 if (c >= 0x5c) c++;
2654 fprintf(file, "%c", c);
2655 }
2656 if (j == (context->Height - 1))
2657 fprintf(file,"\"\n");
2658 else
2659 fprintf(file,"\",\n");
2660 }
2661 }
2662 fprintf(file, "};\n");
2663
2664 fclose(file);
2665 }
2666