/*  Grafx2 - The Ultimate 256-color bitmap paint program
    Copyright 2008 Peter Gordon
    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  or
    write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#if defined(__WIN32__)
  #include  // GetLogicalDrives(), GetDriveType(), DRIVE_*
#endif
#if defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__)
#include 
#include 
#endif
#include "const.h"
#include "struct.h"
#include "global.h"
#include "graph.h"
#include "boutons.h"
#include "palette.h"
#include "aide.h"
#include "operatio.h"
#include "divers.h"
#include "erreurs.h"
#include "clavier.h"
#include "io.h"
#include "hotkeys.h"
#include "files.h"
#include "setup.h"
#include "windows.h"
#include "sdlscreen.h"
#include "mountlist.h" // read_file_system_list
// Ajouter un lecteur à la liste de lecteurs
void Ajouter_lecteur(char Lettre, byte Type, char *Chemin)
{
  Drive[Nb_drives].Lettre=Lettre;
  Drive[Nb_drives].Type  =Type;
  Drive[Nb_drives].Chemin=(char *)malloc(strlen(Chemin)+1);
  strcpy(Drive[Nb_drives].Chemin, Chemin);
  Nb_drives++;
}
// Rechercher la liste et le type des lecteurs de la machine
#if defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__)
void bstrtostr( BSTR in, STRPTR out, TEXT max );
#endif
void Rechercher_drives(void)
{
  #if defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__)
    struct DosList *dl;
    char tmp[256];
    dl = LockDosList( LDF_VOLUMES | LDF_READ );
    if( dl )
    {
      while( ( dl = NextDosEntry( dl, LDF_VOLUMES | LDF_READ ) ) )
      {
        bstrtostr( dl->dol_Name, tmp, 254 );
        strcat( tmp, ":" );
        Ajouter_lecteur(':',LECTEUR_HDD,tmp);
      }
      UnLockDosList( LDF_VOLUMES | LDF_READ );
    }
  #elif defined (__WIN32__)
        char NomLecteur[]="A:\\";
        int DriveBits = GetLogicalDrives();
        int IndiceLecteur;
        int IndiceBit;
        // Sous Windows, on a la totale, presque aussi bien que sous DOS:
        IndiceLecteur = 0;
        for (IndiceBit=0; IndiceBit<26 && IndiceLecteur<23; IndiceBit++)
        {
          if ( (1 << IndiceBit) & DriveBits )
          {
            // On a ce lecteur, il faut maintenant déterminer son type "physique".
            // pour profiter des jolies icones de X-man.
            int TypeLecteur;
            char CheminLecteur[]="A:\\";
            // Cette API Windows est étrange, je dois m'y faire...
            CheminLecteur[0]='A'+IndiceBit;
            switch (GetDriveType(CheminLecteur))
            {
              case DRIVE_CDROM:
                TypeLecteur=LECTEUR_CDROM;
                break;
              case DRIVE_REMOTE:
                TypeLecteur=LECTEUR_NETWORK;
                break;
              case DRIVE_REMOVABLE:
                TypeLecteur=LECTEUR_FLOPPY_3_5;
                break;
              case DRIVE_FIXED:
                TypeLecteur=LECTEUR_HDD;
                break;
              default:
                TypeLecteur=LECTEUR_NETWORK;
                break;
            }
      		NomLecteur[0]='A'+IndiceBit;
            Ajouter_lecteur(NomLecteur[0], TypeLecteur,NomLecteur);
            IndiceLecteur++;
          }
        }
  #else
    //Sous les différents unix, on va mettre
    // un disque dur qui pointera vers la racine,
    // et un autre vers le home directory de l'utilisateur.
    // Ensuite on utilise read_file_system_list pour compléter
    struct mount_entry* Liste_points_montage;
    struct mount_entry* next;
    char lettre = 'A';
    #if defined(__BEOS__) || defined(__HAIKU__)
        char * Home = getenv("$HOME");
    #else
        char * Home = getenv("HOME");
    #endif
    Ajouter_lecteur('/', LECTEUR_HDD, "/");
    if(Home)
        Ajouter_lecteur('~', LECTEUR_HDD, Home);
    Liste_points_montage = read_file_system_list(false);
    while(Liste_points_montage != NULL)
    {
        if(Liste_points_montage->me_dummy == 0 && strcmp(Liste_points_montage->me_mountdir,"/") && strcmp(Liste_points_montage->me_mountdir,"/home"))
        {
            Ajouter_lecteur(lettre++,
                Liste_points_montage->me_remote==1?LECTEUR_NETWORK:LECTEUR_HDD,
                Liste_points_montage->me_mountdir);
        }
        next = Liste_points_montage -> me_next;
#if !(defined(__macosx__) || defined(__FreeBSD__))
        free(Liste_points_montage -> me_type);
#endif
        free(Liste_points_montage);
        Liste_points_montage = next;
    }
  #endif
}
// Active un lecteur, changeant normalement le répertoire en cours.
// Renvoie 0 si ok, -1 si problème.
int ActiverLecteur(int NumeroLecteur)
{
  return chdir(Drive[NumeroLecteur].Chemin);
}
// Fonctions de lecture dans la skin de l'interface graphique
void Chercher_bas(SDL_Surface *GUI, int *Debut_X, int *Debut_Y, byte Couleur_neutre,char * Section)
{
  byte Couleur;
  int Y;
  Y=*Debut_Y;
  *Debut_X=0;
  do
  {
    Couleur=Sdl_Get_pixel_8(GUI,*Debut_X,Y);
    if (Couleur!=Couleur_neutre)
    {
      *Debut_Y=Y;
      return;
    }
    Y++;
  } while (Yh);
  
  printf("Error in skin file: Was looking down from %d,%d for a '%s', and reached the end of the image\n",
    *Debut_X, *Debut_Y, Section);
  Erreur(ERREUR_GUI_CORROMPU);
}
void Chercher_droite(SDL_Surface *GUI, int *Debut_X, int Debut_Y, byte Couleur_neutre, char * Section)
{
  byte Couleur;
  int X;
  X=*Debut_X;
  
  do
  {
    Couleur=Sdl_Get_pixel_8(GUI,X,Debut_Y);
    if (Couleur!=Couleur_neutre)
    {
      *Debut_X=X;
      return;
    }
    X++;
  } while (Xw);
  
  printf("Error in skin file: Was looking right from %d,%d for a '%s', and reached the edege of the image\n",
    *Debut_X, Debut_Y, Section);
  Erreur(ERREUR_GUI_CORROMPU);
}
void Lire_bloc(SDL_Surface *GUI, int Debut_X, int Debut_Y, void *Dest, int Largeur, int Hauteur, char * Section, int Type)
{
  // Type: 0 = normal GUI element, only 4 colors allowed
  // Type: 1 = mouse cursor, 4 colors allowed + transparent
  // Type: 2 = brush icon or sieve pattern (only CM_Blanc and CM_Trans)
  // Type: 3 = raw bitmap (splash screen)
  
  byte * Ptr=Dest;
  int X,Y;
  byte Couleur;
  // Verification taille
  if (Debut_Y+Hauteur>=GUI->h || Debut_X+Largeur>=GUI->w)
  {
    printf("Error in skin file: Was looking at %d,%d for a %d*%d object (%s) but it doesn't fit the image.\n",
      Debut_X, Debut_Y, Hauteur, Largeur, Section);
    Erreur(ERREUR_GUI_CORROMPU);
  }
  for (Y=Debut_Y; Yformat || GUI->format->BitsPerPixel != 8)
  {
    printf("Not a 8-bit image");
    Erreur(ERREUR_GUI_CORROMPU);
  }
  SDLPal=GUI->format->palette;
  if (!SDLPal || SDLPal->ncolors!=256)
  {
    printf("Not a 256-color palette");
    Erreur(ERREUR_GUI_CORROMPU);
  }
  // Lecture de la palette par défaut
  for (i=0; i<256; i++)
  {
    Palette_defaut[i].R=SDLPal->colors[i].r;
    Palette_defaut[i].V=SDLPal->colors[i].g;
    Palette_defaut[i].B=SDLPal->colors[i].b;
  }
  
  // Carré "noir"
  CM_Noir = Sdl_Get_pixel_8(GUI,Curseur_X,Curseur_Y);
  do
  {
    if (++Curseur_X>=GUI->w)
    {
      printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      Erreur(ERREUR_GUI_CORROMPU);
    }
    Couleur=Sdl_Get_pixel_8(GUI,Curseur_X,Curseur_Y);
  } while(Couleur==CM_Noir);
  // Carré "foncé"
  CM_Fonce=Couleur;
  do
  {
    if (++Curseur_X>=GUI->w)
    {
      printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      Erreur(ERREUR_GUI_CORROMPU);
    }
    Couleur=Sdl_Get_pixel_8(GUI,Curseur_X,Curseur_Y);
  } while(Couleur==CM_Fonce);
  // Carré "clair"
  CM_Clair=Couleur;
  do
  {
    if (++Curseur_X>GUI->w)
    {
      printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      Erreur(ERREUR_GUI_CORROMPU);
    }
    Couleur=Sdl_Get_pixel_8(GUI,Curseur_X,Curseur_Y);
  } while(Couleur==CM_Clair);
  // Carré "blanc"
  CM_Blanc=Couleur;
  do
  {
    if (++Curseur_X>=GUI->w)
    {
      printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      Erreur(ERREUR_GUI_CORROMPU);
    }
    Couleur=Sdl_Get_pixel_8(GUI,Curseur_X,Curseur_Y);
  } while(Couleur==CM_Blanc);
  // Carré "transparent"
  CM_Trans=Couleur;
  do
  {
    if (++Curseur_X>=GUI->w)
    {
      printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      Erreur(ERREUR_GUI_CORROMPU);
    }
    Couleur=Sdl_Get_pixel_8(GUI,Curseur_X,Curseur_Y);
  } while(Couleur==CM_Trans);
  // Reste : couleur neutre
  CM_Neutre=Couleur;
  
  Curseur_X=0;
  Curseur_Y=1;
  while ((Couleur=Sdl_Get_pixel_8(GUI,Curseur_X,Curseur_Y))==CM_Noir)
  {
    Curseur_Y++;
    if (Curseur_Y>=GUI->h)
    {
      printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      Erreur(ERREUR_GUI_CORROMPU);
    }
  }
  
  // Menu
  Chercher_bas(GUI, &Curseur_X, &Curseur_Y, CM_Neutre, "menu");
  Lire_bloc(GUI, Curseur_X, Curseur_Y, BLOCK_MENU, LARGEUR_MENU, HAUTEUR_MENU,"menu",0);
  Curseur_Y+=HAUTEUR_MENU;
  // Effets
  for (i=0; i>1);
    Pinceau_predefini_Decalage_Y[Indice]=(Pinceau_predefini_Hauteur[Indice]>>1);
  }
  Curseur_Decalage_X[FORME_CURSEUR_FLECHE]=0;
  Curseur_Decalage_Y[FORME_CURSEUR_FLECHE]=0;
  Curseur_Decalage_X[FORME_CURSEUR_CIBLE]=7;
  Curseur_Decalage_Y[FORME_CURSEUR_CIBLE]=7;
  Curseur_Decalage_X[FORME_CURSEUR_CIBLE_PIPETTE]=7;
  Curseur_Decalage_Y[FORME_CURSEUR_CIBLE_PIPETTE]=7;
  Curseur_Decalage_X[FORME_CURSEUR_SABLIER]=7;
  Curseur_Decalage_Y[FORME_CURSEUR_SABLIER]=7;
  Curseur_Decalage_X[FORME_CURSEUR_MULTIDIRECTIONNEL]=7;
  Curseur_Decalage_Y[FORME_CURSEUR_MULTIDIRECTIONNEL]=7;
  Curseur_Decalage_X[FORME_CURSEUR_HORIZONTAL]=7;
  Curseur_Decalage_Y[FORME_CURSEUR_HORIZONTAL]=3;
  Curseur_Decalage_X[FORME_CURSEUR_CIBLE_FINE]=7;
  Curseur_Decalage_Y[FORME_CURSEUR_CIBLE_FINE]=7;
  Curseur_Decalage_X[FORME_CURSEUR_CIBLE_PIPETTE_FINE]=7;
  Curseur_Decalage_Y[FORME_CURSEUR_CIBLE_PIPETTE_FINE]=7;
}
// Initialisation des boutons:
  // Action factice:
void Rien_du_tout(void)
{}
  // Initialiseur d'un bouton:
void Initialiser_bouton(byte   Numero,
                        word   Decalage_X      , word   Decalage_Y,
                        word   Largeur         , word   Hauteur,
                        byte   Forme,
                        fonction_action Gauche , fonction_action Droite,
                        fonction_action Desenclencher,
                        byte   Famille)
{
  Bouton[Numero].Decalage_X      =Decalage_X;
  Bouton[Numero].Decalage_Y      =Decalage_Y;
  Bouton[Numero].Largeur         =Largeur-1;
  Bouton[Numero].Hauteur         =Hauteur-1;
  Bouton[Numero].Enfonce         =0;
  Bouton[Numero].Forme           =Forme;
  Bouton[Numero].Gauche          =Gauche;
  Bouton[Numero].Droite          =Droite;
  Bouton[Numero].Desenclencher   =Desenclencher;
  Bouton[Numero].Famille         =Famille;
}
  // Initiliseur de tous les boutons:
void Initialisation_des_boutons(void)
{
  byte Indice_bouton;
  for (Indice_bouton=0;Indice_bouton= MAX_MODES_VIDEO-1)
  {
    DEBUG("Erreur! Tentative de créer un mode de trop! Limite:", MAX_MODES_VIDEO);
    return;
  }
  if (!Fullscreen)
    Supporte = 128; // Prefere, non modifiable
  else if (SDL_VideoModeOK(Largeur, Hauteur, 8, SDL_FULLSCREEN))
    Supporte = 1; // Supporte
  else
  {
    // Non supporte : on ne le prend pas
    return;
  }
  Mode_video[Nb_modes_video].Largeur          = Largeur;
  Mode_video[Nb_modes_video].Hauteur          = Hauteur;
  Mode_video[Nb_modes_video].Mode             = Mode;
  Mode_video[Nb_modes_video].Fullscreen       = Fullscreen;
  Mode_video[Nb_modes_video].Etat                   = Supporte;
  Nb_modes_video ++;
}
// Utilisé pour trier les modes retournés par SDL
int Compare_modes_video(const void *p1, const void *p2)
{
  const struct S_Mode_video *Mode1 = (const struct S_Mode_video *)p1;
  const struct S_Mode_video *Mode2 = (const struct S_Mode_video *)p2;
  // Tris par largeur
  if(Mode1->Largeur - Mode2->Largeur)
    return Mode1->Largeur - Mode2->Largeur;
  // Tri par hauteur
  return Mode1->Hauteur - Mode2->Hauteur;
}
// Initiliseur de tous les modes video:
void Definition_des_modes_video(void)
{                   // Numero       LargHaut Mode      FXFY Ratio Ref WinOnly Pointeur
  SDL_Rect** Modes;
  Nb_modes_video=0;
  // Doit être en premier pour avoir le numéro 0:
  Definir_mode_video( 640,480,0, 0);
  Definir_mode_video( 320,200,0, 1);
  Definir_mode_video( 320,224,0, 1);
  Definir_mode_video( 320,240,0, 1);
  Definir_mode_video( 320,256,0, 1);
  Definir_mode_video( 320,270,0, 1);
  Definir_mode_video( 320,282,0, 1);
  Definir_mode_video( 320,300,0, 1);
  Definir_mode_video( 320,360,0, 1);
  Definir_mode_video( 320,400,0, 1);
  Definir_mode_video( 320,448,0, 1);
  Definir_mode_video( 320,480,0, 1);
  Definir_mode_video( 320,512,0, 1);
  Definir_mode_video( 320,540,0, 1);
  Definir_mode_video( 320,564,0, 1);
  Definir_mode_video( 320,600,0, 1);
  Definir_mode_video( 360,200,0, 1);
  Definir_mode_video( 360,224,0, 1);
  Definir_mode_video( 360,240,0, 1);
  Definir_mode_video( 360,256,0, 1);
  Definir_mode_video( 360,270,0, 1);
  Definir_mode_video( 360,282,0, 1);
  Definir_mode_video( 360,300,0, 1);
  Definir_mode_video( 360,360,0, 1);
  Definir_mode_video( 360,400,0, 1);
  Definir_mode_video( 360,448,0, 1);
  Definir_mode_video( 360,480,0, 1);
  Definir_mode_video( 360,512,0, 1);
  Definir_mode_video( 360,540,0, 1);
  Definir_mode_video( 360,564,0, 1);
  Definir_mode_video( 360,600,0, 1);
  Definir_mode_video( 400,200,0, 1);
  Definir_mode_video( 400,224,0, 1);
  Definir_mode_video( 400,240,0, 1);
  Definir_mode_video( 400,256,0, 1);
  Definir_mode_video( 400,270,0, 1);
  Definir_mode_video( 400,282,0, 1);
  Definir_mode_video( 400,300,0, 1);
  Definir_mode_video( 400,360,0, 1);
  Definir_mode_video( 400,400,0, 1);
  Definir_mode_video( 400,448,0, 1);
  Definir_mode_video( 400,480,0, 1);
  Definir_mode_video( 400,512,0, 1);
  Definir_mode_video( 400,540,0, 1);
  Definir_mode_video( 400,564,0, 1);
  Definir_mode_video( 400,600,0, 1);
  Definir_mode_video( 640,224,0, 1);
  Definir_mode_video( 640,240,0, 1);
  Definir_mode_video( 640,256,0, 1);
  Definir_mode_video( 640,270,0, 1);
  Definir_mode_video( 640,300,0, 1);
  Definir_mode_video( 640,350,0, 1);
  Definir_mode_video( 640,400,0, 1);
  Definir_mode_video( 640,448,0, 1);
  Definir_mode_video( 640,480,0, 1);
  Definir_mode_video( 640,512,0, 1);
  Definir_mode_video( 640,540,0, 1);
  Definir_mode_video( 640,564,0, 1);
  Definir_mode_video( 640,600,0, 1);
  Definir_mode_video( 800,600,0, 1);
  Definir_mode_video(1024,768,0, 1);
  Modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
  if ((Modes != (SDL_Rect**)0) && (Modes!=(SDL_Rect**)-1))
  {
    int Indice;
    for (Indice=0; Modes[Indice]; Indice++)
    {
      int Indice2;
      for (Indice2=1; Indice2 < Nb_modes_video; Indice2++)
        if (Modes[Indice]->w == Mode_video[Indice2].Largeur &&
            Modes[Indice]->h == Mode_video[Indice2].Hauteur)
        {
          // Mode déja prévu: ok
          break;
        }
      if (Indice2 >= Nb_modes_video)
      {
        // Nouveau mode à ajouter à la liste
        Definir_mode_video(Modes[Indice]->w,Modes[Indice]->h,0, 1);
      }
    }
    // Tri des modes : ceux trouvés par SDL ont été listés à la fin.
    qsort(&Mode_video[1], Nb_modes_video - 1, sizeof(struct S_Mode_video), Compare_modes_video);
  }
}
//---------------------------------------------------------------------------
word Ordonnancement[NB_TOUCHES]=
{
  SPECIAL_SCROLL_UP,                // Scroll up
  SPECIAL_SCROLL_DOWN,              // Scroll down
  SPECIAL_SCROLL_LEFT,              // Scroll left
  SPECIAL_SCROLL_RIGHT,             // Scroll right
  SPECIAL_SCROLL_UP_FAST,           // Scroll up faster
  SPECIAL_SCROLL_DOWN_FAST,         // Scroll down faster
  SPECIAL_SCROLL_LEFT_FAST,         // Scroll left faster
  SPECIAL_SCROLL_RIGHT_FAST,        // Scroll right faster
  SPECIAL_SCROLL_UP_SLOW,           // Scroll up slower
  SPECIAL_SCROLL_DOWN_SLOW,         // Scroll down slower
  SPECIAL_SCROLL_LEFT_SLOW,         // Scroll left slower
  SPECIAL_SCROLL_RIGHT_SLOW,        // Scroll right slower
  SPECIAL_MOUSE_UP,                 // Emulate mouse up
  SPECIAL_MOUSE_DOWN,               // Emulate mouse down
  SPECIAL_MOUSE_LEFT,               // Emulate mouse left
  SPECIAL_MOUSE_RIGHT,              // Emulate mouse right
  SPECIAL_CLICK_LEFT,               // Emulate mouse click left
  SPECIAL_CLICK_RIGHT,              // Emulate mouse click right
  0x100+BOUTON_CACHER,              // Show / Hide menu
  SPECIAL_SHOW_HIDE_CURSOR,         // Show / Hide cursor
  SPECIAL_PINCEAU_POINT,            // Paintbrush = "."
  0x100+BOUTON_PINCEAUX,            // Paintbrush choice
  0x200+BOUTON_PINCEAUX,            // Monochrome brush
  0x100+BOUTON_DESSIN,              // Freehand drawing
  0x200+BOUTON_DESSIN,              // Switch freehand drawing mode
  SPECIAL_DESSIN_CONTINU,           // Continuous freehand drawing
  0x100+BOUTON_LIGNES,              // Line
  0x200+BOUTON_LIGNES,              // Knotted lines
  0x100+BOUTON_SPRAY,               // Spray
  0x200+BOUTON_SPRAY,               // Spray menu
  0x100+BOUTON_FLOODFILL,           // Floodfill
  0x200+BOUTON_FLOODFILL,           // Replace color
  0x100+BOUTON_COURBES,             // Bézier's curves
  0x200+BOUTON_COURBES,             // Bézier's curve with 3 or 4 points
  0x100+BOUTON_RECTANGLES,          // Empty rectangle
  0x100+BOUTON_FILLRECT,            // Filled rectangle
  0x100+BOUTON_CERCLES,             // Empty circle
  0x200+BOUTON_CERCLES,             // Empty ellipse
  0x100+BOUTON_FILLCERC,            // Filled circle
  0x200+BOUTON_FILLCERC,            // Filled ellipse
  0x100+BOUTON_POLYGONES,           // Empty polygon
  0x200+BOUTON_POLYGONES,           // Empty polyform
  0x100+BOUTON_POLYFILL,            // Polyfill
  0x200+BOUTON_POLYFILL,            // Filled polyform
  0x100+BOUTON_GRADRECT,            // Gradient rectangle
  0x100+BOUTON_GRADMENU,            // Gradation menu
  0x100+BOUTON_SPHERES,             // Spheres
  0x200+BOUTON_SPHERES,             // Gradient ellipses
  0x100+BOUTON_AJUSTER,             // Adjust picture
  0x200+BOUTON_AJUSTER,             // Flip picture menu
  0x100+BOUTON_EFFETS,              // Menu des effets
  SPECIAL_SHADE_MODE,               // Shade mode
  SPECIAL_SHADE_MENU,               // Shade menu
  SPECIAL_QUICK_SHADE_MODE,         // Quick-shade mode
  SPECIAL_QUICK_SHADE_MENU,         // Quick-shade menu
  SPECIAL_STENCIL_MODE,             // Stencil mode
  SPECIAL_STENCIL_MENU,             // Stencil menu
  SPECIAL_MASK_MODE,                // Mask mode
  SPECIAL_MASK_MENU,                // Mask menu
  SPECIAL_GRID_MODE,                // Grid mode
  SPECIAL_GRID_MENU,                // Grid menu
  SPECIAL_SIEVE_MODE,               // Sieve mode
  SPECIAL_SIEVE_MENU,               // Sieve menu
  SPECIAL_INVERT_SIEVE,             // Inverser la trame du mode Sieve
  SPECIAL_COLORIZE_MODE,            // Colorize mode
  SPECIAL_COLORIZE_MENU,            // Colorize menu
  SPECIAL_SMOOTH_MODE,              // Smooth mode
  SPECIAL_SMOOTH_MENU,              // Smooth menu
  SPECIAL_SMEAR_MODE,               // Smear mode
  SPECIAL_TILING_MODE,              // Tiling mode
  SPECIAL_TILING_MENU,              // Tiling menu
  0x100+BOUTON_BROSSE,              // Pick brush
  0x100+BOUTON_POLYBROSSE,          // Pick polyform brush
  0x200+BOUTON_BROSSE,              // Restore brush
  SPECIAL_FLIP_X,                   // Flip X
  SPECIAL_FLIP_Y,                   // Flip Y
  SPECIAL_ROTATE_90,                // 90° brush rotation
  SPECIAL_ROTATE_180,               // 180° brush rotation
  SPECIAL_STRETCH,                  // Stretch brush
  SPECIAL_DISTORT,                  // Distort brush
  SPECIAL_OUTLINE,                  // Outline brush
  SPECIAL_NIBBLE,                   // Nibble brush
  SPECIAL_GET_BRUSH_COLORS,         // Get colors from brush
  SPECIAL_RECOLORIZE_BRUSH,         // Recolorize brush
  SPECIAL_ROTATE_ANY_ANGLE,         // Rotate brush by any angle
  0x100+BOUTON_PIPETTE,             // Pipette
  0x200+BOUTON_PIPETTE,             // Swap fore/back color
  0x100+BOUTON_LOUPE,               // Magnifier mode
  0x200+BOUTON_LOUPE,               // Zoom factor menu
  SPECIAL_ZOOM_IN,                  // Zoom in
  SPECIAL_ZOOM_OUT,                 // Zoom out
  0x100+BOUTON_EFFETS_BROSSE,       // Brush effects menu
  0x100+BOUTON_TEXTE,               // Text
  0x100+BOUTON_RESOL,               // Resolution menu
  0x200+BOUTON_RESOL,               // Safety resolution
  0x100+BOUTON_AIDE,                // Help & credits
  0x200+BOUTON_AIDE,                // Statistics
  0x100+BOUTON_PAGE,                // Go to spare page
  0x200+BOUTON_PAGE,                // Copy to spare page
  0x100+BOUTON_SAUVER,              // Save as
  0x200+BOUTON_SAUVER,              // Save
  0x100+BOUTON_CHARGER,             // Load
  0x200+BOUTON_CHARGER,             // Re-load
  SPECIAL_SAVE_BRUSH,               // Save brush
  SPECIAL_LOAD_BRUSH,               // Load brush
  0x100+BOUTON_PARAMETRES,          // Settings
  0x100+BOUTON_UNDO,                // Undo
  0x200+BOUTON_UNDO,                // Redo
  0x100+BOUTON_KILL,                // Kill
  0x100+BOUTON_CLEAR,               // Clear
  0x200+BOUTON_CLEAR,               // Clear with backcolor
  0x100+BOUTON_QUIT,                // Quit
  0x100+BOUTON_PALETTE,             // Palette menu
  0x200+BOUTON_PALETTE,             // Palette menu secondaire
  SPECIAL_EXCLUDE_COLORS_MENU,      // Exclude colors menu
  0x100+BOUTON_PAL_LEFT,            // Scroll palette left
  0x100+BOUTON_PAL_RIGHT,           // Scroll palette right
  0x200+BOUTON_PAL_LEFT,            // Scroll palette left faster
  0x200+BOUTON_PAL_RIGHT,           // Scroll palette right faster
  SPECIAL_CENTER_ATTACHMENT,        // Center brush attachement
  SPECIAL_TOP_LEFT_ATTACHMENT,      // Top-left brush attachement
  SPECIAL_TOP_RIGHT_ATTACHMENT,     // Top-right brush attachement
  SPECIAL_BOTTOM_LEFT_ATTACHMENT,   // Bottom-left brush attachement
  SPECIAL_BOTTOM_RIGHT_ATTACHMENT,  // Bottom right brush attachement
  SPECIAL_NEXT_FORECOLOR,           // Next foreground color
  SPECIAL_PREVIOUS_FORECOLOR,       // Previous foreground color
  SPECIAL_NEXT_BACKCOLOR,           // Next background color
  SPECIAL_PREVIOUS_BACKCOLOR,       // Previous background color
  SPECIAL_NEXT_USER_FORECOLOR,      // Next user-defined foreground color
  SPECIAL_PREVIOUS_USER_FORECOLOR,  // Previous user-defined foreground color
  SPECIAL_NEXT_USER_BACKCOLOR,      // Next user-defined background color
  SPECIAL_PREVIOUS_USER_BACKCOLOR,  // Previous user-defined background color
  SPECIAL_RETRECIR_PINCEAU,         // Rétrécir le pinceau
  SPECIAL_GROSSIR_PINCEAU           // Grossir le pinceau
};
int Charger_CFG(int Tout_charger)
{
  FILE*  Handle;
  char Nom_du_fichier[TAILLE_CHEMIN_FICHIER];
  long Taille_fichier;
  int  Indice,Indice2;
  struct Config_Header       CFG_Header;
  struct Config_Chunk        Chunk;
  struct Config_Infos_touche CFG_Infos_touche;
  struct Config_Mode_video   CFG_Mode_video;
  struct stat Informations_Fichier;
  int Conversion_touches = 0;
  strcpy(Nom_du_fichier,Repertoire_de_configuration);
  strcat(Nom_du_fichier,"gfx2.cfg");
  stat(Nom_du_fichier,&Informations_Fichier);
  Taille_fichier=Informations_Fichier.st_size;
  if ((Handle=fopen(Nom_du_fichier,"rb"))==NULL)
    return ERREUR_CFG_ABSENT;
  if ( (Taille_fichier<(long)sizeof(CFG_Header))
    || (!read_bytes(Handle, &CFG_Header.Signature, 3))
    || memcmp(CFG_Header.Signature,"CFG",3)
    || (!read_byte(Handle, &CFG_Header.Version1))
    || (!read_byte(Handle, &CFG_Header.Version2))
    || (!read_byte(Handle, &CFG_Header.Beta1))
    || (!read_byte(Handle, &CFG_Header.Beta2)) )
      goto Erreur_lecture_config;
  // Version DOS de Robinson et X-Man
  if ( (CFG_Header.Version1== 2)
    && (CFG_Header.Version2== 0)
    && (CFG_Header.Beta1== 96))
  {
    // Les touches (scancodes) sont à convertir)
    Conversion_touches = 1;
  }
  // Version SDL
  else if ( (CFG_Header.Version1!=VERSION1)
    || (CFG_Header.Version2!=VERSION2)
    || (CFG_Header.Beta1!=BETA1)
    || (CFG_Header.Beta2!=BETA2) )
    goto Erreur_config_ancienne;
  // - Lecture des infos contenues dans le fichier de config -
  while (read_byte(Handle, &Chunk.Numero))
  {
    read_word_le(Handle, &Chunk.Taille);
    switch (Chunk.Numero)
    {
      case CHUNK_TOUCHES: // Touches
        if (Tout_charger)
        {
          for (Indice=0; Indice<(long)(Chunk.Taille/sizeof(CFG_Infos_touche)); Indice++)
          {
            if (!read_word_le(Handle, &CFG_Infos_touche.Numero) ||
                !read_word_le(Handle, &CFG_Infos_touche.Touche) ||
                !read_word_le(Handle, &CFG_Infos_touche.Touche2) )
              goto Erreur_lecture_config;
            else
            {
              if (Conversion_touches)
              {
                CFG_Infos_touche.Touche = Touche_pour_scancode(CFG_Infos_touche.Touche);
              }
              for (Indice2=0;
                 ((Indice2>8)
                {
                  case 0 :
                    Config_Touche[Ordonnancement[Indice2]&0xFF]=CFG_Infos_touche.Touche;
                    break;
                  case 1 :
                    Bouton[Ordonnancement[Indice2]&0xFF].Raccourci_gauche = CFG_Infos_touche.Touche;
                    break;
                  case 2 :
                    Bouton[Ordonnancement[Indice2]&0xFF].Raccourci_droite = CFG_Infos_touche.Touche;
                    break;
                }
              }
              else
                goto Erreur_lecture_config;
            }
          }
        }
        else
        {
          if (fseek(Handle,Chunk.Taille,SEEK_CUR)==-1)
            goto Erreur_lecture_config;
        }
        break;
      case CHUNK_MODES_VIDEO: // Modes vidéo
        for (Indice=0; Indice<(long)(Chunk.Taille/sizeof(CFG_Mode_video)); Indice++)
        {
          if (!read_byte(Handle, &CFG_Mode_video.Etat) ||
              !read_word_le(Handle, &CFG_Mode_video.Largeur) ||
              !read_word_le(Handle, &CFG_Mode_video.Hauteur) )
            goto Erreur_lecture_config;
          for (Indice2=1; Indice2>8)
    {
      case 0 : CFG_Infos_touche.Touche=Config_Touche[Ordonnancement[Indice]&0xFF]; break;
      case 1 : CFG_Infos_touche.Touche=Bouton[Ordonnancement[Indice]&0xFF].Raccourci_gauche; break;
      case 2 : CFG_Infos_touche.Touche=Bouton[Ordonnancement[Indice]&0xFF].Raccourci_droite; break;
    }
    CFG_Infos_touche.Touche2=0x00FF;
    if (!write_word_le(Handle, CFG_Infos_touche.Numero) ||
        !write_word_le(Handle, CFG_Infos_touche.Touche) ||
        !write_word_le(Handle, CFG_Infos_touche.Touche2) )
      goto Erreur_sauvegarde_config;
  }
  // D'abord compter les modes pour lesquels l'utilisateur a mis une préférence
  Modes_a_sauver=0;
  for (Indice=1; Indice>8)
    {
      case 0 :
        Config_Touche[Ordonnancement[Indice]&0xFF]=ConfigTouche[Indice].Touche;
        break;
      case 1 :
        Bouton[Ordonnancement[Indice]&0xFF].Raccourci_gauche = ConfigTouche[Indice].Touche;
        break;
      case 2 :
        Bouton[Ordonnancement[Indice]&0xFF].Raccourci_droite = ConfigTouche[Indice].Touche;
        break;
    }
  }
  // Shade
  Shade_Actuel=0;
  for (Indice=0; Indice<8; Indice++)
  {
    Shade_Liste[Indice].Pas=1;
    Shade_Liste[Indice].Mode=0;
    for (Indice2=0; Indice2<512; Indice2++)
      Shade_Liste[Indice].Liste[Indice2]=256;
  }
  // Shade par défaut pour la palette standard
  for (Indice=0; Indice<7; Indice++)
    for (Indice2=0; Indice2<16; Indice2++)
      Shade_Liste[0].Liste[Indice*17+Indice2]=Indice*16+Indice2+16;
  Liste2tables(Shade_Liste[Shade_Actuel].Liste,
            Shade_Liste[Shade_Actuel].Pas,
            Shade_Liste[Shade_Actuel].Mode,
            Shade_Table_gauche,Shade_Table_droite);
  // Masque
  for (Indice=0; Indice<256; Indice++)
    Mask_table[Indice]=0;
  // Stencil
  for (Indice=0; Indice<256; Indice++)
    Stencil[Indice]=1;
  // Dégradés
  Degrade_Courant=0;
  for(Indice=0;Indice<16;Indice++)
  {
    Degrade_Tableau[Indice].Debut=0;
    Degrade_Tableau[Indice].Fin=0;
    Degrade_Tableau[Indice].Inverse=0;
    Degrade_Tableau[Indice].Melange=0;
    Degrade_Tableau[Indice].Technique=0;
  }
  Degrade_Charger_infos_du_tableau(Degrade_Courant);
  // Smooth
  Smooth_Matrice[0][0]=1;
  Smooth_Matrice[0][1]=2;
  Smooth_Matrice[0][2]=1;
  Smooth_Matrice[1][0]=2;
  Smooth_Matrice[1][1]=4;
  Smooth_Matrice[1][2]=2;
  Smooth_Matrice[2][0]=1;
  Smooth_Matrice[2][1]=2;
  Smooth_Matrice[2][2]=1;
  // Exclude colors
  for (Indice=0; Indice<256; Indice++)
    Exclude_color[Indice]=0;
  // Quick shade
  Quick_shade_Step=1;
  Quick_shade_Loop=0;
  // Grille
  Snap_Largeur=Snap_Hauteur=8;
  Snap_Decalage_X=Snap_Decalage_Y=0;
}