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