/*  Grafx2 - The Ultimate 256-color bitmap paint program
    Copyright 2008 Yves Rizoud
    Copyright 2008 Franck Charlet
    Copyright 2007 Adrien Destugues
    Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud)
    Grafx2 is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; version 2
    of the License.
    Grafx2 is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with Grafx2; if not, see 
*/
#define _XOPEN_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#ifndef __no_pnglib__
#include 
#endif
#include "buttons.h"
#include "const.h"
#include "errors.h"
#include "global.h"
#include "io.h"
#include "loadsave.h"
#include "misc.h"
#include "op_c.h"
#include "pages.h"
#include "palette.h"
#include "sdlscreen.h"
#include "struct.h"
#include "windows.h"
// -- PKM -------------------------------------------------------------------
void Test_PKM(void);
void Load_PKM(void);
void Save_PKM(void);
// -- LBM -------------------------------------------------------------------
void Test_LBM(void);
void Load_LBM(void);
void Save_LBM(void);
// -- GIF -------------------------------------------------------------------
void Test_GIF(void);
void Load_GIF(void);
void Save_GIF(void);
// -- PCX -------------------------------------------------------------------
void Test_PCX(void);
void Load_PCX(void);
void Save_PCX(void);
// -- BMP -------------------------------------------------------------------
void Test_BMP(void);
void Load_BMP(void);
void Save_BMP(void);
// -- IMG -------------------------------------------------------------------
void Test_IMG(void);
void Load_IMG(void);
void Save_IMG(void);
// -- SCx -------------------------------------------------------------------
void Test_SCx(void);
void Load_SCx(void);
void Save_SCx(void);
// -- CEL -------------------------------------------------------------------
void Test_CEL(void);
void Load_CEL(void);
void Save_CEL(void);
// -- KCF -------------------------------------------------------------------
void Test_KCF(void);
void Load_KCF(void);
void Save_KCF(void);
// -- PAL -------------------------------------------------------------------
void Test_PAL(void);
void Load_PAL(void);
void Save_PAL(void);
// -- PI1 -------------------------------------------------------------------
void Test_PI1(void);
void Load_PI1(void);
void Save_PI1(void);
// -- PC1 -------------------------------------------------------------------
void Test_PC1(void);
void Load_PC1(void);
void Save_PC1(void);
// -- NEO -------------------------------------------------------------------
void Test_NEO(void);
void Load_NEO(void);
void Save_NEO(void);
// -- PNG -------------------------------------------------------------------
#ifndef __no_pnglib__
void Test_PNG(void);
void Load_PNG(void);
void Save_PNG(void);
#endif
void Init_preview(short width,short height,long size,int format);
T_Format File_formats[NB_KNOWN_FORMATS] = {
  {"pkm", Test_PKM, Load_PKM, Save_PKM, 1, 1},
  {"lbm", Test_LBM, Load_LBM, Save_LBM, 1, 0},
  {"gif", Test_GIF, Load_GIF, Save_GIF, 1, 1},
  {"bmp", Test_BMP, Load_BMP, Save_BMP, 1, 0},
  {"pcx", Test_PCX, Load_PCX, Save_PCX, 1, 0},
  {"img", Test_IMG, Load_IMG, Save_IMG, 1, 0},
  {"sc?", Test_SCx, Load_SCx, Save_SCx, 1, 0},
  {"pi1", Test_PI1, Load_PI1, Save_PI1, 1, 0},
  {"pc1", Test_PC1, Load_PC1, Save_PC1, 1, 0},
  {"cel", Test_CEL, Load_CEL, Save_CEL, 1, 0},
  {"neo", Test_NEO, Load_NEO, Save_NEO, 1, 0},
  {"kcf", Test_KCF, Load_KCF, Save_KCF, 0, 0},
  {"pal", Test_PAL, Load_PAL, Save_PAL, 0, 0},
#ifndef __no_pnglib__
  {"png", Test_PNG, Load_PNG, Save_PNG, 1, 1}
#endif
};
// Cette variable est alimentée après chargement réussi d'une image.
// Actuellement seul le format PNG peut donner autre chose que PIXEL_SIMPLE.
enum PIXEL_RATIO Ratio_of_loaded_image=PIXEL_SIMPLE;
// Chargement des pixels dans l'écran principal
void Pixel_load_in_current_screen(word x_pos,word y_pos,byte color)
{
  //if ((x_pos>=0) && (y_pos>=0)) //Toujours vrai ?
  if ((x_pos=0) && (y_pos>=0))
  if ((x_pos=0) && (y_pos>=0))
  if ((x_pos> 5) << 5) |
            ((g >> 5) << 2) |
            ((b >> 6));
    Pixel(Preview_pos_X+(x_pos/Preview_factor_X),
          Preview_pos_Y+(y_pos/Preview_factor_Y),
          color);
  }
}
// Création d'une palette fake
void Set_palette_fake_24b(T_Palette palette)
{
  int color;
  // Génération de la palette
  for (color=0;color<256;color++)
  {
    palette[color].R=((color & 0xE0)>>5)<<5;
    palette[color].G=((color & 0x1C)>>2)<<5;
    palette[color].B=((color & 0x03)>>0)<<6;
  }
}
// Initialization for a 24bit image
void Init_preview_24b(short width,short height,long size,int format)
{
  // Call common processing
  Init_preview(width,height,size,format);
  if (File_error)
    return;
  if (Pixel_load_function==Pixel_load_in_preview)
  {
    // Choose 24bit pixel "writer"
    Pixel_load_24b=Pixel_load_in_24b_preview;
    // Load palette
    Set_palette_fake_24b(Main_palette);
    Set_palette(Main_palette);
    Remap_fileselector();
  }
  else
  {
    // Choose 24bit pixel "writer"
    Pixel_load_24b=Pixel_load_in_24b_buffer;
    // Allocate 24bit buffer
    Buffer_image_24b=
      (T_Components *)Borrow_memory_from_page(width*height*sizeof(T_Components));
    if (!Buffer_image_24b)
    {
      // Print an error message
      // The following is to be sure the messagfe is readable
      Compute_optimal_menu_colors(Main_palette);
      Message_out_of_memory();
      if (Pixel_load_function==Pixel_load_in_current_screen)
        File_error=1; // 1 => On n'a pas perdu l'image courante
      else
        File_error=3; // 3 => Chargement de brosse échoué
    }
    else
      Image_24b=1;        // On a un buffer à traiter en fin de chargement
  }
}
void Init_preview(short width,short height,long size,int format)
//
//   Cette procédure doit être appelée par les routines de chargement
// d'images.
//   Elle doit être appelée entre le moment où l'on connait la dimension de
// l'image (dimension réelle, pas dimension tronquée) et l'affichage du
// premier point.
//
{
  char  str[10];
  if (Pixel_load_function==Pixel_load_in_preview)
  {
    // Préparation du chargement d'une preview:
    // Affichage des données "Image size:"
    if ((width<10000) && (height<10000))
    {
      Num2str(width,str,4);
      Num2str(height,str+5,4);
      str[4]='x';
      Print_in_window(143,59,str,MC_Black,MC_Light);
    }
    else
    {
      Print_in_window(143,59,"VERY BIG!",MC_Black,MC_Light);
    }
    // Affichage de la taille du fichier
    if (size<1048576)
    {
      // Le fichier fait moins d'un Mega, on affiche sa taille direct
      Num2str(size,str,7);
      Print_in_window(236,59,str,MC_Black,MC_Light);
    }
    else if ((size/1024)<100000)
    {
      // Le fichier fait plus d'un Mega, on peut afficher sa taille en Ko
      Num2str(size/1024,str,5);
      strcpy(str+5,"Kb");
      Print_in_window(236,59,str,MC_Black,MC_Light);
    }
    else
    {
      // Le fichier fait plus de 100 Mega octets (cas très rare :))
      Print_in_window(236,59,"LARGE!!",MC_Black,MC_Light);
    }
    // Affichage du vrai format
    if (format!=Main_format)
    {
      Print_in_window( 59,59,File_formats[format-1].Extension,MC_Black,MC_Light);
    }
    // On efface le commentaire précédent
    Window_rectangle(45,70,32*8,8,MC_Light);
    // Affichage du commentaire
    if (File_formats[format-1].Comment)
      Print_in_window(45,70,Main_comment,MC_Black,MC_Light);
    // Calculs des données nécessaires à l'affichage de la preview:
    Preview_factor_X=Round_div_max(width,122*Menu_factor_X);
    Preview_factor_Y=Round_div_max(height, 82*Menu_factor_Y);
    if ( (!Config.Maximize_preview) && (Preview_factor_X!=Preview_factor_Y) )
    {
      if (Preview_factor_X>Preview_factor_Y)
        Preview_factor_Y=Preview_factor_X;
      else
        Preview_factor_X=Preview_factor_Y;
    }
    Preview_pos_X=Window_pos_X+183*Menu_factor_X;
    Preview_pos_Y=Window_pos_Y+ 95*Menu_factor_Y;
    // On nettoie la zone où va s'afficher la preview:
    Window_rectangle(183,95,120,80,MC_Light);
    
    // Un update pour couvrir les 4 zones: 3 libellés plus le commentaire
    Update_window_area(45,48,256,30);
    // Zone de preview
    Update_window_area(183,95,120,80);
  }
  else
  {
    if (Pixel_load_function==Pixel_load_in_current_screen)
    {
      if (Backup_with_new_dimensions(0,width,height))
      {
        // La nouvelle page a pu être allouée, elle est pour l'instant pleine
        // de 0s. Elle fait Main_image_width de large.
        // Normalement tout va bien, tout est sous contrôle...
      }
      else
      {
        // Afficher un message d'erreur
        // Pour être sûr que ce soit lisible.
        Compute_optimal_menu_colors(Main_palette);
        Message_out_of_memory();
        File_error=1; // 1 => On n'a pas perdu l'image courante
      }
    }
    else // chargement dans la brosse
    {
      free(Brush);
      free(Smear_brush);
      Brush=(byte *)malloc(width*height);
      Brush_width=width;
      Brush_height=height;
      if (Brush)
      {
        Smear_brush=(byte *)malloc(width*height);
        if (!Smear_brush)
          File_error=3;
      }
      else
        File_error=3;
    }
  }
}
void Draw_palette_preview(void)
{
  short index;
  if (Pixel_load_function==Pixel_load_in_preview)
    for (index=0; index<256; index++)
      Window_rectangle(183+(index/16)*7,95+(index&15)*5,5,5,index);
  Update_window_area(183,95,120,80);
}
// Calcul du nom complet du fichier
void Get_full_filename(char * filename, byte is_colorix_format)
{
    byte last_char;
    strcpy(filename,Main_file_directory);
    //On va ajouter un séparateur à la fin du chemin s'il n'y est pas encore
    if (filename[strlen(filename)-1]!=PATH_SEPARATOR[0])
        strcat(filename,PATH_SEPARATOR);
  // Si on est en train de sauvegarder une image Colorix, on calcule son ext.
  if (is_colorix_format)
  {
    last_char=strlen(Main_filename)-1;
    if (Main_filename[last_char]=='?')
    {
      if (Main_image_width<=320)
        Main_filename[last_char]='I';
      else
      {
        if (Main_image_width<=360)
          Main_filename[last_char]='Q';
        else
        {
          if (Main_image_width<=640)
            Main_filename[last_char]='F';
          else
          {
            if (Main_image_width<=800)
              Main_filename[last_char]='N';
            else
              Main_filename[last_char]='O';
          }
        }
      }
    }
  }
  strcat(filename,Main_filename);
}
/////////////////////////////////////////////////////////////////////////////
//                    Gestion des lectures et écritures                    //
/////////////////////////////////////////////////////////////////////////////
byte * Write_buffer;
word   Write_buffer_index;
void Init_write_buffer(void)
{
  Write_buffer=(byte *)malloc(64000);
  Write_buffer_index=0;
}
void Write_one_byte(FILE *file, byte b)
{
  Write_buffer[Write_buffer_index++]=b;
  if (Write_buffer_index>=64000)
  {
    if (! Write_bytes(file,Write_buffer,64000))
      File_error=1;
    Write_buffer_index=0;
  }
}
void End_write(FILE *file)
{
  if (Write_buffer_index)
    if (! Write_bytes(file,Write_buffer,Write_buffer_index))
      File_error=1;
  free(Write_buffer);
}
/////////////////////////////////////////////////////////////////////////////
// -------- Modifier la valeur du code d'erreur d'accès à un fichier --------
//   On n'est pas obligé d'utiliser cette fonction à chaque fois mais il est
// important de l'utiliser dans les cas du type:
//   if (!File_error) *** else File_error=***;
// En fait, dans le cas où l'on modifie File_error alors qu'elle contient
// dèjà un code d'erreur.
void Set_file_error(int value)
{
  if (File_error>=0)
    File_error=value;
}
// -- Charger n'importe connu quel type de fichier d'image (ou palette) -----
void Load_image(byte image)
{
  int  index; // index de balayage des formats
  int  format=0; // Format du fichier à charger
  // On place par défaut File_error à vrai au cas où on ne sache pas
  // charger le format du fichier:
  File_error=1;
  if (Main_format!=0)
  {
    File_formats[Main_format-1].Test();
    if (!File_error)
      // Si dans le sélecteur il y a un format valide on le prend tout de suite
      format=Main_format-1;
  }
  if (File_error)
  {
    //  Sinon, on va devoir scanner les différents formats qu'on connait pour
    // savoir à quel format est le fichier:
    for (index=0;index0)
    {
      Error(0);
    }
    if (Image_24b)
    {
      // On vient de charger une image 24b
      if (!File_error)
      {
        // Le chargement a réussi, on peut faire la conversion en 256 couleurs
        if (Pixel_load_function==Pixel_load_in_current_screen)
        {
          // Cas d'un chargement dans l'image
          if (Convert_24b_bitmap_to_256(Main_screen,Buffer_image_24b,Main_image_width,Main_image_height,Main_palette))
            File_error=2;
          else
          {
            Set_palette(Main_palette);
          }
        }
        else
        {
          // Cas d'un chargement dans la brosse
          if (Convert_24b_bitmap_to_256(Brush,Buffer_image_24b,Brush_width,Brush_height,Main_palette))
            File_error=2;
        }
        //if (!File_error)
        //  Palette_256_to_64(Main_palette);
        // Normalement plus besoin car 256 color natif, et c'etait probablement
        // un bug - yr
      }
      free(Buffer_image_24b);
    }
    if (image)
    {
      if ( (File_error!=1) && (File_formats[format].Backup_done) )
      {
        if (Pixel_load_function==Pixel_load_in_preview)
        {
          dword  color_usage[256];
          Count_used_colors_area(color_usage,Preview_pos_X,Preview_pos_Y,Main_image_width/Preview_factor_X,Main_image_height/Preview_factor_Y);
          //Count_used_colors(color_usage);
          Display_cursor();
          Set_nice_menu_colors(color_usage,1);
          Hide_cursor();
        }
      
        // On considère que l'image chargée n'est plus modifiée
        Main_image_is_modified=0;
        // Et on documente la variable Main_fileformat avec la valeur:
        Main_fileformat=format+1;
        // Correction des dimensions
        if (Main_image_width<1)
          Main_image_width=1;
        if (Main_image_height<1)
          Main_image_height=1;
      }
      else if (File_error!=1)
      {
        // On considère que l'image chargée est encore modifiée
        Main_image_is_modified=1;
        // Et on documente la variable Main_fileformat avec la valeur:
        Main_fileformat=format+1;
      }
      else
      {
        // Dans ce cas, on sait que l'image n'a pas changé, mais ses
        // paramètres (dimension, palette, ...) si. Donc on les restaures.
        Download_infos_page_main(Main_backups->Pages);
      }
    }
  }
  else
    // Sinon, l'appelant sera au courant de l'échec grace à File_error;
    // et si on s'apprêtait à faire un chargement définitif de l'image (pas
    // une preview), alors on flash l'utilisateur.
    if (Pixel_load_function!=Pixel_load_in_preview)
      Error(0);
}
// -- Sauver n'importe quel type connu de fichier d'image (ou palette) ------
void Save_image(byte image)
{
  // On place par défaut File_error à vrai au cas où on ne sache pas
  // sauver le format du fichier: (Est-ce vraiment utile??? Je ne crois pas!)
  File_error=1;
  Read_pixel_function=(image)?Read_pixel_from_current_screen:Read_pixel_from_brush;
  File_formats[Main_fileformat-1].Save();
  if (File_error)
    Error(0);
  else
  {
    if ((image) && (File_formats[Main_fileformat-1].Backup_done))
      Main_image_is_modified=0;
  }
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// PAL ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// -- Tester si un fichier est au format PAL --------------------------------
void Test_PAL(void)
{
  FILE *file;             // Fichier du fichier
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  long file_size;   // Taille du fichier
  Get_full_filename(filename,0);
  File_error=1;
  // Ouverture du fichier
  if ((file=fopen(filename, "rb")))
  {
    // Lecture de la taille du fichier
    file_size=File_length_file(file);
    fclose(file);
    // Le fichier ne peut être au format PAL que si sa taille vaut 768 octets
    if (file_size==sizeof(T_Palette))
      File_error=0;
  }
}
// -- Lire un fichier au format PAL -----------------------------------------
void Load_PAL(void)
{
  FILE *file;              // Fichier du fichier
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  //long  file_size;   // Taille du fichier
  Get_full_filename(filename,0);
  File_error=0;
  // Ouverture du fichier
  if ((file=fopen(filename, "rb")))
  {
    T_Palette palette_64;
    // Init_preview(???); // Pas possible... pas d'image...
    // Lecture du fichier dans Main_palette
    if (Read_bytes(file,palette_64,sizeof(T_Palette)))
    {
      Palette_64_to_256(palette_64);
      memcpy(Main_palette,palette_64,sizeof(T_Palette));
      Set_palette(Main_palette);
      Remap_fileselector();
      // On dessine une preview de la palette (si chargement=preview)
      Draw_palette_preview();
    }
    else
      File_error=2;
    // Fermeture du fichier
    fclose(file);
  }
  else
    // Si on n'a pas réussi à ouvrir le fichier, alors il y a eu une erreur
    File_error=1;
}
// -- Sauver un fichier au format PAL ---------------------------------------
void Save_PAL(void)
{
  FILE *file;             // Fichier du fichier
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  //long file_size;   // Taille du fichier
  Get_full_filename(filename,0);
  File_error=0;
  // Ouverture du fichier
  if ((file=fopen(filename,"wb")))
  {
    T_Palette palette_64;
    memcpy(palette_64,Main_palette,sizeof(T_Palette));
    Palette_256_to_64(palette_64);
    // Enregistrement de Main_palette dans le fichier
    if (! Write_bytes(file,palette_64,sizeof(T_Palette)))
    {
      File_error=1;
      fclose(file);
      remove(filename);
    }
    else // Ecriture correcte => Fermeture normale du fichier
      fclose(file);
  }
  else // Si on n'a pas réussi à ouvrir le fichier, alors il y a eu une erreur
  {
    File_error=1;
    fclose(file);
    remove(filename);
                     //   On se fout du résultat de l'opération car si ça
                     // renvoie 0 c'est que le fichier avait été partiel-
                     // -lement écrit, sinon pas du tout. Or dans tous les
                     // cas ça revient au même pour nous: Sauvegarde ratée!
  }
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// IMG ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
typedef struct
{
  byte Filler1[6];
  word Width;
  word Height;
  byte Filler2[118];
  T_Palette Palette;
} __attribute__((__packed__)) T_IMG_Header;
// -- Tester si un fichier est au format IMG --------------------------------
void Test_IMG(void)
{
  FILE *file;              // Fichier du fichier
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  T_IMG_Header IMG_header;
  byte signature[6]={0x01,0x00,0x47,0x12,0x6D,0xB0};
  Get_full_filename(filename,0);
  File_error=1;
  // Ouverture du fichier
  if ((file=fopen(filename, "rb")))
  {
    // Lecture et vérification de la signature
    if (Read_bytes(file,&IMG_header,sizeof(T_IMG_Header)))
    {
      if ( (!memcmp(IMG_header.Filler1,signature,6))
        && IMG_header.Width && IMG_header.Height)
        File_error=0;
    }
    // Fermeture du fichier
    fclose(file);
  }
}
// -- Lire un fichier au format IMG -----------------------------------------
void Load_IMG(void)
{
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  byte * buffer;
  FILE *file;
  word x_pos,y_pos;
  long width_read;
  long file_size;
  T_IMG_Header IMG_header;
  Get_full_filename(filename,0);
  File_error=0;
  if ((file=fopen(filename, "rb")))
  {
    file_size=File_length_file(file);
    if (Read_bytes(file,&IMG_header,sizeof(T_IMG_Header)))
    {
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    IMG_header.Width = SDL_Swap16(IMG_header.Width);
    IMG_header.Height = SDL_Swap16(IMG_header.Height);
#endif
      buffer=(byte *)malloc(IMG_header.Width);
      Init_preview(IMG_header.Width,IMG_header.Height,file_size,FORMAT_IMG);
      if (File_error==0)
      {
        memcpy(Main_palette,IMG_header.Palette,sizeof(T_Palette));
        Set_palette(Main_palette);
        Remap_fileselector();
        Main_image_width=IMG_header.Width;
        Main_image_height=IMG_header.Height;
        width_read=IMG_header.Width;
        for (y_pos=0;(y_posCOMMENT_SIZE)
                  {
                    color=temp_byte;              // On se sert de color comme
                    temp_byte=COMMENT_SIZE;   // variable temporaire
                    color-=COMMENT_SIZE;
                  }
                  else
                    color=0;
                  if (Read_bytes(file,Main_comment,temp_byte))
                  {
                    index+=temp_byte;
                    Main_comment[temp_byte]='\0';
                    if (color)
                      if (fseek(file,color,SEEK_CUR))
                        File_error=2;
                  }
                  else
                    File_error=2;
                }
                else
                  File_error=2;
                break;
              case 1 : // Dimensions de l'écran d'origine
                if (Read_byte(file,&temp_byte))
                {
                  if (temp_byte==4)
                  {
                    index+=4;
                    if ( ! Read_word_le(file,(word *) &Original_screen_X)
                      || !Read_word_le(file,(word *) &Original_screen_Y) )
                      File_error=2;
                  }
                  else
                    File_error=2;
                }
                else
                  File_error=2;
                break;
              case 2 : // color de transparence
                if (Read_byte(file,&temp_byte))
                {
                  if (temp_byte==1)
                  {
                    index++;
                    if (! Read_byte(file,&Back_color))
                      File_error=2;
                  }
                  else
                    File_error=2;
                }
                else
                  File_error=2;
                break;
              default:
                if (Read_byte(file,&temp_byte))
                {
                  index+=temp_byte;
                  if (fseek(file,temp_byte,SEEK_CUR))
                    File_error=2;
                }
                else
                  File_error=2;
            }
          }
          else
            File_error=2;
        }
        if ( (!File_error) && (index!=header.Jump) )
          File_error=2;
      }
      /*Init_lecture();*/
      if (!File_error)
      {
        Init_preview(header.Width,header.Height,file_size,FORMAT_PKM);
        if (File_error==0)
        {
          
          Main_image_width=header.Width;
          Main_image_height=header.Height;
          image_size=(dword)(Main_image_width*Main_image_height);
          // Palette lue en 64
          memcpy(Main_palette,header.Palette,sizeof(T_Palette));
          Palette_64_to_256(Main_palette);
          Set_palette(Main_palette);
          Remap_fileselector();
          Compteur_de_donnees_packees=0;
          Compteur_de_pixels=0;
          Taille_pack=(file_size)-sizeof(T_PKM_Header)-header.Jump;
          // Boucle de décompression:
          while ( (Compteur_de_pixels>8);
      Write_one_byte(file,Screen_height&0xFF);
      Write_one_byte(file,Screen_height>>8);
      // Ecriture de la back-color
      Write_one_byte(file,2);
      Write_one_byte(file,1);
      Write_one_byte(file,Back_color);
      // Routine de compression PKM de l'image
      image_size=(dword)(Main_image_width*Main_image_height);
      Compteur_de_pixels=0;
      pixel_value=Read_pixel_function(0,0);
      while ( (Compteur_de_pixels=image_size) break;
          pixel_value=Read_pixel_function(Compteur_de_pixels % Main_image_width,Compteur_de_pixels / Main_image_width);
        }
        if ( (last_color!=header.recog1) && (last_color!=header.recog2) )
        {
          if (repetitions==1)
            Write_one_byte(file,last_color);
          else
          if (repetitions==2)
          {
            Write_one_byte(file,last_color);
            Write_one_byte(file,last_color);
          }
          else
          if ( (repetitions>2) && (repetitions<256) )
          { // RECON1/couleur/nombre
            Write_one_byte(file,header.recog1);
            Write_one_byte(file,last_color);
            Write_one_byte(file,repetitions&0xFF);
          }
          else
          if (repetitions>=256)
          { // RECON2/couleur/hi(nombre)/lo(nombre)
            Write_one_byte(file,header.recog2);
            Write_one_byte(file,last_color);
            Write_one_byte(file,repetitions>>8);
            Write_one_byte(file,repetitions&0xFF);
          }
        }
        else
        {
          if (repetitions<256)
          {
            Write_one_byte(file,header.recog1);
            Write_one_byte(file,last_color);
            Write_one_byte(file,repetitions&0xFF);
          }
          else
          {
            Write_one_byte(file,header.recog2);
            Write_one_byte(file,last_color);
            Write_one_byte(file,repetitions>>8);
            Write_one_byte(file,repetitions&0xFF);
          }
        }
      }
      End_write(file);
    }
    else
      File_error=1;
    fclose(file);
  }
  else
  {
    File_error=1;
    fclose(file);
  }
  //   S'il y a eu une erreur de sauvegarde, on ne va tout de même pas laisser
  // ce fichier pourri traîner... Ca fait pas propre.
  if (File_error)
    remove(filename);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// LBM ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
typedef struct
{
  word  Width;
  word  Height;
  word  X_org;       // Inutile
  word  Y_org;       // Inutile
  byte  BitPlanes;
  byte  Mask;
  byte  Compression;
  byte  Pad1;       // Inutile
  word  Transp_col;
  byte  X_aspect;    // Inutile
  byte  Y_aspect;    // Inutile
  word  X_screen;
  word  Y_screen;
} __attribute__((__packed__)) T_LBM_Header;
byte * LBM_buffer;
FILE *LBM_file;
// -- Tester si un fichier est au format LBM --------------------------------
void Test_LBM(void)
{
  char filename[MAX_PATH_CHARACTERS];
  char  format[4];
  char  section[4];
  dword dummy;
  Get_full_filename(filename,0);
  File_error=0;
  if ((LBM_file=fopen(filename, "rb")))
  {
    if (! Read_bytes(LBM_file,section,4))
      File_error=1;
    else
    if (memcmp(section,"FORM",4))
      File_error=1;
    else
    {
      Read_dword_be(LBM_file, &dummy);
                   //   On aurait pu vérifier que ce long est égal à la taille
                   // du fichier - 8, mais ça aurait interdit de charger des
                   // fichiers tronqués (et déjà que c'est chiant de perdre
                   // une partie du fichier il faut quand même pouvoir en
                   // garder un peu... Sinon, moi je pleure :'( !!! )
      if (! Read_bytes(LBM_file,format,4))
        File_error=1;
      else
      if ( (memcmp(format,"ILBM",4)) && (memcmp(format,"PBM ",4)) )
        File_error=1;
    }
    fclose(LBM_file);
  }
  else
    File_error=1;
}
// -- Lire un fichier au format LBM -----------------------------------------
  byte Image_HAM;
  // ---------------- Adapter la palette pour les images HAM ----------------
  void Adapt_palette_HAM(void)
  {
    short i,j,temp;
    byte  color;
    if (Image_HAM==6)
    {
      for (i=1; i<=14; i++)
      {
        // On recopie a palette de base
        memcpy(Main_palette+(i<<4),Main_palette,48);
        // On modifie les teintes de cette palette
        for (j=0; j<16; j++)
        {
          color=(i<<4)+j;
          if (i<=7)
          {
            if (i&1)
            {
              temp=Main_palette[j].R+16;
              Main_palette[color].R=(temp<63)?temp:63;
            }
            if (i&2)
            {
              temp=Main_palette[j].G+16;
              Main_palette[color].G=(temp<63)?temp:63;
            }
            if (i&4)
            {
              temp=Main_palette[j].B+16;
              Main_palette[color].B=(temp<63)?temp:63;
            }
          }
          else
          {
            if ((i-7)&1)
            {
              temp=Main_palette[j].R-16;
              Main_palette[color].R=(temp>=0)?temp:0;
            }
            if ((i-7)&2)
            {
              temp=Main_palette[j].G-16;
              Main_palette[color].G=(temp>=0)?temp:0;
            }
            if ((i-7)&4)
            {
              temp=Main_palette[j].B-16;
              Main_palette[color].B=(temp>=0)?temp:0;
            }
          }
        }
      }
      // Ici, il reste les 16 dernières couleurs à modifier
      for (i=240,j=0; j<16; i++,j++)
      {
        temp=Main_palette[j].R+8;
        Main_palette[i].R=(temp<63)?temp:63;
        temp=Main_palette[j].G+8;
        Main_palette[i].G=(temp<63)?temp:63;
        temp=Main_palette[j].B+8;
        Main_palette[i].B=(temp<63)?temp:63;
      }
    }
    else if (Image_HAM==8)
    {
      for (i=1; i<=3; i++)
      {
        // On recopie la palette de base
        memcpy(Main_palette+(i<<6),Main_palette,192);
        // On modifie les teintes de cette palette
        for (j=0; j<64; j++)
        {
          color=(i<<6)+j;
          switch (i)
          {
            case 1 :
              temp=Main_palette[j].R+16;
              Main_palette[color].R=(temp<63)?temp:63;
              break;
            case 2 :
              temp=Main_palette[j].G+16;
              Main_palette[color].G=(temp<63)?temp:63;
              break;
            default:
              temp=Main_palette[j].B+16;
              Main_palette[color].B=(temp<63)?temp:63;
          }
        }
      }
    }
    else // Image 64 couleurs sauvée en 32.
    {
      for (i=0; i<32; i++)
      {
        j=i+32;
        Main_palette[j].R=Main_palette[i].R>>1;
        Main_palette[j].G=Main_palette[i].G>>1;
        Main_palette[j].B=Main_palette[i].B>>1;
      }
    }
  }
  // ------------------------- Attendre une section -------------------------
  byte Wait_for(byte * expected_section)
  {
    // Valeur retournée: 1=Section trouvée, 0=Section non trouvée (erreur)
    dword Taille_section;
    byte section_read[4];
    if (! Read_bytes(LBM_file,section_read,4))
      return 0;
    while (memcmp(section_read,expected_section,4)) // Sect. pas encore trouvée
    {
      if (!Read_dword_be(LBM_file,&Taille_section))
        return 0;
      if (Taille_section&1)
        Taille_section++;
      if (fseek(LBM_file,Taille_section,SEEK_CUR))
        return 0;
      if (! Read_bytes(LBM_file,section_read,4))
        return 0;
    }
    return 1;
  }
// Les images ILBM sont stockés en bitplanes donc on doit trifouiller les bits pour
// en faire du chunky
byte Color_ILBM_line(word x_pos, word real_line_size, byte HBPm1)
{
  // Renvoie la couleur du pixel (ILBM) en x_pos.
  // CL sera le rang auquel on extrait les bits de la couleur
  byte cl = 7 - (x_pos & 7);
  int ax,bh,dx;
  byte bl=0;
  for(dx=HBPm1;dx>=0;dx--)
  {
  //CIL_Loop
    ax = (real_line_size * dx + x_pos) >> 3;
    bh = (LBM_buffer[ax] >> cl) & 1;
    bl = (bl << 1) + bh;
  }
  return bl;
}
  // ----------------------- Afficher une ligne ILBM ------------------------
  void Draw_ILBM_line(short y_pos, short real_line_size)
  {
    byte  color;
    byte  red,green,blue;
    byte  temp;
    short x_pos;
    if (Image_HAM<=1)                                               // ILBM
    {
      for (x_pos=0; x_pos>2;
            color=Best_color(red,green,blue);
            break;
          case 0x02: // red
            red=temp>>2;
            color=Best_color(red,green,blue);
            break;
          case 0x03: // green
            green=temp>>2;
            color=Best_color(red,green,blue);
            break;
          default:   // Nouvelle couleur
            color=temp;
            red=Main_palette[color].R;
            green =Main_palette[color].G;
            blue =Main_palette[color].B;
        }
        Pixel_load_function(x_pos,y_pos,color);
      }
    }
  }
void Load_LBM(void)
{
  char filename[MAX_PATH_CHARACTERS];
  T_LBM_Header header;
  char  format[4];
  char  section[4];
  byte  temp_byte;
  short b256;
  dword nb_colors;
  dword image_size;
  short x_pos;
  short y_pos;
  short counter;
  short line_size;       // Taille d'une ligne en octets
  short real_line_size; // Taille d'une ligne en pixels
  byte  color;
  long  file_size;
  dword dummy;
  Get_full_filename(filename,0);
  File_error=0;
  if ((LBM_file=fopen(filename, "rb")))
  {
    file_size=File_length_file(LBM_file);
    // On avance dans le fichier (pas besoin de tester ce qui l'a déjà été)
    Read_bytes(LBM_file,section,4);
    Read_dword_be(LBM_file,&dummy);
    Read_bytes(LBM_file,format,4);
    if (!Wait_for((byte *)"BMHD"))
      File_error=1;
    Read_dword_be(LBM_file,&dummy);
    // Maintenant on lit le header pour pouvoir commencer le chargement de l'image
    if ( (Read_word_be(LBM_file,&header.Width))
      && (Read_word_be(LBM_file,&header.Height))
      && (Read_word_be(LBM_file,&header.X_org))
      && (Read_word_be(LBM_file,&header.Y_org))
      && (Read_byte(LBM_file,&header.BitPlanes))
      && (Read_byte(LBM_file,&header.Mask))
      && (Read_byte(LBM_file,&header.Compression))
      && (Read_byte(LBM_file,&header.Pad1))
      && (Read_word_be(LBM_file,&header.Transp_col))
      && (Read_byte(LBM_file,&header.X_aspect))
      && (Read_byte(LBM_file,&header.Y_aspect))
      && (Read_word_be(LBM_file,&header.X_screen))
      && (Read_word_be(LBM_file,&header.Y_screen))
      && header.Width && header.Height)
    {
      if ( (header.BitPlanes) && (Wait_for((byte *)"CMAP")) )
      {
        Read_dword_be(LBM_file,&nb_colors);
        nb_colors/=3;
        if (((dword)1< il faut copier les 32 coul.
          }              // sur les 32 suivantes et assombrir ces dernières.
          else
          {
            if ((header.BitPlanes==6) || (header.BitPlanes==8))
              Image_HAM=header.BitPlanes;
            else
              /* File_error=1;*/  /* C'est censé être incorrect mais j'ai */
              Image_HAM=0;            /* trouvé un fichier comme ça, alors... */
          }
        }
        else
          Image_HAM=0;
        if ( (!File_error) && (nb_colors>=2) && (nb_colors<=256) )
        {
          HBPm1=header.BitPlanes-1;
          if (header.Mask==1)
            header.BitPlanes++;
          // Deluxe paint le fait... alors on le fait...
          Back_color=header.Transp_col;
          // On commence par passer la palette en 256 comme ça, si la nouvelle
          // palette a moins de 256 coul, la précédente ne souffrira pas d'un
          // assombrissement préjudiciable.
          if (Config.Clear_palette)
            memset(Main_palette,0,sizeof(T_Palette));
          else
            Palette_64_to_256(Main_palette);
          //   On peut maintenant charger la nouvelle palette
          if (Read_bytes(LBM_file,Main_palette,3*nb_colors))
          {
            Palette_256_to_64(Main_palette);
            if (Image_HAM)
              Adapt_palette_HAM();
            Palette_64_to_256(Main_palette);
            Set_palette(Main_palette);
            Remap_fileselector();
            // On lit l'octet de padding du CMAP si la taille est impaire
            if (nb_colors&1)
              if (Read_byte(LBM_file,&temp_byte))
                File_error=2;
            if ( (Wait_for((byte *)"BODY")) && (!File_error) )
            {
              Read_dword_be(LBM_file,&image_size);
              //swab((char *)&header.Width ,(char *)&Main_image_width,2);
              //swab((char *)&header.Height,(char *)&Main_image_height,2);
              Main_image_width = header.Width;
              Main_image_height = header.Height;
              //swab((char *)&header.X_screen,(char *)&Original_screen_X,2);
              //swab((char *)&header.Y_screen,(char *)&Original_screen_Y,2);
              Original_screen_X = header.X_screen;
              Original_screen_Y = header.Y_screen;
              Init_preview(Main_image_width,Main_image_height,file_size,FORMAT_LBM);
              if (File_error==0)
              {
                if (!memcmp(format,"ILBM",4))    // "ILBM": InterLeaved BitMap
                {
                  // Calcul de la taille d'une ligne ILBM (pour les images ayant des dimensions exotiques)
                  if (Main_image_width & 15)
                  {
                    real_line_size=( (Main_image_width+16) >> 4 ) << 4;
                    line_size=( (Main_image_width+16) >> 4 )*(header.BitPlanes<<1);
                  }
                  else
                  {
                    real_line_size=Main_image_width;
                    line_size=(Main_image_width>>3)*header.BitPlanes;
                  }
                  if (!header.Compression)
                  {                                           // non compressé
                    LBM_buffer=(byte *)malloc(line_size);
                    for (y_pos=0; ((y_pos 127 alors il faut répéter 256-'temp_byte' fois la couleur de l'octet suivant
                        // Si temp_byte <= 127 alors il faut afficher directement les 'temp_byte' octets suivants
                        if (temp_byte>127)
                        {
							if(Read_byte(LBM_file, &color)!=1)
							{
								File_error=2;
								break;
							}
                          b256=(short)(256-temp_byte);
                          for (counter=0; counter<=b256; counter++)
                            if (x_pos=line_size || Read_byte(LBM_file, &(LBM_buffer[x_pos++]))!=1)
								File_error=2;
                      }
                      if (!File_error)
                        Draw_ILBM_line(y_pos,real_line_size);
                    }
                    free(LBM_buffer);
                    /*Close_lecture();*/
                  }
                }
                else                               // "PBM ": Planar(?) BitMap
                {
                  real_line_size=Main_image_width+(Main_image_width&1);
                  if (!header.Compression)
                  {                                           // non compressé
                    LBM_buffer=(byte *)malloc(real_line_size);
                    for (y_pos=0; ((y_pos127)
                        {
                          if(Read_byte(LBM_file, &color)!=1)
						  {
							File_error=2;
							break;
						  }
                          b256=256-temp_byte;
                          for (counter=0; counter<=b256; counter++)
                            Pixel_load_function(x_pos++,y_pos,color);
                        }
                        else
                          for (counter=0; counter<=temp_byte; counter++)
                          {
                            byte byte_read=0;
                            if(Read_byte(LBM_file, &byte_read)!=1)
							{
								File_error=2;
								break;
							}
                            Pixel_load_function(x_pos++,y_pos,byte_read);
                          }
                      }
                    }
                    /*Close_lecture();*/
                  }
                }
              }
            }
            else
              Set_file_error(2);
          }
          else
          {
            File_error=1;
          }
        }
        else
          Set_file_error(1);
      }
      else
        File_error=1;
    }
    else
      File_error=1;
    fclose(LBM_file);
  }
  else
    File_error=1;
}
// -- Sauver un fichier au format LBM ---------------------------------------
  byte LBM_color_list[129];
  word LBM_list_size;
  byte LBM_repetition_mode;
  // ------------- Ecrire les couleurs que l'on vient de traiter ------------
  void Transfer_colors(void)
  {
    byte index;
    if (LBM_list_size>0)
    {
      if (LBM_repetition_mode)
      {
        Write_one_byte(LBM_file,257-LBM_list_size);
        Write_one_byte(LBM_file,LBM_color_list[0]);
      }
      else
      {
        Write_one_byte(LBM_file,LBM_list_size-1);
        for (index=0; index et on a 3 couleurs qui se suivent
          {
            LBM_list_size-=2;
            Transfer_colors();
            LBM_color_list[0]=color;
            LBM_color_list[1]=color;
            LBM_color_list[2]=color;
            LBM_list_size=3;
            LBM_repetition_mode=1;
          }
        }
        else // La couleur n'est pas la même que la précédente
        {
          if (!LBM_repetition_mode)                 // On conserve le mode...
          {
            LBM_color_list[LBM_list_size++]=color;
            if (LBM_list_size==128)
              Transfer_colors();
          }
          else                                        // On change de mode...
          {
            Transfer_colors();
            LBM_color_list[LBM_list_size]=color;
            LBM_list_size++;
          }
        }
    }
  }
void Save_LBM(void)
{
  char filename[MAX_PATH_CHARACTERS];
  T_LBM_Header header;
  word x_pos;
  word y_pos;
  byte temp_byte;
  word real_width;
  int file_size;
  File_error=0;
  Get_full_filename(filename,0);
  // Ouverture du fichier
  if ((LBM_file=fopen(filename,"wb")))
  {
    Write_bytes(LBM_file,"FORM",4);
    Write_dword_be(LBM_file,0); // On mettra la taille à jour à la fin
    Write_bytes(LBM_file,"PBM BMHD",8);
    Write_dword_be(LBM_file,20);
    // On corrige la largeur de l'image pour qu'elle soit multiple de 2
    real_width=Main_image_width+(Main_image_width&1);
    //swab((byte *)&real_width,(byte *)&header.Width,2);
    header.Width=Main_image_width;
    header.Height=Main_image_height;
    header.X_org=0;
    header.Y_org=0;
    header.BitPlanes=8;
    header.Mask=0;
    header.Compression=1;
    header.Pad1=0;
    header.Transp_col=Back_color;
    header.X_aspect=1;
    header.Y_aspect=1;
    header.X_screen = Screen_width;
    header.Y_screen = Screen_height;
    Write_word_be(LBM_file,header.Width);
    Write_word_be(LBM_file,header.Height);
    Write_word_be(LBM_file,header.X_org);
    Write_word_be(LBM_file,header.Y_org);
    Write_bytes(LBM_file,&header.BitPlanes,1);
    Write_bytes(LBM_file,&header.Mask,1);
    Write_bytes(LBM_file,&header.Compression,1);
    Write_bytes(LBM_file,&header.Pad1,1);
    Write_word_be(LBM_file,header.Transp_col);
    Write_bytes(LBM_file,&header.X_aspect,1);
    Write_bytes(LBM_file,&header.Y_aspect,1);
    Write_word_be(LBM_file,header.X_screen);
    Write_word_be(LBM_file,header.Y_screen);
    Write_bytes(LBM_file,"CMAP",4);
    Write_dword_be(LBM_file,sizeof(T_Palette));
    Write_bytes(LBM_file,Main_palette,sizeof(T_Palette));
    Write_bytes(LBM_file,"BODY",4);
    Write_dword_be(LBM_file,0); // On mettra la taille à jour à la fin
    Init_write_buffer();
    LBM_list_size=0;
    for (y_pos=0; ((y_pos on efface le fichier
      remove(filename);
  }
  else
    File_error=1;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// BMP ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
typedef struct
{
    word  Signature;   // ='BM' = 0x4D42
    dword Size_1;    // =Taille du fichier
    word  Reserved_1;    // =0
    word  Reserved_2;    // =0
    dword Offset;    // Nb octets avant les données bitmap
    dword Size_2;    // =40 
    dword Width;
    dword Height;
    word  Planes;       // =1
    word  Nb_bits;     // =1,4,8 ou 24
    dword Compression;
    dword Size_3;
    dword XPM;
    dword YPM;
    dword Nb_Clr;
    dword Clr_Imprt;
} __attribute__((__packed__)) T_BMP_Header;
// -- Tester si un fichier est au format BMP --------------------------------
void Test_BMP(void)
{
  char filename[MAX_PATH_CHARACTERS];
  FILE *file;
  T_BMP_Header header;
  File_error=1;
  Get_full_filename(filename,0);
  if ((file=fopen(filename, "rb")))
  {
    if (Read_bytes(file,&(header.Signature),2) // "BM"
     && Read_dword_le(file,&(header.Size_1))
     && Read_word_le(file,&(header.Reserved_1))
     && Read_word_le(file,&(header.Reserved_2))
     && Read_dword_le(file,&(header.Offset))
     && Read_dword_le(file,&(header.Size_2))
     && Read_dword_le(file,&(header.Width))
     && Read_dword_le(file,&(header.Height))
     && Read_word_le(file,&(header.Planes))
     && Read_word_le(file,&(header.Nb_bits))
     && Read_dword_le(file,&(header.Compression))
     && Read_dword_le(file,&(header.Size_3))
     && Read_dword_le(file,&(header.XPM))
     && Read_dword_le(file,&(header.YPM))
     && Read_dword_le(file,&(header.Nb_Clr))
     && Read_dword_le(file,&(header.Clr_Imprt))
        )
     {
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
      header.Signature = SDL_Swap16(header.Signature);
#endif
      if ( (header.Signature==0x4D42) && (header.Size_2==40)
        && header.Width && header.Height )
        File_error=0;
     }
    fclose(file);
  }
}
// Find the 8 important bits in a dword
byte Bitmap_mask(dword pixel, dword mask)
{
  byte result;
  int i;
  int bits_found;
  switch(mask)
  {
    // Shortcuts to quickly handle the common 24/32bit cases
    case 0x000000FF:
      return (pixel & 0x000000FF);
    case 0x0000FF00:
      return (pixel & 0x0000FF00)>>8;
    case 0x00FF0000:
      return (pixel & 0x00FF0000)>>16;
    case 0xFF000000:
      return (pixel & 0xFF000000)>>24;
  }
  // Uncommon : do it bit by bit.
  bits_found=0;
  result=0;
  // Process the mask from low to high bit
  for (i=0;i<32;i++)
  {
    // Found a bit in the mask
    if (mask & (1<=8)
        return result;
    }
  }
  // Less than 8 bits in the mask: scale the result to 8 bits
  return result << (8-bits_found);
}
// -- Charger un fichier au format BMP --------------------------------------
void Load_BMP(void)
{
  char filename[MAX_PATH_CHARACTERS];
  FILE *file;
  T_BMP_Header header;
  byte * buffer;
  word  index;
  byte  local_palette[256][4]; // R,G,B,0
  word  nb_colors =  0;
  short x_pos;
  short y_pos;
  word  line_size;
  byte  a,b,c=0;
  long  file_size;
  Get_full_filename(filename,0);
  File_error=0;
  if ((file=fopen(filename, "rb")))
  {
    file_size=File_length_file(file);
    if (Read_word_le(file,&(header.Signature))
     && Read_dword_le(file,&(header.Size_1))
     && Read_word_le(file,&(header.Reserved_1))
     && Read_word_le(file,&(header.Reserved_2))
     && Read_dword_le(file,&(header.Offset))
     && Read_dword_le(file,&(header.Size_2))
     && Read_dword_le(file,&(header.Width))
     && Read_dword_le(file,&(header.Height))
     && Read_word_le(file,&(header.Planes))
     && Read_word_le(file,&(header.Nb_bits))
     && Read_dword_le(file,&(header.Compression))
     && Read_dword_le(file,&(header.Size_3))
     && Read_dword_le(file,&(header.XPM))
     && Read_dword_le(file,&(header.YPM))
     && Read_dword_le(file,&(header.Nb_Clr))
     && Read_dword_le(file,&(header.Clr_Imprt))
    )
    {
      switch (header.Nb_bits)
      {
        case 1 :
        case 4 :
        case 8 :
          if (header.Nb_Clr)
            nb_colors=header.Nb_Clr;
          else
            nb_colors=1<>3;
                buffer=(byte *)malloc(line_size);
                for (y_pos=Main_image_height-1; ((y_pos>=0) && (!File_error)); y_pos--)
                {
                  if (Read_bytes(file,buffer,line_size))
                    for (x_pos=0; x_pos>1] & 0xF);
                          else
                            Pixel_load_function(x_pos,y_pos,buffer[x_pos>>1] >> 4 );
                          break;
                        case 1 :
                          if ( buffer[x_pos>>3] & (0x80>>(x_pos&7)) )
                            Pixel_load_function(x_pos,y_pos,1);
                          else
                            Pixel_load_function(x_pos,y_pos,0);
                      }
                  else
                    File_error=2;
                }
                free(buffer);
                break;
              case 1 : // Compression RLE 8 bits
                x_pos=0;
                y_pos=Main_image_height-1;
                /*Init_lecture();*/
                if(Read_byte(file, &a)!=1 || Read_byte(file, &b)!=1)
					File_error=2;
                while (!File_error)
                {
                  if (a) // Encoded mode
                    for (index=1; index<=a; index++)
                      Pixel_load_function(x_pos++,y_pos,b);
                  else   // Absolute mode
                    switch (b)
                    {
                      case 0 : // End of line
                        x_pos=0;
                        y_pos--;
                        break;
                      case 1 : // End of bitmap
                        break;
                      case 2 : // Delta
                		if(Read_byte(file, &a)!=1 || Read_byte(file, &b)!=1)
							File_error=2;
                        x_pos+=a;
                        y_pos-=b;
                        break;
                      default: // Nouvelle série
                        while (b)
                        {
                          if(Read_byte(file, &a)!=1)
							  File_error=2;
                          //Read_one_byte(file, &c);
                          Pixel_load_function(x_pos++,y_pos,a);
                          //if (--c)
                          //{
                          //  Pixel_load_function(x_pos++,y_pos,c);
                          //  b--;
                          //}
                          b--;
                        }
                        if (ftell(file) & 1) fseek(file, 1, SEEK_CUR);
                    }
                  if (a==0 && b==1)
                    break;
                  if(Read_byte(file, &a) !=1 || Read_byte(file, &b)!=1)
				  {
					File_error=2;
				  }
                }
                /*Close_lecture();*/
                break;
              case 2 : // Compression RLE 4 bits
                x_pos=0;
                y_pos=Main_image_height-1;
                /*Init_lecture();*/
                if(Read_byte(file, &a)!=1 ||  Read_byte(file, &b) != 1)
					File_error =2;
                while ( (!File_error) && ((a)||(b!=1)) )
                {
                  if (a) // Encoded mode (A fois les 1/2 pixels de B)
                    for (index=1; index<=a; index++)
                    {
                      if (index & 1)
                        Pixel_load_function(x_pos,y_pos,b>>4);
                      else
                        Pixel_load_function(x_pos,y_pos,b&0xF);
                      x_pos++;
                    }
                  else   // Absolute mode
                    switch (b)
                    {
                      case 0 : //End of line
                        x_pos=0;
                        y_pos--;
                        break;
                      case 1 : // End of bitmap
                        break;
                      case 2 : // Delta
                       if(Read_byte(file, &a)!=1 ||  Read_byte(file, &b)!=1)
						   File_error=2;
                        x_pos+=a;
                        y_pos-=b;
                        break;
                      default: // Nouvelle série (B 1/2 pixels bruts)
                        for (index=1; ((index<=b) && (!File_error)); index++,x_pos++)
                        {
                          if (index&1)
                          {
                            if(Read_byte(file, &c)!=1) File_error=2;
                            Pixel_load_function(x_pos,y_pos,c>>4);
                          }
                          else
                            Pixel_load_function(x_pos,y_pos,c&0xF);
                        }
                        //   On lit l'octet rendant le nombre d'octets pair, si
                        // nécessaire. Encore un truc de crétin "made in MS".
                        if ( ((b&3)==1) || ((b&3)==2) )
                        {
                          byte dummy;
                          if(Read_byte(file, &dummy)!=1) File_error=2;
                        }
                    }
                  if(Read_byte(file, &a)!=1 || Read_byte(file, &b)!=1) File_error=2;
                }
                /*Close_lecture();*/
            }
            fclose(file);
          }
          else
          {
            fclose(file);
            File_error=1;
          }
        }
      }
      else
      {
        // Image 16/24/32 bits
        dword red_mask;
        dword green_mask;
        dword blue_mask;
        if (header.Nb_bits == 16)
        {
          red_mask =   0x00007C00;
          green_mask = 0x000003E0;
          blue_mask =  0x0000001F;
        }
        else
        {
          red_mask = 0x00FF0000;
          green_mask = 0x0000FF00;
          blue_mask = 0x000000FF;
        }
        File_error=0;
        Main_image_width=header.Width;
        Main_image_height=header.Height;
        Init_preview_24b(header.Width,header.Height,file_size,FORMAT_BMP);
        if (File_error==0)
        {
          switch (header.Compression)
          {
            case 3: // BI_BITFIELDS
              if (!Read_dword_le(file,&red_mask) ||
                  !Read_dword_le(file,&green_mask) ||
                  !Read_dword_le(file,&blue_mask))
                File_error=2;
              break;
            default:
              break;
          }
          if (fseek(file, header.Offset, SEEK_SET))
            File_error=2;
        }
        if (File_error==0)
        {
          switch (header.Nb_bits)
          {
            // 24bit bitmap
            default:
            case 24:
              line_size=Main_image_width*3;
              x_pos=(line_size % 4); // x_pos sert de variable temporaire
              if (x_pos>0)
                line_size+=(4-x_pos);
    
              buffer=(byte *)malloc(line_size);
              for (y_pos=Main_image_height-1; ((y_pos>=0) && (!File_error)); y_pos--)
              {
                if (Read_bytes(file,buffer,line_size))
                  for (x_pos=0,index=0; x_pos=0) && (!File_error)); y_pos--)
              {
                if (Read_bytes(file,buffer,line_size))
                  for (x_pos=0; x_pos=0) && (!File_error)); y_pos--)
              {
                if (Read_bytes(file,buffer,line_size))
                  for (x_pos=0; x_pos> 3)+1) << 3;
    else
      line_size=Main_image_width;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    header.Signature  = 0x424D;
#else
    header.Signature  = 0x4D42;
#endif
    header.Size_1   =(line_size*Main_image_height)+1078;
    header.Reserved_1   =0;
    header.Reserved_2   =0;
    header.Offset   =1078;
    header.Size_2   =40;
    header.Width    =Main_image_width;
    header.Height    =Main_image_height;
    header.Planes      =1;
    header.Nb_bits    =8;
    header.Compression=0;
    header.Size_3   =0;
    header.XPM        =0;
    header.YPM        =0;
    header.Nb_Clr     =0;
    header.Clr_Imprt  =0;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    header.Size_1   = SDL_Swap32( header.Size_1 );
    header.Offset   = SDL_Swap32( header.Offset );
    header.Size_2   = SDL_Swap32( header.Size_2 );
    header.Width    = SDL_Swap32( header.Width );
    header.Height    = SDL_Swap32( header.Height );
    header.Planes      = SDL_Swap16( header.Planes );
    header.Nb_bits    = SDL_Swap16( header.Nb_bits );
    // If you ever set any more fields to non-zero, please swap here!
#endif
    if (Write_bytes(file,&header,sizeof(T_BMP_Header)))
    {
      //   Chez Bill, ils ont dit: "On va mettre les couleur dans l'ordre
      // inverse, et pour faire chier, on va les mettre sur une échelle de
      // 0 à 255 parce que le standard VGA c'est de 0 à 63 (logique!). Et
      // puis comme c'est pas assez débile, on va aussi y rajouter un octet
      // toujours à 0 pour forcer les gens à s'acheter des gros disques
      // durs... Comme ça, ça fera passer la pillule lorsqu'on sortira
      // Windows 95." ...
      for (index=0; index<256; index++)
      {
        local_palette[index][0]=Main_palette[index].B;
        local_palette[index][1]=Main_palette[index].G;
        local_palette[index][2]=Main_palette[index].R;
        local_palette[index][3]=0;
      }
      if (Write_bytes(file,local_palette,1024))
      {
        Init_write_buffer();
        // ... Et Bill, il a dit: "OK les gars! Mais seulement si vous rangez
        // les pixels dans l'ordre inverse, mais que sur les Y quand-même
        // parce que faut pas pousser."
        for (y_pos=Main_image_height-1; ((y_pos>=0) && (!File_error)); y_pos--)
          for (x_pos=0; x_pos>=current_nb_bits;
      nb_bits_processed  +=current_nb_bits;
      nb_bits_to_process-=current_nb_bits;
      GIF_remainder_bits    -=current_nb_bits;
    }
    return GIF_current_code;
  }
  // -- Affiche un nouveau pixel --
  void GIF_new_pixel(byte color)
  {
    Pixel_load_function(GIF_pos_X,GIF_pos_Y,color);
    GIF_pos_X++;
    if (GIF_pos_X>=Main_image_width)
    {
      GIF_pos_X=0;
      if (!GIF_interlaced)
        GIF_pos_Y++;
      else
      {
        switch (GIF_pass)
        {
          case 0 : GIF_pos_Y+=8;
                   break;
          case 1 : GIF_pos_Y+=8;
                   break;
          case 2 : GIF_pos_Y+=4;
                   break;
          default: GIF_pos_Y+=2;
        }
        if (GIF_pos_Y>=Main_image_height)
        {
          switch(++GIF_pass)
          {
          case 1 : GIF_pos_Y=4;
                   break;
          case 2 : GIF_pos_Y=2;
                   break;
          case 3 : GIF_pos_Y=1;
                   break;
          case 4 : GIF_finished_interlaced_image=1;
          }
        }
      }
    }
  }
void Load_GIF(void)
{
  char filename[MAX_PATH_CHARACTERS];
  char signature[6];
  word * alphabet_stack;     // Pile de décodage d'une chaîne
  word * alphabet_prefix;  // Table des préfixes des codes
  word * alphabet_suffix;  // Table des suffixes des codes
  word   alphabet_free;     // Position libre dans l'alphabet
  word   alphabet_max;      // Nombre d'entrées possibles dans l'alphabet
  word   alphabet_stack_pos; // Position dans la pile de décodage d'un chaîne
  T_GIF_LSDB LSDB;
  T_GIF_IDB IDB;
  word nb_colors;       // Nombre de couleurs dans l'image
  word color_index; // index de traitement d'une couleur
  byte size_to_read; // Nombre de données à lire      (divers)
  byte block_indentifier;  // Code indicateur du type de bloc en cours
  word initial_nb_bits;   // Nb de bits au début du traitement LZW
  word special_case=0;       // Mémoire pour le cas spécial
  word old_code=0;       // Code précédent
  word byte_read;         // Sauvegarde du code en cours de lecture
  word value_clr;        // Valeur <=> Clear tables
  word value_eof;        // Valeur <=> End d'image
  long file_size;
  int number_LID; // Nombre d'images trouvées dans le fichier
  /////////////////////////////////////////////////// FIN DES DECLARATIONS //
  GIF_pos_X=0;
  GIF_pos_Y=0;
  GIF_last_byte=0;
  GIF_remainder_bits=0;
  GIF_remainder_byte=0;
  number_LID=0;
  
  Get_full_filename(filename,0);
  if ((GIF_file=fopen(filename, "rb")))
  {
    file_size=File_length_file(GIF_file);
    if ( (Read_bytes(GIF_file,signature,6)) &&
         ( (memcmp(signature,"GIF87a",6)==0) ||
           (memcmp(signature,"GIF89a",6)==0) ) )
    {
      // Allocation de mémoire pour les tables & piles de traitement:
      alphabet_stack   =(word *)malloc(4096*sizeof(word));
      alphabet_prefix=(word *)malloc(4096*sizeof(word));
      alphabet_suffix=(word *)malloc(4096*sizeof(word));
      if (Read_word_le(GIF_file,&(LSDB.Width))
      && Read_word_le(GIF_file,&(LSDB.Height))
      && Read_byte(GIF_file,&(LSDB.Resol))
      && Read_byte(GIF_file,&(LSDB.Backcol))
      && Read_byte(GIF_file,&(LSDB.Aspect))
        )
      {
        // Lecture du Logical Screen Descriptor Block réussie:
        Original_screen_X=LSDB.Width;
        Original_screen_Y=LSDB.Height;
        // Palette globale dispo = (LSDB.Resol  and $80)
        // Profondeur de couleur =((LSDB.Resol  and $70) shr 4)+1
        // Nombre de bits/pixel  = (LSDB.Resol  and $07)+1
        // Ordre de Classement   = (LSDB.Aspect and $80)
        alphabet_stack_pos=0;
        GIF_last_byte    =0;
        GIF_remainder_bits    =0;
        GIF_remainder_byte    =0;
        nb_colors=(1 << ((LSDB.Resol & 0x07)+1));
        initial_nb_bits=(LSDB.Resol & 0x07)+2;
        if (LSDB.Resol & 0x80)
        {
          // Palette globale dispo:
          if (Config.Clear_palette)
            memset(Main_palette,0,sizeof(T_Palette));
          //   On peut maintenant charger la nouvelle palette:
          if (!(LSDB.Aspect & 0x80))
            // Palette dans l'ordre:
            for(color_index=0;color_indexnb_char_to_keep)
                        fseek(GIF_file,size_to_read-nb_char_to_keep,SEEK_CUR);
                    }
                    break;
                  case 0xF9: // Graphics Control Extension
                    // Prévu pour la transparence
                    
                  default:
                    // On saute le bloc:
                    fseek(GIF_file,size_to_read,SEEK_CUR);
                    break;
                }
                // Lecture de la taille du bloc suivant:
                Read_byte(GIF_file,&size_to_read);
              }
            }
            break;
            case 0x2C: // Local Image Descriptor
            {
              // Si on a deja lu une image, c'est une GIF animée ou bizarroide, on sort.
              if (number_LID!=0)
              {
                File_error=2;
                break;
              }
              number_LID++;
              
              // lecture de 10 derniers octets
              if ( Read_word_le(GIF_file,&(IDB.Pos_X))
                && Read_word_le(GIF_file,&(IDB.Pos_Y))
                && Read_word_le(GIF_file,&(IDB.Image_width))
                && Read_word_le(GIF_file,&(IDB.Image_height))
                && Read_byte(GIF_file,&(IDB.Indicator))
                && Read_byte(GIF_file,&(IDB.Nb_bits_pixel))
                && IDB.Image_width && IDB.Image_height)
              {
                Main_image_width=IDB.Image_width;
                Main_image_height=IDB.Image_height;
    
                Init_preview(IDB.Image_width,IDB.Image_height,file_size,FORMAT_GIF);
    
                // Palette locale dispo = (IDB.Indicator and $80)
                // Image entrelacée     = (IDB.Indicator and $40)
                // Ordre de classement  = (IDB.Indicator and $20)
                // Nombre de bits/pixel = (IDB.Indicator and $07)+1 (si palette locale dispo)
    
                if (IDB.Indicator & 0x80)
                {
                  // Palette locale dispo
    
                  nb_colors=(1 << ((IDB.Indicator & 0x07)+1));
                  initial_nb_bits=(IDB.Indicator & 0x07)+2;
    
                  if (!(IDB.Indicator & 0x40))
                    // Palette dans l'ordre:
                    for(color_index=0;color_indexvalue_clr)
                      {
                        alphabet_stack[alphabet_stack_pos++]=alphabet_suffix[GIF_current_code];
                        GIF_current_code=alphabet_prefix[GIF_current_code];
                      }
    
                      special_case=alphabet_stack[alphabet_stack_pos++]=GIF_current_code;
    
                      do
                        GIF_new_pixel(alphabet_stack[--alphabet_stack_pos]);
                      while (alphabet_stack_pos!=0);
    
                      alphabet_prefix[alphabet_free  ]=old_code;
                      alphabet_suffix[alphabet_free++]=GIF_current_code;
                      old_code=byte_read;
    
                      if (alphabet_free>alphabet_max)
                      {
                        if (GIF_nb_bits<12)
                          alphabet_max      =((1 << (++GIF_nb_bits))-1);
                      }
                    }
                    else // Code Clear rencontré
                    {
                      GIF_nb_bits       =initial_nb_bits;
                      alphabet_max      =((1 <<  GIF_nb_bits)-1);
                      alphabet_free     =nb_colors+2;
                      special_case       =GIF_get_next_code();
                      old_code       =GIF_current_code;
                      GIF_new_pixel(GIF_current_code);
                    }
                  }
                  else
                    File_error=2;
                } // Code End-Of-Information ou erreur de fichier rencontré
    
                /*Close_lecture();*/
    
                if (File_error>=0)
                if ( /* (GIF_pos_X!=0) || */
                     ( ( (!GIF_interlaced) && (GIF_pos_Y!=Main_image_height) ) ||
                       (  (GIF_interlaced) && (!GIF_finished_interlaced_image) )
                     ) )
                  File_error=2;
              } // Le fichier contenait un IDB
              else
                File_error=2;
            }
            default:
            break;
          }
          // Lecture du code de fonction suivant:
          Read_byte(GIF_file,&block_indentifier);
        }
      } // Le fichier contenait un LSDB
      else
        File_error=1;
      // Libération de la mémoire utilisée par les tables & piles de traitement:
      free(alphabet_suffix);
      free(alphabet_prefix);
      free(alphabet_stack);
    } // Le fichier contenait au moins la signature GIF87a ou GIF89a
    else
      File_error=1;
    fclose(GIF_file);
  } // Le fichier était ouvrable
  else
    File_error=1;
}
// -- Sauver un fichier au format GIF ---------------------------------------
  int  GIF_stop;         // "On peut arrêter la sauvegarde du fichier"
  byte GIF_buffer[256];   // buffer d'écriture de bloc de données compilées
  // -- Vider le buffer GIF dans le buffer KM --
  void GIF_empty_buffer(void)
  {
    word index;
    if (GIF_remainder_byte)
    {
      GIF_buffer[0]=GIF_remainder_byte;
      for (index=0;index<=GIF_remainder_byte;index++)
        Write_one_byte(GIF_file,GIF_buffer[index]);
      GIF_remainder_byte=0;
    }
  }
  // -- Ecrit un code à GIF_nb_bits --
  void GIF_set_code(word Code)
  {
    word nb_bits_to_process=GIF_nb_bits;
    word nb_bits_processed  =0;
    word current_nb_bits;
    while (nb_bits_to_process)
    {
      current_nb_bits=(nb_bits_to_process<=(8-GIF_remainder_bits))?nb_bits_to_process:(8-GIF_remainder_bits);
      GIF_last_byte|=(Code & ((1<>=current_nb_bits;
      GIF_remainder_bits    +=current_nb_bits;
      nb_bits_processed  +=current_nb_bits;
      nb_bits_to_process-=current_nb_bits;
      if (GIF_remainder_bits==8) // Il ne reste plus de bits à coder sur l'octet courant
      {
        // Ecrire l'octet à balancer:
        GIF_buffer[++GIF_remainder_byte]=GIF_last_byte;
        // Si on a atteint la fin du bloc de Raster Data
        if (GIF_remainder_byte==255)
          // On doit vider le buffer qui est maintenant plein
          GIF_empty_buffer();
        GIF_last_byte=0;
        GIF_remainder_bits=0;
      }
    }
  }
  // -- Lire le pixel suivant --
  byte GIF_next_pixel(void)
  {
    byte temp;
    temp=Read_pixel_function(GIF_pos_X,GIF_pos_Y);
    if (++GIF_pos_X>=Main_image_width)
    {
      GIF_pos_X=0;
      if (++GIF_pos_Y>=Main_image_height)
        GIF_stop=1;
    }
    return temp;
  }
void Save_GIF(void)
{
  char filename[MAX_PATH_CHARACTERS];
  word * alphabet_prefix;  // Table des préfixes des codes
  word * alphabet_suffix;  // Table des suffixes des codes
  word * alphabet_daughter;    // Table des chaînes filles (plus longues)
  word * alphabet_sister;    // Table des chaînes soeurs (même longueur)
  word   alphabet_free;     // Position libre dans l'alphabet
  word   alphabet_max;      // Nombre d'entrées possibles dans l'alphabet
  word   start;            // Code précédent (sert au linkage des chaînes)
  int    descend;          // Booléen "On vient de descendre"
  T_GIF_LSDB LSDB;
  T_GIF_IDB IDB;
  byte block_indentifier;  // Code indicateur du type de bloc en cours
  word current_string;   // Code de la chaîne en cours de traitement
  byte current_char;         // Caractère à coder
  word index;            // index de recherche de chaîne
  /////////////////////////////////////////////////// FIN DES DECLARATIONS //
  GIF_pos_X=0;
  GIF_pos_Y=0;
  GIF_last_byte=0;
  GIF_remainder_bits=0;
  GIF_remainder_byte=0;
  Get_full_filename(filename,0);
  if ((GIF_file=fopen(filename,"wb")))
  {
    // On écrit la signature du fichier
    if (Write_bytes(GIF_file,"GIF89a",6))
    {
      // La signature du fichier a été correctement écrite.
      // Allocation de mémoire pour les tables
      alphabet_prefix=(word *)malloc(4096*sizeof(word));
      alphabet_suffix=(word *)malloc(4096*sizeof(word));
      alphabet_daughter  =(word *)malloc(4096*sizeof(word));
      alphabet_sister  =(word *)malloc(4096*sizeof(word));
      // On initialise le LSDB du fichier
      if (Config.Screen_size_in_GIF)
      {
        LSDB.Width=Screen_width;
        LSDB.Height=Screen_height;
      }
      else
      {
        LSDB.Width=Main_image_width;
        LSDB.Height=Main_image_height;
      }
      LSDB.Resol  =0x97;          // Image en 256 couleurs, avec une palette
      LSDB.Backcol=0;
      LSDB.Aspect =0;             // Palette normale
      // On sauve le LSDB dans le fichier
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
      LSDB.Width = SDL_Swap16(LSDB.Width);
      LSDB.Height = SDL_Swap16(LSDB.Height);
#endif
      if (Write_bytes(GIF_file,&LSDB,sizeof(T_GIF_LSDB)))
      {
        // Le LSDB a été correctement écrit.
        // On sauve la palette
        if (Write_bytes(GIF_file,Main_palette,768))
        {
          // La palette a été correctement écrite.
          //   Le jour où on se servira des blocks d'extensions pour placer
          // des commentaires, on le fera ici.
          // Ecriture de la transparence
          //Write_bytes(GIF_file,"\x21\xF9\x04\x01\x00\x00\xNN\x00",8);
          // Ecriture du commentaire
          if (Main_comment[0])
          {
            Write_bytes(GIF_file,"\x21\xFE",2);
            Write_byte(GIF_file,strlen(Main_comment));
            Write_bytes(GIF_file,Main_comment,strlen(Main_comment)+1);
          }
                            
          
          // On va écrire un block indicateur d'IDB et l'IDB du fichier
          block_indentifier=0x2C;
          IDB.Pos_X=0;
          IDB.Pos_Y=0;
          IDB.Image_width=Main_image_width;
          IDB.Image_height=Main_image_height;
          IDB.Indicator=0x07;    // Image non entrelacée, pas de palette locale.
          IDB.Nb_bits_pixel=8; // Image 256 couleurs;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
          IDB.Image_width = SDL_Swap16(IDB.Image_width);
          IDB.Image_height = SDL_Swap16(IDB.Image_height);
#endif
          if ( Write_bytes(GIF_file,&block_indentifier,1) &&
               Write_bytes(GIF_file,&IDB,sizeof(T_GIF_IDB)) )
          {
            //   Le block indicateur d'IDB et l'IDB ont étés correctements
            // écrits.
            Init_write_buffer();
            index=4096;
            File_error=0;
            GIF_stop=0;
            // Réintialisation de la table:
            alphabet_free=258;
            GIF_nb_bits  =9;
            alphabet_max =511;
            GIF_set_code(256);
            for (start=0;start<4096;start++)
            {
              alphabet_daughter[start]=4096;
              alphabet_sister[start]=4096;
            }
            ////////////////////////////////////////////// COMPRESSION LZW //
            start=current_string=GIF_next_pixel();
            descend=1;
            do
            {
              current_char=GIF_next_pixel();
              //   On regarde si dans la table on aurait pas une chaîne
              // équivalente à current_string+Caractere
              while ( (index0xFFF)
                {
                  // Réintialisation de la table:
                  GIF_set_code(256);
                  alphabet_free=258;
                  GIF_nb_bits  =9;
                  alphabet_max =511;
                  for (start=0;start<4096;start++)
                  {
                    alphabet_daughter[start]=4096;
                    alphabet_sister[start]=4096;
                  }
                }
                else if (alphabet_free>alphabet_max+1)
                {
                  // On augmente le nb de bits
                  GIF_nb_bits++;
                  alphabet_max=(1< 16c , 1 => 256c , ...
    word Bytes_per_plane_line;// Doit toujours être pair
    word Palette_info;       // 1 => color , 2 => Gris (ignoré à partir de la version 4)
    word Screen_X;           // |_ Dimensions de
    word Screen_Y;           // |  l'écran d'origine
    byte Filler[54];         // Ca... J'adore!
  } __attribute__((__packed__)) T_PCX_Header;
T_PCX_Header PCX_header;
// -- Tester si un fichier est au format PCX --------------------------------
void Test_PCX(void)
{
  char filename[MAX_PATH_CHARACTERS];
  FILE *file;
  File_error=0;
  Get_full_filename(filename,0);
  if ((file=fopen(filename, "rb")))
  {
    if (Read_byte(file,&(PCX_header.Manufacturer)) &&
        Read_byte(file,&(PCX_header.Version)) &&
        Read_byte(file,&(PCX_header.Compression)) &&
        Read_byte(file,&(PCX_header.Depth)) &&
        Read_word_le(file,&(PCX_header.X_min)) &&
        Read_word_le(file,&(PCX_header.Y_min)) &&
        Read_word_le(file,&(PCX_header.X_max)) &&
        Read_word_le(file,&(PCX_header.Y_max)) &&
        Read_word_le(file,&(PCX_header.X_dpi)) &&
        Read_word_le(file,&(PCX_header.Y_dpi)) &&
        Read_bytes(file,&(PCX_header.Palette_16c),48) &&        
        Read_byte(file,&(PCX_header.Reserved)) &&
        Read_byte(file,&(PCX_header.Plane)) &&
        Read_word_le(file,&(PCX_header.Bytes_per_plane_line)) &&
        Read_word_le(file,&(PCX_header.Palette_info)) &&
        Read_word_le(file,&(PCX_header.Screen_X)) &&
        Read_word_le(file,&(PCX_header.Screen_Y)) &&
        Read_bytes(file,&(PCX_header.Filler),54) )
    {
    
      //   Vu que ce header a une signature de merde et peu significative, il
      // va falloir que je teste différentes petites valeurs dont je connais
      // l'intervalle. Grrr!
      if ( (PCX_header.Manufacturer!=10)
        || (PCX_header.Compression>1)
        || ( (PCX_header.Depth!=1) && (PCX_header.Depth!=2) && (PCX_header.Depth!=4) && (PCX_header.Depth!=8) )
        || ( (PCX_header.Plane!=1) && (PCX_header.Plane!=2) && (PCX_header.Plane!=4) && (PCX_header.Plane!=8) && (PCX_header.Plane!=3) )
        || (PCX_header.X_max>((reduction_minus_one-(x_pos%reduction))*depth)) & byte_mask;
      Pixel_load_function(x_pos,y_pos,color);
    }
  }
void Load_PCX(void)
{
  char filename[MAX_PATH_CHARACTERS];
  FILE *file;
  
  short line_size;
  short real_line_size; // width de l'image corrigée
  short width_read;
  short x_pos;
  short y_pos;
  byte  byte1;
  byte  byte2;
  byte  index;
  dword nb_colors;
  long  file_size;
  byte  palette_CGA[9]={ 84,252,252,  252, 84,252,  252,252,252};
  long  position;
  long  image_size;
  byte * buffer;
  Get_full_filename(filename,0);
  File_error=0;
  if ((file=fopen(filename, "rb")))
  {
    file_size=File_length_file(file);
    /*
    if (Read_bytes(file,&PCX_header,sizeof(T_PCX_Header)))
    {*/
    
    if (Read_byte(file,&(PCX_header.Manufacturer)) &&
        Read_byte(file,&(PCX_header.Version)) &&
        Read_byte(file,&(PCX_header.Compression)) &&
        Read_byte(file,&(PCX_header.Depth)) &&
        Read_word_le(file,&(PCX_header.X_min)) &&
        Read_word_le(file,&(PCX_header.Y_min)) &&
        Read_word_le(file,&(PCX_header.X_max)) &&
        Read_word_le(file,&(PCX_header.Y_max)) &&
        Read_word_le(file,&(PCX_header.X_dpi)) &&
        Read_word_le(file,&(PCX_header.Y_dpi)) &&
        Read_bytes(file,&(PCX_header.Palette_16c),48) &&        
        Read_byte(file,&(PCX_header.Reserved)) &&
        Read_byte(file,&(PCX_header.Plane)) &&
        Read_word_le(file,&(PCX_header.Bytes_per_plane_line)) &&
        Read_word_le(file,&(PCX_header.Palette_info)) &&
        Read_word_le(file,&(PCX_header.Screen_X)) &&
        Read_word_le(file,&(PCX_header.Screen_Y)) &&
        Read_bytes(file,&(PCX_header.Filler),54) )
    {
      
      Main_image_width=PCX_header.X_max-PCX_header.X_min+1;
      Main_image_height=PCX_header.Y_max-PCX_header.Y_min+1;
      Original_screen_X=PCX_header.Screen_X;
      Original_screen_Y=PCX_header.Screen_Y;
      if (PCX_header.Plane!=3)
      {
        Init_preview(Main_image_width,Main_image_height,file_size,FORMAT_PCX);
        if (File_error==0)
        {
          // On prépare la palette à accueillir les valeurs du fichier PCX
          if (Config.Clear_palette)
            memset(Main_palette,0,sizeof(T_Palette));
          nb_colors=(dword)(1<4)
            memcpy(Main_palette,PCX_header.Palette_16c,48);
          else
          {
            Main_palette[1].R=0;
            Main_palette[1].G=0;
            Main_palette[1].B=0;
            byte1=PCX_header.Palette_16c[3]>>5;
            if (nb_colors==4)
            { // Pal. CGA "alakon" (du Turc Allahkoum qui signifie "à la con" :))
              memcpy(Main_palette+1,palette_CGA,9);
              if (!(byte1&2))
              {
                Main_palette[1].B=84;
                Main_palette[2].B=84;
                Main_palette[3].B=84;
              }
            } // Palette monochrome (on va dire que c'est du N&B)
            else
            {
              Main_palette[1].R=252;
              Main_palette[1].G=252;
              Main_palette[1].B=252;
            }
          }
          //   On se positionne à la fin du fichier - 769 octets pour voir s'il y
          // a une palette.
          if ( (PCX_header.Depth==8) && (PCX_header.Version>=5) && (file_size>(256*3)) )
          {
            fseek(file,file_size-((256*3)+1),SEEK_SET);
            // On regarde s'il y a une palette après les données de l'image
            if (Read_byte(file,&byte1))
              if (byte1==12) // Lire la palette si c'est une image en 256 couleurs
              {
                int index;
                // On lit la palette 256c que ces crétins ont foutue à la fin du fichier
                for(index=0;index<256;index++)
                  if ( ! Read_byte(file,&Main_palette[index].R)
                   || ! Read_byte(file,&Main_palette[index].G)
                   || ! Read_byte(file,&Main_palette[index].B) )
                  {
                    File_error=2;
                    DEBUG("ERROR READING PCX PALETTE !",index);
                    break;
                  }
              }
          }
          Set_palette(Main_palette);
          Remap_fileselector();
          //   Maintenant qu'on a lu la palette que ces crétins sont allés foutre
          // à la fin, on retourne juste après le header pour lire l'image.
          fseek(file,128,SEEK_SET);
          if (!File_error)
          {
            line_size=PCX_header.Bytes_per_plane_line*PCX_header.Plane;
            real_line_size=(short)PCX_header.Bytes_per_plane_line<<3;
            //   On se sert de données LBM car le dessin de ligne en moins de 256
            // couleurs se fait comme avec la structure ILBM.
            Image_HAM=0;
            HBPm1=PCX_header.Plane-1;
            LBM_buffer=(byte *)malloc(line_size);
            // Chargement de l'image
            if (PCX_header.Compression)  // Image compressée
            {
              /*Init_lecture();*/
  
              image_size=(long)PCX_header.Bytes_per_plane_line*Main_image_height;
              if (PCX_header.Depth==8) // 256 couleurs (1 plan)
              {
                for (position=0; ((position=line_size)
                      {
                        for (x_pos=0; x_pos=line_size)
                  {
                    for (x_pos=0; x_pos1) || (last_pixel>=0xC0) )
            Write_one_byte(file,counter|0xC0);
          Write_one_byte(file,last_pixel);
      
        }
      }
      
      // Ecriture de l'octet (12) indiquant que la palette arrive
      if (!File_error)
        Write_one_byte(file,12);
      
      End_write(file);
      
      // Ecriture de la palette
      if (!File_error)
      {
        if (! Write_bytes(file,Main_palette,sizeof(T_Palette)))
          File_error=1;
      }
    }
    else
      File_error=1;
       
    fclose(file);
       
    if (File_error)
      remove(filename);
       
  }    
  else 
    File_error=1;
}      
       
       
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// CEL ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
typedef struct
{
  word Width;              // width de l'image
  word Height;             // height de l'image
} __attribute__((__packed__))  T_CEL_Header1;
typedef struct
{
  byte Signature[4];           // Signature du format
  byte Kind;               // Type de fichier ($10=PALette $20=BitMaP)
  byte Nb_bits;             // Nombre de bits
  word Filler1;            // ???
  word Width;            // width de l'image
  word Height;            // height de l'image
  word X_offset;         // Offset en X de l'image
  word Y_offset;         // Offset en Y de l'image
  byte Filler2[16];        // ???
} __attribute__((__packed__))  T_CEL_Header2;
// -- Tester si un fichier est au format CEL --------------------------------
void Test_CEL(void)
{
  char filename[MAX_PATH_CHARACTERS];
  int  size;
  FILE *file;
  T_CEL_Header1 header1;
  T_CEL_Header2 header2;
  int file_size;
  File_error=0;
  Get_full_filename(filename,0);
  file_size=File_length(filename);
  if (file_size==0)
  {
    File_error = 1; // Si on ne peut pas faire de stat il vaut mieux laisser tomber
    return;
  }
  
  if (! (file=fopen(filename, "rb")))
  {
    File_error = 1;
    return;
  }
  if (Read_word_le(file,&header1.Width) &&
      Read_word_le(file,&header1.Height) )
  {
      //   Vu que ce header n'a pas de signature, il va falloir tester la
      // cohérence de la dimension de l'image avec celle du fichier.
      
      size=file_size-sizeof(T_CEL_Header1);
      if ( (!size) || ( (((header1.Width+1)>>1)*header1.Height)!=size ) )
      {
        // Tentative de reconnaissance de la signature des nouveaux fichiers
        fseek(file,0,SEEK_SET);        
        if (Read_bytes(file,&header2.Signature,4) &&
            !memcmp(header2.Signature,"KiSS",4) &&
            Read_byte(file,&header2.Kind) &&
            (header2.Kind==0x20) &&
            Read_byte(file,&header2.Nb_bits) &&
            Read_word_le(file,&header2.Filler1) &&
            Read_word_le(file,&header2.Width) &&
            Read_word_le(file,&header2.Height) &&
            Read_word_le(file,&header2.X_offset) &&
            Read_word_le(file,&header2.Y_offset) &&
            Read_bytes(file,&header2.Filler2,16))
        {
          // ok
        }
        else
          File_error=1;
      }
      else
        File_error=1;
  }
  else
  {
    File_error=1;
  }
  fclose(file);    
}
// -- Lire un fichier au format CEL -----------------------------------------
void Load_CEL(void)
{
  char filename[MAX_PATH_CHARACTERS];
  FILE *file;
  T_CEL_Header1 header1;
  T_CEL_Header2 header2;
  short x_pos;
  short y_pos;
  byte  last_byte=0;
  long  file_size;
  File_error=0;
  Get_full_filename(filename,0);
  if ((file=fopen(filename, "rb")))
  {
    if (Read_bytes(file,&header1,sizeof(T_CEL_Header1)))
    {
      file_size=File_length_file(file);
      if ( (file_size>(long int)sizeof(T_CEL_Header1))
        && ( (((header1.Width+1)>>1)*header1.Height)==(file_size-(long int)sizeof(T_CEL_Header1)) ) )
      {
        // Chargement d'un fichier CEL sans signature (vieux fichiers)
        Main_image_width=header1.Width;
        Main_image_height=header1.Height;
        Original_screen_X=Main_image_width;
        Original_screen_Y=Main_image_height;
        Init_preview(Main_image_width,Main_image_height,file_size,FORMAT_CEL);
        if (File_error==0)
        {
          // Chargement de l'image
          /*Init_lecture();*/
          for (y_pos=0;((y_pos> 4));
              }
              else
                Pixel_load_function(x_pos,y_pos,(last_byte & 15));
          /*Close_lecture();*/
        }
      }
      else
      {
        // On réessaye avec le nouveau format
        fseek(file,0,SEEK_SET);
        if (Read_bytes(file,&header2,sizeof(T_CEL_Header2)))
        {
          // Chargement d'un fichier CEL avec signature (nouveaux fichiers)
          Main_image_width=header2.Width+header2.X_offset;
          Main_image_height=header2.Height+header2.Y_offset;
          Original_screen_X=Main_image_width;
          Original_screen_Y=Main_image_height;
          Init_preview(Main_image_width,Main_image_height,file_size,FORMAT_CEL);
          if (File_error==0)
          {
            // Chargement de l'image
            /*Init_lecture();*/
            if (!File_error)
            {
              // Effacement du décalage
              for (y_pos=0;y_pos> 4));
                      }
                      else
                        Pixel_load_function(x_pos+header2.X_offset,y_pos+header2.Y_offset,(last_byte & 15));
                  break;
                case 8:
                  for (y_pos=0;((y_pos16 sont utilisées dans l'image
    for (x_pos=16;((x_pos<256) && (!Utilisation[x_pos]));x_pos++);
    if (x_pos==256)
    {
      // Cas d'une image 16 couleurs (écriture à l'ancien format)
      header1.Width =Main_image_width;
      header1.Height=Main_image_height;
      if (Write_bytes(file,&header1,sizeof(T_CEL_Header1)))
      {
        // Sauvegarde de l'image
        Init_write_buffer();
        for (y_pos=0;((y_pos>4)!=0)
            File_error=1;
    }
    else
    {
      if (Read_bytes(file,&header2,sizeof(T_CEL_Header2)))
      {
        if (memcmp(header2.Signature,"KiSS",4)==0)
        {
          if (header2.Kind!=0x10)
            File_error=1;
        }
        else
          File_error=1;
      }
      else
        File_error=1;
    }
    fclose(file);
  }
  else
    File_error=1;
}
// -- Lire un fichier au format KCF -----------------------------------------
void Load_KCF(void)
{
  char filename[MAX_PATH_CHARACTERS];
  FILE *file;
  T_KCF_Header buffer;
  T_CEL_Header2 header2;
  byte bytes[3];
  int pal_index;
  int color_index;
  int index;
  long  file_size;
  File_error=0;
  Get_full_filename(filename,0);
  if ((file=fopen(filename, "rb")))
  {
    file_size=File_length_file(file);
    if (file_size==sizeof(T_KCF_Header))
    {
      // Fichier KCF à l'ancien format
      if (Read_bytes(file,&buffer,sizeof(T_KCF_Header)))
      {
        // Init_preview(???); // Pas possible... pas d'image...
        if (Config.Clear_palette)
          memset(Main_palette,0,sizeof(T_Palette));
        // Chargement de la palette
        for (pal_index=0;pal_index<10;pal_index++)
          for (color_index=0;color_index<16;color_index++)
          {
            index=16+(pal_index*16)+color_index;
            Main_palette[index].R=((buffer.Palette[pal_index].color[color_index].Byte1 >> 4) << 4);
            Main_palette[index].B=((buffer.Palette[pal_index].color[color_index].Byte1 & 15) << 4);
            Main_palette[index].G=((buffer.Palette[pal_index].color[color_index].Byte2 & 15) << 4);
          }
        for (index=0;index<16;index++)
        {
          Main_palette[index].R=Main_palette[index+16].R;
          Main_palette[index].G=Main_palette[index+16].G;
          Main_palette[index].B=Main_palette[index+16].B;
        }
        Set_palette(Main_palette);
        Remap_fileselector();
      }
      else
        File_error=1;
    }
    else
    {
      // Fichier KCF au nouveau format
      if (Read_bytes(file,&header2,sizeof(T_CEL_Header2)))
      {
        // Init_preview(???); // Pas possible... pas d'image...
        index=(header2.Nb_bits==12)?16:0;
        for (pal_index=0;pal_index> 4) << 4;
                 Main_palette[index].B=(bytes[0] & 15) << 4;
                 Main_palette[index].G=(bytes[1] & 15) << 4;
                 break;
               case 24: // RRRR RRRR | VVVV VVVV | BBBB BBBB
                 Read_bytes(file,bytes,3);
                 Main_palette[index].R=bytes[0];
                 Main_palette[index].G=bytes[1];
                 Main_palette[index].B=bytes[2];
             }
             index++;
           }
        }
        if (header2.Nb_bits==12)
          for (index=0;index<16;index++)
          {
            Main_palette[index].R=Main_palette[index+16].R;
            Main_palette[index].G=Main_palette[index+16].G;
            Main_palette[index].B=Main_palette[index+16].B;
          }
        Set_palette(Main_palette);
        Remap_fileselector();
      }
      else
        File_error=1;
    }
    fclose(file);
  }
  else
    File_error=1;
  if (!File_error) Draw_palette_preview();
}
// -- Ecrire un fichier au format KCF ---------------------------------------
void Save_KCF(void)
{
  char filename[MAX_PATH_CHARACTERS];
  FILE *file;
  T_KCF_Header buffer;
  T_CEL_Header2 header2;
  byte bytes[3];
  int pal_index;
  int color_index;
  int index;
  dword Utilisation[256]; // Table d'utilisation de couleurs
  // On commence par compter l'utilisation de chaque couleurs
  Count_used_colors(Utilisation);
  File_error=0;
  Get_full_filename(filename,0);
  if ((file=fopen(filename,"wb")))
  {
    // Sauvegarde de la palette
    // On regarde si des couleurs >16 sont utilisées dans l'image
    for (index=16;((index<256) && (!Utilisation[index]));index++);
    if (index==256)
    {
      // Cas d'une image 16 couleurs (écriture à l'ancien format)
      for (pal_index=0;pal_index<10;pal_index++)
        for (color_index=0;color_index<16;color_index++)
        {
          index=16+(pal_index*16)+color_index;
          buffer.Palette[pal_index].color[color_index].Byte1=((Main_palette[index].R>>4)<<4) | (Main_palette[index].B>>4);
          buffer.Palette[pal_index].color[color_index].Byte2=Main_palette[index].G>>4;
        }
      if (! Write_bytes(file,&buffer,sizeof(T_KCF_Header)))
        File_error=1;
    }
    else
    {
      // Cas d'une image 256 couleurs (écriture au nouveau format)
      memcpy(header2.Signature,"KiSS",4); // Initialisation de la signature
      header2.Kind=0x10;              // Initialisation du type (PALette)
      header2.Nb_bits=24;              // Initialisation du nombre de bits
      header2.Filler1=0;              // Initialisation du filler 1 (???)
      header2.Width=256;            // Initialisation du nombre de couleurs
      header2.Height=1;              // Initialisation du nombre de palettes
      header2.X_offset=0;           // Initialisation du décalage X
      header2.Y_offset=0;           // Initialisation du décalage Y
      for (index=0;index<16;index++) // Initialisation du filler 2 (???)
        header2.Filler2[index]=0;
      if (! Write_bytes(file,&header2,sizeof(T_CEL_Header2)))
        File_error=1;
      for (index=0;(index<256) && (!File_error);index++)
      {
        bytes[0]=Main_palette[index].R;
        bytes[1]=Main_palette[index].G;
        bytes[2]=Main_palette[index].B;
        if (! Write_bytes(file,bytes,3))
          File_error=1;
      }
    }
    fclose(file);
    if (File_error)
      remove(filename);
  }
  else
    File_error=1;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// SCx ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
typedef struct
{
  byte Filler1[4];
  word Width;
  word Height;
  byte Filler2;
  byte Planes;
} __attribute__((__packed__)) T_SCx_Header;
// -- Tester si un fichier est au format SCx --------------------------------
void Test_SCx(void)
{
  FILE *file;              // Fichier du fichier
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  //byte Signature[3];
  T_SCx_Header SCx_header;
  Get_full_filename(filename,0);
  File_error=1;
  // Ouverture du fichier
  if ((file=fopen(filename, "rb")))
  {
    // Lecture et vérification de la signature
    if ((Read_bytes(file,&SCx_header,sizeof(T_SCx_Header))))
    {
      if ( (!memcmp(SCx_header.Filler1,"RIX",3))
        && SCx_header.Width && SCx_header.Height)
      File_error=0;
    }
    // Fermeture du fichier
    fclose(file);
  }
}
// -- Lire un fichier au format SCx -----------------------------------------
void Load_SCx(void)
{
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  FILE *file;
  word x_pos,y_pos;
  long size,real_size;
  long file_size;
  T_SCx_Header SCx_header;
  T_Palette SCx_Palette;
  Get_full_filename(filename,0);
  File_error=0;
  if ((file=fopen(filename, "rb")))
  {
    file_size=File_length_file(file);
    if ((Read_bytes(file,&SCx_header,sizeof(T_SCx_Header))))
    {
      Init_preview(SCx_header.Width,SCx_header.Height,file_size,FORMAT_SCx);
      if (File_error==0)
      {
        if (!SCx_header.Planes)
          size=sizeof(T_Palette);
        else
          size=sizeof(T_Components)*(1<>3)*SCx_header.Planes;
            real_size=(size/SCx_header.Planes)<<3;
            LBM_buffer=(byte *)malloc(size);
            HBPm1=SCx_header.Planes-1;
            Image_HAM=0;
            for (y_pos=0;(y_pos>=1;
  }
}
//// CODAGE d'une partie d'IMAGE ////
void PI1_16p_to_8b(byte * src,byte * dest)
{
  int  i;           // index du pixel à calculer
  word byte_mask;      // Masque de codage
  word w0,w1,w2,w3; // Les 4 words bien ordonnés de la destination
  byte_mask=0x8000;
  w0=w1=w2=w3=0;
  for (i=0;i<16;i++)
  {
    // Pour coder le pixel n°i, il faut modifier les 4 words sur leur bit
    // correspondant à celui du masque
    w0|=(src[i] & 0x01)?byte_mask:0x00;
    w1|=(src[i] & 0x02)?byte_mask:0x00;
    w2|=(src[i] & 0x04)?byte_mask:0x00;
    w3|=(src[i] & 0x08)?byte_mask:0x00;
    byte_mask>>=1;
  }
  dest[0]=w0 >> 8;
  dest[1]=w0 & 0x00FF;
  dest[2]=w1 >> 8;
  dest[3]=w1 & 0x00FF;
  dest[4]=w2 >> 8;
  dest[5]=w2 & 0x00FF;
  dest[6]=w3 >> 8;
  dest[7]=w3 & 0x00FF;
}
//// DECODAGE de la PALETTE ////
void PI1_decode_palette(byte * src,byte * palette)
{
  int i;  // Numéro de la couleur traitée
  int ip; // index dans la palette
  word w; // Word contenant le code
  // Schéma d'un word =
  //
  //    Low        High
  // VVVV RRRR | 0000 BBBB
  // 0321 0321 |      0321
  ip=0;
  for (i=0;i<16;i++)
  {
    w=((word)src[(i*2)+1]<<8) | src[(i*2)+0];
    // Traitement des couleurs rouge, verte et bleue:
    palette[ip++]=(((w & 0x0007) <<  1) | ((w & 0x0008) >>  3)) << 4;
    palette[ip++]=(((w & 0x7000) >> 11) | ((w & 0x8000) >> 15)) << 4;
    palette[ip++]=(((w & 0x0700) >>  7) | ((w & 0x0800) >> 11)) << 4;
  }
}
//// CODAGE de la PALETTE ////
void PI1_code_palette(byte * palette,byte * dest)
{
  int i;  // Numéro de la couleur traitée
  int ip; // index dans la palette
  word w; // Word contenant le code
  // Schéma d'un word =
  //
  //    Low        High
  // VVVV RRRR | 0000 BBBB
  // 0321 0321 |      0321
  ip=0;
  for (i=0;i<16;i++)
  {
    // Traitement des couleurs rouge, verte et bleue:
    w =(((word)(palette[ip]>>2) & 0x38) >> 3) | (((word)(palette[ip]>>2) & 0x04) <<  1); ip++;
    w|=(((word)(palette[ip]>>2) & 0x38) << 9) | (((word)(palette[ip]>>2) & 0x04) << 13); ip++;
    w|=(((word)(palette[ip]>>2) & 0x38) << 5) | (((word)(palette[ip]>>2) & 0x04) <<  9); ip++;
    dest[(i*2)+0]=w & 0x00FF;
    dest[(i*2)+1]=(w>>8);
  }
}
// -- Tester si un fichier est au format PI1 --------------------------------
void Test_PI1(void)
{
  FILE * file;              // Fichier du fichier
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  int  size;              // Taille du fichier
  word resolution;                 // Résolution de l'image
  Get_full_filename(filename,0);
  File_error=1;
  // Ouverture du fichier
  if ((file=fopen(filename, "rb")))
  {
    // Vérification de la taille
    size=File_length_file(file);
    if ((size==32034) || (size==32066))
    {
      // Lecture et vérification de la résolution
      if (Read_word_le(file,&resolution))
      {
        if (resolution==0x0000)
          File_error=0;
      }
    }
    // Fermeture du fichier
    fclose(file);
  }
}
// -- Lire un fichier au format PI1 -----------------------------------------
void Load_PI1(void)
{
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  FILE *file;
  word x_pos,y_pos;
  byte * buffer;
  byte * ptr;
  byte pixels[320];
  Get_full_filename(filename,0);
  File_error=0;
  if ((file=fopen(filename, "rb")))
  {
    // allocation d'un buffer mémoire
    buffer=(byte *)malloc(32034);
    if (buffer!=NULL)
    {
      // Lecture du fichier dans le buffer
      if (Read_bytes(file,buffer,32034))
      {
        // Initialisation de la preview
        Init_preview(320,200,File_length_file(file),FORMAT_PI1);
        if (File_error==0)
        {
          // Initialisation de la palette
          if (Config.Clear_palette)
            memset(Main_palette,0,sizeof(T_Palette));
          PI1_decode_palette(buffer+2,(byte *)Main_palette);
          Set_palette(Main_palette);
          Remap_fileselector();
          Main_image_width=320;
          Main_image_height=200;
          // Chargement/décompression de l'image
          ptr=buffer+34;
          for (y_pos=0;y_pos<200;y_pos++)
          {
            for (x_pos=0;x_pos<(320>>4);x_pos++)
            {
              PI1_8b_to_16p(ptr,pixels+(x_pos<<4));
              ptr+=8;
            }
            for (x_pos=0;x_pos<320;x_pos++)
              Pixel_load_function(x_pos,y_pos,pixels[x_pos]);
          }
        }
      }
      else
        File_error=1;
      free(buffer);
    }
    else
      File_error=1;
    fclose(file);
  }
  else
    File_error=1;
}
// -- Sauver un fichier au format PI1 ---------------------------------------
void Save_PI1(void)
{
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  FILE *file;
  short x_pos,y_pos;
  byte * buffer;
  byte * ptr;
  byte pixels[320];
  Get_full_filename(filename,0);
  File_error=0;
  // Ouverture du fichier
  if ((file=fopen(filename,"wb")))
  {
    // allocation d'un buffer mémoire
    buffer=(byte *)malloc(32066);
    // Codage de la résolution
    buffer[0]=0x00;
    buffer[1]=0x00;
    // Codage de la palette
    PI1_code_palette((byte *)Main_palette,buffer+2);
    // Codage de l'image
    ptr=buffer+34;
    for (y_pos=0;y_pos<200;y_pos++)
    {
      // Codage de la ligne
      memset(pixels,0,320);
      if (y_pos>4);x_pos++)
      {
        PI1_16p_to_8b(pixels+(x_pos<<4),ptr);
        ptr+=8;
      }
    }
    memset(buffer+32034,0,32); // 32 extra NULL bytes at the end of the file to make ST Deluxe Paint happy
    if (Write_bytes(file,buffer,32066))
    {
      fclose(file);
    }
    else // Error d'écriture (disque plein ou protégé)
    {
      fclose(file);
      remove(filename);
      File_error=1;
    }
    // Libération du buffer mémoire
    free(buffer);
  }
  else
  {
    fclose(file);
    remove(filename);
    File_error=1;
  }
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// PC1 ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//// DECOMPRESSION d'un buffer selon la méthode PACKBITS ////
void PC1_uncompress_packbits(byte * src,byte * dest)
{
  int is,id; // Les indices de parcour des buffers
  int n;     // Octet de contrôle
  for (is=id=0;id<32000;)
  {
    n=src[is++];
    if (n & 0x80)
    {
      // Recopier src[is] -n+1 fois
      n=257-n;
      for (;(n>0) && (id<32000);n--)
        dest[id++]=src[is];
      is++;
    }
    else
    {
      // Recopier n+1 octets littéralement
      n=n+1;
      for (;(n>0) && (id<32000);n--)
        dest[id++]=src[is++];
    }
    // Contrôle des erreurs
    if (n>0)
      File_error=1;
  }
}
//// COMPRESSION d'un buffer selon la méthode PACKBITS ////
void PC1_compress_packbits(byte * src,byte * dest,int source_size,int * dest_size)
{
  int is; // index dans la source
  int id; // index dans la destination
  int ir; // index de   la répétition
  int n;  // Taille des séquences
  int repet; // "Il y a répétition"
  for (is=id=0;is0;n--)
        dest[id++]=src[is++];
    }
    // On code la partie sans répétitions
    if (repet)
    {
      // On compte la quantité de fois qu'il faut répéter la valeur
      for (ir+=3;ir>=1;
    }
  }
}
//// CODAGE d'une partie d'IMAGE ////
// Transformation d'1 ligne de pixels en 4 plans de bits
void PC1_1line_to_4bp(byte * src,byte * dst0,byte * dst1,byte * dst2,byte * dst3)
{
  int  i,j;         // Compteurs
  int  ip;          // index du pixel à calculer
  byte byte_mask;      // Masque de decodage
  byte b0,b1,b2,b3; // Les 4 octets des plans bits sources
  ip=0;
  // Pour chacun des 40 octets des plans de bits
  for (i=0;i<40;i++)
  {
    // Pour chacun des 8 bits des octets
    byte_mask=0x80;
    b0=b1=b2=b3=0;
    for (j=0;j<8;j++)
    {
      b0|=(src[ip] & 0x01)?byte_mask:0x00;
      b1|=(src[ip] & 0x02)?byte_mask:0x00;
      b2|=(src[ip] & 0x04)?byte_mask:0x00;
      b3|=(src[ip] & 0x08)?byte_mask:0x00;
      ip++;
      byte_mask>>=1;
    }
    dst0[i]=b0;
    dst1[i]=b1;
    dst2[i]=b2;
    dst3[i]=b3;
  }
}
// -- Tester si un fichier est au format PC1 --------------------------------
void Test_PC1(void)
{
  FILE *file;              // Fichier du fichier
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  int  size;              // Taille du fichier
  word resolution;                 // Résolution de l'image
  Get_full_filename(filename,0);
  File_error=1;
  // Ouverture du fichier
  if ((file=fopen(filename, "rb")))
  {
    // Vérification de la taille
    size=File_length_file(file);
    if ((size<=32066))
    {
      // Lecture et vérification de la résolution
      if (Read_word_le(file,&resolution))
      {
        if (resolution==0x0080)
          File_error=0;
      }
    }
    // Fermeture du fichier
    fclose(file);
  }
}
// -- Lire un fichier au format PC1 -----------------------------------------
void Load_PC1(void)
{
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  FILE *file;
  int  size;
  word x_pos,y_pos;
  byte * buffercomp;
  byte * bufferdecomp;
  byte * ptr;
  byte pixels[320];
  Get_full_filename(filename,0);
  File_error=0;
  if ((file=fopen(filename, "rb")))
  {
    size=File_length_file(file);
    // allocation des buffers mémoire
    buffercomp=(byte *)malloc(size);
    bufferdecomp=(byte *)malloc(32000);
    if ( (buffercomp!=NULL) && (bufferdecomp!=NULL) )
    {
      // Lecture du fichier dans le buffer
      if (Read_bytes(file,buffercomp,size))
      {
        // Initialisation de la preview
        Init_preview(320,200,File_length_file(file),FORMAT_PC1);
        if (File_error==0)
        {
          // Initialisation de la palette
          if (Config.Clear_palette)
            memset(Main_palette,0,sizeof(T_Palette));
          PI1_decode_palette(buffercomp+2,(byte *)Main_palette);
          Set_palette(Main_palette);
          Remap_fileselector();
          Main_image_width=320;
          Main_image_height=200;
          // Décompression du buffer
          PC1_uncompress_packbits(buffercomp+34,bufferdecomp);
          // Décodage de l'image
          ptr=bufferdecomp;
          for (y_pos=0;y_pos<200;y_pos++)
          {
            // Décodage de la scanline
            PC1_4bp_to_1line(ptr,ptr+40,ptr+80,ptr+120,pixels);
            ptr+=160;
            // Chargement de la ligne
            for (x_pos=0;x_pos<320;x_pos++)
              Pixel_load_function(x_pos,y_pos,pixels[x_pos]);
          }
        }
      }
      else
        File_error=1;
      free(bufferdecomp);
      free(buffercomp);
    }
    else
    {
      File_error=1;
      free(bufferdecomp);
      free(buffercomp);
    }
    fclose(file);
  }
  else
    File_error=1;
}
// -- Sauver un fichier au format PC1 ---------------------------------------
void Save_PC1(void)
{
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  FILE *file;
  int   size;
  short x_pos,y_pos;
  byte * buffercomp;
  byte * bufferdecomp;
  byte * ptr;
  byte pixels[320];
  Get_full_filename(filename,0);
  File_error=0;
  // Ouverture du fichier
  if ((file=fopen(filename,"wb")))
  {
    // Allocation des buffers mémoire
    bufferdecomp=(byte *)malloc(32000);
    buffercomp  =(byte *)malloc(64066);
    // Codage de la résolution
    buffercomp[0]=0x80;
    buffercomp[1]=0x00;
    // Codage de la palette
    PI1_code_palette((byte *)Main_palette,buffercomp+2);
    // Codage de l'image
    ptr=bufferdecomp;
    for (y_pos=0;y_pos<200;y_pos++)
    {
      // Codage de la ligne
      memset(pixels,0,320);
      if (y_pos>4);x_pos++)
            {
              PI1_8b_to_16p(ptr,pixels+(x_pos<<4));
              ptr+=8;
            }
            for (x_pos=0;x_pos<320;x_pos++)
              Pixel_load_function(x_pos,y_pos,pixels[x_pos]);
          }
        }
      }
      else
        File_error=1;
      free(buffer);
    }
    else
      File_error=1;
    fclose(file);
  }
  else
    File_error=1;
}
void Save_NEO(void)
{
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  FILE *file;
  short x_pos,y_pos;
  byte * buffer;
  byte * ptr;
  byte pixels[320];
  Get_full_filename(filename,0);
  File_error=0;
  // Ouverture du fichier
  if ((file=fopen(filename,"wb")))
  {
    // allocation d'un buffer mémoire
    buffer=(byte *)malloc(32128);
    // Codage de la résolution
    buffer[0]=0x00;
    buffer[1]=0x00;
    buffer[2]=0x00;
    buffer[3]=0x00;
    // Codage de la palette
    PI1_code_palette((byte *)Main_palette,buffer+4);
    // Codage de l'image
    ptr=buffer+128;
    for (y_pos=0;y_pos<200;y_pos++)
    {
      // Codage de la ligne
      memset(pixels,0,320);
      if (y_pos>4);x_pos++)
      {
        PI1_16p_to_8b(pixels+(x_pos<<4),ptr);
        ptr+=8;
      }
    }
    if (Write_bytes(file,buffer,32128))
    {
      fclose(file);
    }
    else // Error d'écriture (disque plein ou protégé)
    {
      fclose(file);
      remove(filename);
      File_error=1;
    }
    // Libération du buffer mémoire
    free(buffer);
  }
  else
  {
    fclose(file);
    remove(filename);
    File_error=1;
  }
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// PNG ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
#ifndef __no_pnglib__
// -- Tester si un fichier est au format PNG --------------------------------
void Test_PNG(void)
{
  FILE *file;             // Fichier du fichier
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  byte png_header[8];
  
  Get_full_filename(filename,0);
  
  File_error=1;
  // Ouverture du fichier
  if ((file=fopen(filename, "rb")))
  {
    // Lecture du header du fichier
    if (Read_bytes(file,png_header,8))
    {
      if ( !png_sig_cmp(png_header, 0, 8))
        File_error=0;
    }
    fclose(file);
  }
}
png_bytep * Row_pointers;
// -- Lire un fichier au format PNG -----------------------------------------
void Load_PNG(void)
{
  FILE *file;             // Fichier du fichier
  char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier
  byte png_header[8];  
  byte row_pointers_allocated;
 
  png_structp png_ptr;
  png_infop info_ptr;
  Get_full_filename(filename,0);
  File_error=0;
  
  if ((file=fopen(filename, "rb")))
  {
    // Load header (8 first bytes)
    if (Read_bytes(file,png_header,8))
    {
      // Do we recognize a png file signature ?
      if ( !png_sig_cmp(png_header, 0, 8))
      {
        // Prepare internal PNG loader
        png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
        if (png_ptr)
        {
          // Prepare internal PNG loader
          info_ptr = png_create_info_struct(png_ptr);
          if (info_ptr)
          {
            png_byte color_type;
            png_byte bit_depth;
            
            // Setup a return point. If a pnglib loading error occurs
            // in this if(), the else will be executed.
            if (!setjmp(png_jmpbuf(png_ptr)))
            {
              png_init_io(png_ptr, file);
              // Inform pnglib we already loaded the header.
              png_set_sig_bytes(png_ptr, 8);
            
              // Load file information
              png_read_info(png_ptr, info_ptr);
              color_type = info_ptr->color_type;
              bit_depth = info_ptr->bit_depth;
              
              // If it's any supported file
              // (Note: As of writing this, this test covers every possible 
              // image format of libpng)
              if (color_type == PNG_COLOR_TYPE_PALETTE
               || color_type == PNG_COLOR_TYPE_GRAY
               || color_type == PNG_COLOR_TYPE_GRAY_ALPHA
               || color_type == PNG_COLOR_TYPE_RGB
               || color_type == PNG_COLOR_TYPE_RGB_ALPHA
              )
              {
                int num_text;
                png_text *text_ptr;
                
                int unit_type;
                png_uint_32 res_x;
                png_uint_32 res_y;
                // Comment (tEXt)
                Main_comment[0]='\0'; // Clear the previous comment
                if ((num_text=png_get_text(png_ptr, info_ptr, &text_ptr, NULL)))
                {
                  while (num_text--)
                  {
                    if (!strcmp(text_ptr[num_text].key,"Title"))
                    {
                      int size;
                      size = Min(text_ptr[num_text].text_length, COMMENT_SIZE);
                      strncpy(Main_comment, text_ptr[num_text].text, size);
                      Main_comment[size]='\0';
                      break; // Skip all others tEXt chunks
                    }
                  }
                }
                // Pixel Ratio (pHYs)
                if (png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type))
                {
                  // Ignore unit, and use the X/Y ratio as a hint for
                  // WIDE or TALL pixels
                  if (res_x>0 && res_y>0)
                  {
                    if (res_y/res_x>1)
                    {
                      Ratio_of_loaded_image=PIXEL_WIDE;
                    }
                    else if (res_x/res_y>1)
                    {
                      Ratio_of_loaded_image=PIXEL_TALL;
                    }
                  }
                }
                if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA)
                  Init_preview_24b(info_ptr->width,info_ptr->height,File_length_file(file),FORMAT_PNG);
                else
                  Init_preview(info_ptr->width,info_ptr->height,File_length_file(file),FORMAT_PNG);
                if (File_error==0)
                {
                  int x,y;
                  png_colorp palette;
                  int num_palette;
                  // 16-bit images
                  if (bit_depth == 16)
                  {
                    // Reduce to 8-bit
                    png_set_strip_16(png_ptr);
                  }
                  else if (bit_depth < 8)
                  {
                    // Inform libpng we want one byte per pixel,
                    // even though the file was less than 8bpp
                    png_set_packing(png_ptr);
                  }
                    
                  // Images with alpha channel
                  if (color_type & PNG_COLOR_MASK_ALPHA)
                  {
                    // Tell libpng to ignore it
                    png_set_strip_alpha(png_ptr);
                  }
                  // Greyscale images : 
                  if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
                  {
                    // Map low bpp greyscales to full 8bit (0-255 range)
                    if (bit_depth < 8)
                      png_set_gray_1_2_4_to_8(png_ptr);
                    
                    // Create greyscale palette
                    for (x=0;x<256;x++)
                    {
                      Main_palette[x].R=x;
                      Main_palette[x].G=x;
                      Main_palette[x].B=x;
                    } 
                  }
                  else if (color_type == PNG_COLOR_TYPE_PALETTE) // Palette images
                  {
                    
                    if (bit_depth < 8)
                    {
                      // Clear unused colors
                      if (Config.Clear_palette)
                        memset(Main_palette,0,sizeof(T_Palette));
                    }
                    // Load the palette
                    png_get_PLTE(png_ptr, info_ptr, &palette,
                       &num_palette);
                    for (x=0;xwidth;
                  Main_image_height=info_ptr->height;
                  
                  png_set_interlace_handling(png_ptr);
                  png_read_update_info(png_ptr, info_ptr);
                  // Allocate row pointers
                  Row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * Main_image_height);
                  row_pointers_allocated = 0;
                  /* read file */
                  if (!setjmp(png_jmpbuf(png_ptr)))
                  {
                    if (color_type == PNG_COLOR_TYPE_GRAY
                    ||  color_type == PNG_COLOR_TYPE_GRAY_ALPHA
                    ||  color_type == PNG_COLOR_TYPE_PALETTE
                    )
                    {
                      // 8bpp
                      
                      for (y=0; yrowbytes);
                      row_pointers_allocated = 1;
                      
                      png_read_image(png_ptr, Row_pointers);
                      
                      for (y=0; yrowbytes);
                        row_pointers_allocated = 1;
                        
                        png_read_image(png_ptr, Row_pointers);
                        
                        for (y=0; y