/*  Grafx2 - The Ultimate 256-color bitmap paint program
    Copyright 2008 Peter Gordon
    Copyright 2008 Yves Rizoud
    Copyright 2009 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 
*/
// Signal handler: I activate it for the two platforms who certainly
// support them. Feel free to check with others.
#if defined(__WIN32__) || defined(__linux__)
  #define GRAFX2_CATCHES_SIGNALS
#endif
#include 
//#include 
#include 
#ifndef __VBCC__
    #include 
#endif
#include 
#include 
#include 
#include 
#if defined(__WIN32__)
  #include  // GetLogicalDrives(), GetDriveType(), DRIVE_*
#endif
#if defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__) || defined(__amigaos__)
  #include 
  #include 
#endif
#ifdef GRAFX2_CATCHES_SIGNALS
  #include 
#endif
#include "const.h"
#include "struct.h"
#include "global.h"
#include "graph.h"
#include "buttons.h"
#include "palette.h"
#include "help.h"
#include "operatio.h"
#include "misc.h"
#include "errors.h"
#include "keyboard.h"
#include "io.h"
#include "hotkeys.h"
#include "setup.h"
#include "windows.h"
#include "sdlscreen.h"
#include "mountlist.h" // read_file_system_list
#include "loadsave.h" // Image_emergency_backup
#include "init.h"
#include "transform.h"
char Gui_loading_error_message[512];
// Rechercher la liste et le type des lecteurs de la machine
#if defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__) || defined(__amigaos__)
void bstrtostr( BSTR in, STRPTR out, TEXT max );
#endif
// Fonctions de lecture dans la skin de l'interface graphique
byte GUI_seek_down(SDL_Surface *gui, int *start_x, int *start_y, byte neutral_color,char * section)
{
  byte color;
  int y;
  y=*start_y;
  *start_x=0;
  do
  {
    color=Get_SDL_pixel_8(gui,*start_x,y);
    if (color!=neutral_color)
    {
      *start_y=y;
      return 0;
    }
    y++;
  } while (yh);
  
  sprintf(Gui_loading_error_message, "Error in skin file: Was looking down from %d,%d for a '%s', and reached the end of the image\n",
    *start_x, *start_y, section);
  return 1;
}
byte GUI_seek_right(SDL_Surface *gui, int *start_x, int start_y, byte neutral_color, char * section)
{
  byte color;
  int x;
  x=*start_x;
  
  do
  {
    color=Get_SDL_pixel_8(gui,x,start_y);
    if (color!=neutral_color)
    {
      *start_x=x;
      return 0;
    }
    x++;
  } while (xw);
  
  sprintf(Gui_loading_error_message, "Error in skin file: Was looking right from %d,%d for a '%s', and reached the edege of the image\n",
    *start_x, start_y, section);
  return 1;
}
byte Read_GUI_block(SDL_Surface *gui, int start_x, int start_y, void *dest, int width, int height, 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 MC_White and MC_Trans)
  // type: 3 = raw bitmap (splash screen)
  
  byte * dest_ptr=dest;
  int x,y;
  byte color;
  // Verification taille
  if (start_y+height>=gui->h || start_x+width>=gui->w)
  {
    sprintf(Gui_loading_error_message, "Error in skin file: Was looking at %d,%d for a %d*%d object (%s) but it doesn't fit the image.\n",
      start_x, start_y, height, width, section);
    return 1;
  }
  for (y=start_y; yCursor_offset_X[cursor_number]=14-start_x;
  gfx->Cursor_offset_Y[cursor_number]=14-start_y;
  for (y=0;yCursor_sprite[cursor_number][y][x]=cursor_buffer[(start_y+y)*29+start_x+x];
}
byte Parse_skin(SDL_Surface * gui, T_Gui_skin *gfx)
{
  int  index;
  int i;
  int cursor_x=0,cursor_y=0;
  byte color;
  byte neutral_color; // color neutre utilisée pour délimiter les éléments GUI
  int char_1=0;  // Indices utilisés pour les 4 "fontes" qui composent les
  int char_2=0;  // grands titres de l'aide. Chaque indice avance dans 
  int char_3=0;  // l'une des fontes dans l'ordre :  1 2
  int char_4=0;  //                                  3 4
  byte mouse_cursor_area[29][29];
  SDL_Palette * SDLPal;
  
  // Default palette
  if (!gui->format || gui->format->BitsPerPixel != 8)
  {
    sprintf(Gui_loading_error_message, "Not a 8-bit image");
    return 1;
  }
  SDLPal=gui->format->palette;
  if (!SDLPal || SDLPal->ncolors!=256)
  {
    sprintf(Gui_loading_error_message, "Not a 256-color palette");
    return 1;
  }
  // Read the default palette
  for (i=0; i<256; i++)
  {
    gfx->Default_palette[i].R=SDLPal->colors[i].r;
    gfx->Default_palette[i].G=SDLPal->colors[i].g;
    gfx->Default_palette[i].B=SDLPal->colors[i].b;
  }
  // Carré "noir"
  MC_Black = Get_SDL_pixel_8(gui,cursor_x,cursor_y);
  do
  {
    if (++cursor_x>=gui->w)
    {
      sprintf(Gui_loading_error_message, "Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      return 1;
    }
    color=Get_SDL_pixel_8(gui,cursor_x,cursor_y);
  } while(color==MC_Black);
  // Carré "foncé"
  MC_Dark=color;
  do
  {
    if (++cursor_x>=gui->w)
    {
      sprintf(Gui_loading_error_message, "Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      return 1;
    }
    color=Get_SDL_pixel_8(gui,cursor_x,cursor_y);
  } while(color==MC_Dark);
  // Carré "clair"
  MC_Light=color;
  do
  {
    if (++cursor_x>gui->w)
    {
      sprintf(Gui_loading_error_message, "Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      return 1;
    }
    color=Get_SDL_pixel_8(gui,cursor_x,cursor_y);
  } while(color==MC_Light);
  // Carré "blanc"
  MC_White=color;
  do
  {
    if (++cursor_x>=gui->w)
    {
      sprintf(Gui_loading_error_message, "Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      return 1;
    }
    color=Get_SDL_pixel_8(gui,cursor_x,cursor_y);
  } while(color==MC_White);
  // Carré "transparent"
  MC_Trans=color;
  do
  {
    if (++cursor_x>=gui->w)
    {
      sprintf(Gui_loading_error_message, "Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      return 1;
    }
    color=Get_SDL_pixel_8(gui,cursor_x,cursor_y);
  } while(color==MC_Trans);
  // Reste : couleur neutre
  neutral_color=color;
  
  cursor_x=0;
  cursor_y=1;
  while ((color=Get_SDL_pixel_8(gui,cursor_x,cursor_y))==MC_Black)
  {
    cursor_y++;
    if (cursor_y>=gui->h)
    {
      sprintf(Gui_loading_error_message, "Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
      return 1;
    }
  }
  
  // Menu
  if (GUI_seek_down(gui, &cursor_x, &cursor_y, neutral_color, "menu"))
    return 1;
  if (Read_GUI_block(gui, cursor_x, cursor_y, gfx->Menu_block, MENU_WIDTH, MENU_HEIGHT,"menu",0))
    return 1;
  cursor_y+=MENU_HEIGHT;
  // Effets
  for (i=0; iEffect_sprite[i], MENU_SPRITE_WIDTH, MENU_SPRITE_HEIGHT, "effect sprite",0))
      return 1;
    cursor_x+=MENU_SPRITE_WIDTH;
  }
  cursor_y+=MENU_SPRITE_HEIGHT;
  
  // Curseurs souris
  for (i=0; iMenu_sprite[i], MENU_SPRITE_WIDTH, MENU_SPRITE_HEIGHT, "menu sprite",1))
      return 1;
    cursor_x+=MENU_SPRITE_WIDTH;
  }
  cursor_y+=MENU_SPRITE_HEIGHT;
  
  // Icones des Pinceaux
  for (i=0; iPaintbrush_sprite[i], PAINTBRUSH_WIDTH, PAINTBRUSH_HEIGHT, "brush icon",2))
      return 1;
    cursor_x+=PAINTBRUSH_WIDTH;
  }
  cursor_y+=PAINTBRUSH_HEIGHT;
  // Sprites drive
  for (i=0; iIcon_sprite[i], ICON_SPRITE_WIDTH, ICON_SPRITE_HEIGHT, "sprite drive",1))
      return 1;
    cursor_x+=ICON_SPRITE_WIDTH;
  }
  cursor_y+=ICON_SPRITE_HEIGHT;
  // Logo splash screen
  if (GUI_seek_down(gui, &cursor_x, &cursor_y, neutral_color, "logo menu"))
    return 1;
  if (Read_GUI_block(gui, cursor_x, cursor_y, gfx->Logo_grafx2, 231, 56, "logo menu",3))
    return 1;
  cursor_y+=56;
  
  // Trames
  for (i=0; iSieve_pattern[i],"sieve pattern"))
      return 1;
    cursor_x+=16;
  }
  cursor_y+=16;
  // Font help normale
  for (i=0; i<256; i++)
  {
    // Rangés par ligne de 32
    if ((i%32)==0)
    {
      if (i!=0)
        cursor_y+=8;
      if (GUI_seek_down(gui, &cursor_x, &cursor_y, neutral_color, "help font (norm)"))
        return 1;
    }
    else
    {
      if (GUI_seek_right(gui, &cursor_x, cursor_y, neutral_color, "help font (norm)"))
        return 1;
    }
    if (Read_GUI_block(gui, cursor_x, cursor_y, &(gfx->Help_font_norm[i][0][0]), 6, 8, "help font (norm)",0))
      return 1;
    cursor_x+=6;
  }
  cursor_y+=8;
  
  // Font help bold
  for (i=0; i<256; i++)
  {
    // Rangés par ligne de 32
    if ((i%32)==0)
    {
      if (i!=0)
        cursor_y+=8;
      if (GUI_seek_down(gui, &cursor_x, &cursor_y, neutral_color, "help font (bold)"))
        return 1;
    }
    else
    {
      if (GUI_seek_right(gui, &cursor_x, cursor_y, neutral_color, "help font (bold)"))
        return 1;
    }
    if (Read_GUI_block(gui, cursor_x, cursor_y, &(gfx->Bold_font[i][0][0]), 6, 8, "help font (bold)",0))
      return 1;
    cursor_x+=6;
  }
  cursor_y+=8;
  // Font help titre
  for (i=0; i<256; i++)
  {
    byte * dest;
    // Rangés par ligne de 64
    if ((i%64)==0)
    {
      if (i!=0)
        cursor_y+=8;
      if (GUI_seek_down(gui, &cursor_x, &cursor_y, neutral_color, "help font (title)"))
        return 1;
    }
    else
    {
      if (GUI_seek_right(gui, &cursor_x, cursor_y, neutral_color, "help font (title)"))
        return 1;
    }
    
    if (i&1)
      if (i&64)
        dest=&(gfx->Help_font_t4[char_4++][0][0]);
      else
        dest=&(gfx->Help_font_t2[char_2++][0][0]);
    else
      if (i&64)
        dest=&(gfx->Help_font_t3[char_3++][0][0]);
      else
        dest=&(gfx->Help_font_t1[char_1++][0][0]);
    
    if (Read_GUI_block(gui, cursor_x, cursor_y, dest, 6, 8, "help font (title)",0))
      return 1;
    cursor_x+=6;
  }
  cursor_y+=8;
  
  Current_help_section=0;
  Help_position=0;
  gfx->Preset_paintbrush_width [ 0]= 1;
  gfx->Preset_paintbrush_height[ 0]= 1;
  gfx->Paintbrush_type         [ 0]=PAINTBRUSH_SHAPE_SQUARE;
  gfx->Preset_paintbrush_width [ 1]= 2;
  gfx->Preset_paintbrush_height[ 1]= 2;
  gfx->Paintbrush_type         [ 1]=PAINTBRUSH_SHAPE_SQUARE;
  gfx->Preset_paintbrush_width [ 2]= 3;
  gfx->Preset_paintbrush_height[ 2]= 3;
  gfx->Paintbrush_type         [ 2]=PAINTBRUSH_SHAPE_SQUARE;
  gfx->Preset_paintbrush_width [ 3]= 4;
  gfx->Preset_paintbrush_height[ 3]= 4;
  gfx->Paintbrush_type         [ 3]=PAINTBRUSH_SHAPE_SQUARE;
  gfx->Preset_paintbrush_width [ 4]= 5;
  gfx->Preset_paintbrush_height[ 4]= 5;
  gfx->Paintbrush_type         [ 4]=PAINTBRUSH_SHAPE_SQUARE;
  gfx->Preset_paintbrush_width [ 5]= 7;
  gfx->Preset_paintbrush_height[ 5]= 7;
  gfx->Paintbrush_type         [ 5]=PAINTBRUSH_SHAPE_SQUARE;
  gfx->Preset_paintbrush_width [ 6]= 8;
  gfx->Preset_paintbrush_height[ 6]= 8;
  gfx->Paintbrush_type         [ 6]=PAINTBRUSH_SHAPE_SQUARE;
  gfx->Preset_paintbrush_width [ 7]=12;
  gfx->Preset_paintbrush_height[ 7]=12;
  gfx->Paintbrush_type         [ 7]=PAINTBRUSH_SHAPE_SQUARE;
  gfx->Preset_paintbrush_width [ 8]=16;
  gfx->Preset_paintbrush_height[ 8]=16;
  gfx->Paintbrush_type         [ 8]=PAINTBRUSH_SHAPE_SQUARE;
  gfx->Preset_paintbrush_width [ 9]=16;
  gfx->Preset_paintbrush_height[ 9]=16;
  gfx->Paintbrush_type         [ 9]=PAINTBRUSH_SHAPE_SIEVE_SQUARE;
  gfx->Preset_paintbrush_width [10]=15;
  gfx->Preset_paintbrush_height[10]=15;
  gfx->Paintbrush_type         [10]=PAINTBRUSH_SHAPE_DIAMOND;
  gfx->Preset_paintbrush_width [11]= 5;
  gfx->Preset_paintbrush_height[11]= 5;
  gfx->Paintbrush_type         [11]=PAINTBRUSH_SHAPE_DIAMOND;
  gfx->Preset_paintbrush_width [12]= 3;
  gfx->Preset_paintbrush_height[12]= 3;
  gfx->Paintbrush_type         [12]=PAINTBRUSH_SHAPE_ROUND;
  gfx->Preset_paintbrush_width [13]= 4;
  gfx->Preset_paintbrush_height[13]= 4;
  gfx->Paintbrush_type         [13]=PAINTBRUSH_SHAPE_ROUND;
  gfx->Preset_paintbrush_width [14]= 5;
  gfx->Preset_paintbrush_height[14]= 5;
  gfx->Paintbrush_type         [14]=PAINTBRUSH_SHAPE_ROUND;
  gfx->Preset_paintbrush_width [15]= 6;
  gfx->Preset_paintbrush_height[15]= 6;
  gfx->Paintbrush_type         [15]=PAINTBRUSH_SHAPE_ROUND;
  gfx->Preset_paintbrush_width [16]= 8;
  gfx->Preset_paintbrush_height[16]= 8;
  gfx->Paintbrush_type         [16]=PAINTBRUSH_SHAPE_ROUND;
  gfx->Preset_paintbrush_width [17]=10;
  gfx->Preset_paintbrush_height[17]=10;
  gfx->Paintbrush_type         [17]=PAINTBRUSH_SHAPE_ROUND;
  gfx->Preset_paintbrush_width [18]=12;
  gfx->Preset_paintbrush_height[18]=12;
  gfx->Paintbrush_type         [18]=PAINTBRUSH_SHAPE_ROUND;
  gfx->Preset_paintbrush_width [19]=14;
  gfx->Preset_paintbrush_height[19]=14;
  gfx->Paintbrush_type         [19]=PAINTBRUSH_SHAPE_ROUND;
  gfx->Preset_paintbrush_width [20]=16;
  gfx->Preset_paintbrush_height[20]=16;
  gfx->Paintbrush_type         [20]=PAINTBRUSH_SHAPE_ROUND;
  gfx->Preset_paintbrush_width [21]=15;
  gfx->Preset_paintbrush_height[21]=15;
  gfx->Paintbrush_type         [21]=PAINTBRUSH_SHAPE_SIEVE_ROUND;
  gfx->Preset_paintbrush_width [22]=11;
  gfx->Preset_paintbrush_height[22]=11;
  gfx->Paintbrush_type         [22]=PAINTBRUSH_SHAPE_SIEVE_ROUND;
  gfx->Preset_paintbrush_width [23]= 5;
  gfx->Preset_paintbrush_height[23]= 5;
  gfx->Paintbrush_type         [23]=PAINTBRUSH_SHAPE_SIEVE_ROUND;
  gfx->Preset_paintbrush_width [24]= 2;
  gfx->Preset_paintbrush_height[24]= 1;
  gfx->Paintbrush_type         [24]=PAINTBRUSH_SHAPE_HORIZONTAL_BAR;
  gfx->Preset_paintbrush_width [25]= 3;
  gfx->Preset_paintbrush_height[25]= 1;
  gfx->Paintbrush_type         [25]=PAINTBRUSH_SHAPE_HORIZONTAL_BAR;
  gfx->Preset_paintbrush_width [26]= 4;
  gfx->Preset_paintbrush_height[26]= 1;
  gfx->Paintbrush_type         [26]=PAINTBRUSH_SHAPE_HORIZONTAL_BAR;
  gfx->Preset_paintbrush_width [27]= 8;
  gfx->Preset_paintbrush_height[27]= 1;
  gfx->Paintbrush_type         [27]=PAINTBRUSH_SHAPE_HORIZONTAL_BAR;
  gfx->Preset_paintbrush_width [28]= 1;
  gfx->Preset_paintbrush_height[28]= 2;
  gfx->Paintbrush_type         [28]=PAINTBRUSH_SHAPE_VERTICAL_BAR;
  gfx->Preset_paintbrush_width [29]= 1;
  gfx->Preset_paintbrush_height[29]= 3;
  gfx->Paintbrush_type         [29]=PAINTBRUSH_SHAPE_VERTICAL_BAR;
  gfx->Preset_paintbrush_width [30]= 1;
  gfx->Preset_paintbrush_height[30]= 4;
  gfx->Paintbrush_type         [30]=PAINTBRUSH_SHAPE_VERTICAL_BAR;
  gfx->Preset_paintbrush_width [31]= 1;
  gfx->Preset_paintbrush_height[31]= 8;
  gfx->Paintbrush_type         [31]=PAINTBRUSH_SHAPE_VERTICAL_BAR;
  gfx->Preset_paintbrush_width [32]= 3;
  gfx->Preset_paintbrush_height[32]= 3;
  gfx->Paintbrush_type         [32]=PAINTBRUSH_SHAPE_CROSS;
  gfx->Preset_paintbrush_width [33]= 5;
  gfx->Preset_paintbrush_height[33]= 5;
  gfx->Paintbrush_type         [33]=PAINTBRUSH_SHAPE_CROSS;
  gfx->Preset_paintbrush_width [34]= 5;
  gfx->Preset_paintbrush_height[34]= 5;
  gfx->Paintbrush_type         [34]=PAINTBRUSH_SHAPE_PLUS;
  gfx->Preset_paintbrush_width [35]=15;
  gfx->Preset_paintbrush_height[35]=15;
  gfx->Paintbrush_type         [35]=PAINTBRUSH_SHAPE_PLUS;
  gfx->Preset_paintbrush_width [36]= 2;
  gfx->Preset_paintbrush_height[36]= 2;
  gfx->Paintbrush_type         [36]=PAINTBRUSH_SHAPE_SLASH;
  gfx->Preset_paintbrush_width [37]= 4;
  gfx->Preset_paintbrush_height[37]= 4;
  gfx->Paintbrush_type         [37]=PAINTBRUSH_SHAPE_SLASH;
  gfx->Preset_paintbrush_width [38]= 8;
  gfx->Preset_paintbrush_height[38]= 8;
  gfx->Paintbrush_type         [38]=PAINTBRUSH_SHAPE_SLASH;
  gfx->Preset_paintbrush_width [39]= 2;
  gfx->Preset_paintbrush_height[39]= 2;
  gfx->Paintbrush_type         [39]=PAINTBRUSH_SHAPE_ANTISLASH;
  gfx->Preset_paintbrush_width [40]= 4;
  gfx->Preset_paintbrush_height[40]= 4;
  gfx->Paintbrush_type         [40]=PAINTBRUSH_SHAPE_ANTISLASH;
  gfx->Preset_paintbrush_width [41]= 8;
  gfx->Preset_paintbrush_height[41]= 8;
  gfx->Paintbrush_type         [41]=PAINTBRUSH_SHAPE_ANTISLASH;
  gfx->Preset_paintbrush_width [42]= 4;
  gfx->Preset_paintbrush_height[42]= 4;
  gfx->Paintbrush_type         [42]=PAINTBRUSH_SHAPE_RANDOM;
  gfx->Preset_paintbrush_width [43]= 8;
  gfx->Preset_paintbrush_height[43]= 8;
  gfx->Paintbrush_type         [43]=PAINTBRUSH_SHAPE_RANDOM;
  gfx->Preset_paintbrush_width [44]=13;
  gfx->Preset_paintbrush_height[44]=13;
  gfx->Paintbrush_type         [44]=PAINTBRUSH_SHAPE_RANDOM;
  gfx->Preset_paintbrush_width [45]= 3;
  gfx->Preset_paintbrush_height[45]= 3;
  gfx->Paintbrush_type         [45]=PAINTBRUSH_SHAPE_MISC;
  gfx->Preset_paintbrush_width [46]= 3;
  gfx->Preset_paintbrush_height[46]= 3;
  gfx->Paintbrush_type         [46]=PAINTBRUSH_SHAPE_MISC;
  gfx->Preset_paintbrush_width [47]= 7;
  gfx->Preset_paintbrush_height[47]= 7;
  gfx->Paintbrush_type         [47]=PAINTBRUSH_SHAPE_MISC;
  for (index=0;indexPreset_paintbrush_offset_X[index]=(gfx->Preset_paintbrush_width [index]>>1);
    gfx->Preset_paintbrush_offset_Y[index]=(gfx->Preset_paintbrush_height[index]>>1);
  }
  return 0;
}
T_Gui_skin * Load_graphics(const char * skin_file)
{
  T_Gui_skin * gfx;
  char filename[MAX_PATH_CHARACTERS];
  SDL_Surface * gui;
  gfx = (T_Gui_skin *)malloc(sizeof(T_Gui_skin));
  if (gfx == NULL)
  {
    sprintf(Gui_loading_error_message, "Not enough memory to read skin file\n");
    return NULL;
  }
  
  // Read the "skin" file
  strcpy(filename,Data_directory);
  strcat(filename,"skins" PATH_SEPARATOR);
  strcat(filename,skin_file);
  
  gui=IMG_Load(filename);
  if (!gui)
  {
    sprintf(Gui_loading_error_message, "Unable to load the skin image (missing? not an image file?)\n");
    free(gfx);
    return NULL;
  }
  if (Parse_skin(gui, gfx))
  {
    SDL_FreeSurface(gui);
    free(gfx);
    return NULL;
  }
  SDL_FreeSurface(gui);
  return gfx;
}
// ---- font loading -----
byte Parse_font(SDL_Surface * image, byte * font)
{
  int character;
  byte color;
  int x, y;
  int chars_per_line;
  
  // Check image size
  if (image->w % 8)
  {
    sprintf(Gui_loading_error_message, "Error in font file: Image width is not a multiple of 8.\n");
    return 1;
  }
  if (image->w * image->h < 8*8*256)
  {
    sprintf(Gui_loading_error_message, "Error in font file: Image is too small to be a 256-character 8x8 font.\n");
    return 1;
  }
  chars_per_line = image->w/8;
  for (character=0; character < 256; character++)
  {
    for (y=0; y<8; y++)
    {
      for (x=0;x<8; x++)
      {
        // Pick pixel
        color = Get_SDL_pixel_8(image, (character % chars_per_line)*8+x, (character / chars_per_line)*8+y);
        if (color > 1)
        {
          sprintf(Gui_loading_error_message, "Error in font file: Only colors 0 and 1 can be used for the font.\n");
          return 1;
        }
        // Put it in font. 0 = BG, 1 = FG.
        font[character*64 + y*8 + x]=color;
      }
    }
  }
  return 0;
}
byte * Load_font(const char * font_name)
{
  byte * font;
  char filename[MAX_PATH_CHARACTERS];
  SDL_Surface * image;
  font = (byte *)malloc(8*8*256);
  if (font == NULL)
  {
    sprintf(Gui_loading_error_message, "Not enough memory to read font file\n");
    return NULL;
  }
  
  // Read the file containing the image
  sprintf(filename,"%sskins%s%s", Data_directory, PATH_SEPARATOR, font_name);
  
  image=IMG_Load(filename);
  if (!image)
  {
    sprintf(Gui_loading_error_message, "Unable to load the skin image (missing? not an image file?)\n");
    free(font);
    return NULL;
  }
  if (Parse_font(image, font))
  {
    SDL_FreeSurface(image);
    free(font);
    return NULL;
  }
  SDL_FreeSurface(image);
  return font;
}
// Initialisation des boutons:
  // Action factice:
void Do_nothing(void)
{}
  // Initialiseur d'un bouton:
void Init_button(byte   btn_number,
                        word   x_offset      , word   y_offset,
                        word   width         , word   height,
                        byte   shape,
                        Func_action left_action,
                        Func_action right_action,
                        Func_action unselect_action,
                        byte   family)
{
  Buttons_Pool[btn_number].X_offset        =x_offset;
  Buttons_Pool[btn_number].Y_offset        =y_offset;
  Buttons_Pool[btn_number].Width           =width-1;
  Buttons_Pool[btn_number].Height          =height-1;
  Buttons_Pool[btn_number].Pressed         =0;
  Buttons_Pool[btn_number].Shape           =shape;
  Buttons_Pool[btn_number].Left_action     =left_action;
  Buttons_Pool[btn_number].Right_action    =right_action;
  Buttons_Pool[btn_number].Unselect_action =unselect_action;
  Buttons_Pool[btn_number].Family          =family;
}
  // Initiliseur de tous les boutons:
void Init_buttons(void)
{
  byte button_index;
  for (button_index=0;button_index= MAX_VIDEO_MODES-1)
  {
    DEBUG("Error! Attempt to create too many videomodes. Maximum is:", MAX_VIDEO_MODES);
    return;
  }
  if (!fullscreen)
    supported = 128; // Prefere, non modifiable
  else if (SDL_VideoModeOK(width, height, 8, SDL_FULLSCREEN))
    supported = 1; // supported
  else
  {
    // Non supporte : on ne le prend pas
    return;
  }
  Video_mode[Nb_video_modes].Width          = width;
  Video_mode[Nb_video_modes].Height          = height;
  Video_mode[Nb_video_modes].Mode             = mode;
  Video_mode[Nb_video_modes].Fullscreen       = fullscreen;
  Video_mode[Nb_video_modes].State                   = supported;
  Nb_video_modes ++;
}
// Utilisé pour trier les modes retournés par SDL
int Compare_video_modes(const void *p1, const void *p2)
{
  const T_Video_mode *mode1 = (const T_Video_mode *)p1;
  const T_Video_mode *mode2 = (const T_Video_mode *)p2;
  // Tris par largeur
  if(mode1->Width - mode2->Width)
    return mode1->Width - mode2->Width;
  // Tri par hauteur
  return mode1->Height - mode2->Height;
}
// Initializes the list of available video modes
void Set_all_video_modes(void)
{
  SDL_Rect** Modes;
  Nb_video_modes=0;
  
  // The first mode will have index number 0.
  // It will be the default mode if an unsupported one
  // is requested in gfx2.ini
  #if defined(__GP2X__)
  // Native GP2X resolution
  Set_video_mode( 320,240,0, 1);
  #else
  // Window mode, with default size of 640x480
  Set_video_mode( 640,480,0, 0);
  #endif
  Set_video_mode( 320,200,0, 1);
  Set_video_mode( 320,224,0, 1);
  #if !defined(__GP2X__)
  // For the GP2X, this one is already declared above.
  Set_video_mode( 320,240,0, 1);
  #endif
  Set_video_mode( 320,256,0, 1);
  Set_video_mode( 320,270,0, 1);
  Set_video_mode( 320,282,0, 1);
  Set_video_mode( 320,300,0, 1);
  Set_video_mode( 320,360,0, 1);
  Set_video_mode( 320,400,0, 1);
  Set_video_mode( 320,448,0, 1);
  Set_video_mode( 320,480,0, 1);
  Set_video_mode( 320,512,0, 1);
  Set_video_mode( 320,540,0, 1);
  Set_video_mode( 320,564,0, 1);
  Set_video_mode( 320,600,0, 1);
  Set_video_mode( 360,200,0, 1);
  Set_video_mode( 360,224,0, 1);
  Set_video_mode( 360,240,0, 1);
  Set_video_mode( 360,256,0, 1);
  Set_video_mode( 360,270,0, 1);
  Set_video_mode( 360,282,0, 1);
  Set_video_mode( 360,300,0, 1);
  Set_video_mode( 360,360,0, 1);
  Set_video_mode( 360,400,0, 1);
  Set_video_mode( 360,448,0, 1);
  Set_video_mode( 360,480,0, 1);
  Set_video_mode( 360,512,0, 1);
  Set_video_mode( 360,540,0, 1);
  Set_video_mode( 360,564,0, 1);
  Set_video_mode( 360,600,0, 1);
  Set_video_mode( 400,200,0, 1);
  Set_video_mode( 400,224,0, 1);
  Set_video_mode( 400,240,0, 1);
  Set_video_mode( 400,256,0, 1);
  Set_video_mode( 400,270,0, 1);
  Set_video_mode( 400,282,0, 1);
  Set_video_mode( 400,300,0, 1);
  Set_video_mode( 400,360,0, 1);
  Set_video_mode( 400,400,0, 1);
  Set_video_mode( 400,448,0, 1);
  Set_video_mode( 400,480,0, 1);
  Set_video_mode( 400,512,0, 1);
  Set_video_mode( 400,540,0, 1);
  Set_video_mode( 400,564,0, 1);
  Set_video_mode( 400,600,0, 1);
  Set_video_mode( 640,224,0, 1);
  Set_video_mode( 640,240,0, 1);
  Set_video_mode( 640,256,0, 1);
  Set_video_mode( 640,270,0, 1);
  Set_video_mode( 640,300,0, 1);
  Set_video_mode( 640,350,0, 1);
  Set_video_mode( 640,400,0, 1);
  Set_video_mode( 640,448,0, 1);
  Set_video_mode( 640,480,0, 1);
  Set_video_mode( 640,512,0, 1);
  Set_video_mode( 640,540,0, 1);
  Set_video_mode( 640,564,0, 1);
  Set_video_mode( 640,600,0, 1);
  Set_video_mode( 800,600,0, 1);
  Set_video_mode(1024,768,0, 1);
  Modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
  if ((Modes != (SDL_Rect**)0) && (Modes!=(SDL_Rect**)-1))
  {
    int index;
    for (index=0; Modes[index]; index++)
    {
      int index2;
#if defined(__GP2X__)
	  // On the GP2X the first mode is not windowed, so include it in the search.
	  index2=0;
#else
	  index2=1;
#endif
      for (/**/; index2 < Nb_video_modes; index2++)
        if (Modes[index]->w == Video_mode[index2].Width &&
            Modes[index]->h == Video_mode[index2].Height)
        {
          // Was already in the hard-coded list: ok, don't add.
          break;
        }
      if (index2 >= Nb_video_modes && Modes[index]->w>=320 && Modes[index]->h>=200)
      {
        // New mode to add to the list
        Set_video_mode(Modes[index]->w,Modes[index]->h,0, 1);
      }
    }
    // Sort the modes : those found by SDL were listed at the end.
    // Note that we voluntarily omit the first entry: the default mode.
    qsort(&Video_mode[1], Nb_video_modes - 1, sizeof(T_Video_mode), Compare_video_modes);
  }
}
//---------------------------------------------------------------------------
int Load_CFG(int reload_all)
{
  FILE*  Handle;
  char filename[MAX_PATH_CHARACTERS];
  long file_size;
  int  index,index2;
  T_Config_header       cfg_header;
  T_Config_chunk        Chunk;
  T_Config_shortcut_info cfg_shortcut_info;
  T_Config_video_mode   cfg_video_mode;
  int key_conversion = 0;
  strcpy(filename,Config_directory);
  strcat(filename,"gfx2.cfg");
  file_size=File_length(filename);
  if ((Handle=fopen(filename,"rb"))==NULL)
    return ERROR_CFG_MISSING;
  if ( (file_size<(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)
    key_conversion = 1;
  }
  // Version SDL jusqu'a 98%
  else if ( (cfg_header.Version1== 2)
    && (cfg_header.Version2== 0)
    && (cfg_header.Beta1== 97))
  {
    // Les touches 00FF (pas de touche) sont a comprendre comme 0x0000
    key_conversion = 2;
  }
  // 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.Number))
  {
    Read_word_le(Handle, &Chunk.Size);
    switch (Chunk.Number)
    {
      case CHUNK_KEYS: // Touches
        if (reload_all)
        {
          for (index=0; index<(long)(Chunk.Size/sizeof(cfg_shortcut_info)); index++)
          {
            if (!Read_word_le(Handle, &cfg_shortcut_info.Number) ||
                !Read_word_le(Handle, &cfg_shortcut_info.Key) ||
                !Read_word_le(Handle, &cfg_shortcut_info.Key2) )
              goto Erreur_lecture_config;
            else
            {
              if (key_conversion==1)
              {
                cfg_shortcut_info.Key = Key_for_scancode(cfg_shortcut_info.Key);
              }
              else if (key_conversion==2)
              {
                if (cfg_shortcut_info.Key == 0x00FF)
                  cfg_shortcut_info.Key = 0x0000;
                if (cfg_shortcut_info.Key2 == 0x00FF)
                  cfg_shortcut_info.Key2 = 0x0000;
              }
              
              for (index2=0;
                 ((index2>8)
                {
                  case 0 :
                    Config_Key[Ordering[index2]&0xFF][0]=cfg_shortcut_info.Key;
                    Config_Key[Ordering[index2]&0xFF][1]=cfg_shortcut_info.Key2;
                    break;
                  case 1 :
                    Buttons_Pool[Ordering[index2]&0xFF].Left_shortcut[0] = cfg_shortcut_info.Key;
                    Buttons_Pool[Ordering[index2]&0xFF].Left_shortcut[1] = cfg_shortcut_info.Key2;
                    break;
                  case 2 :
                    Buttons_Pool[Ordering[index2]&0xFF].Right_shortcut[0] = cfg_shortcut_info.Key;
                    Buttons_Pool[Ordering[index2]&0xFF].Right_shortcut[1] = cfg_shortcut_info.Key2;
                    break;
                }
              }
              else
                goto Erreur_lecture_config;
            }
          }
        }
        else
        {
          if (fseek(Handle,Chunk.Size,SEEK_CUR)==-1)
            goto Erreur_lecture_config;
        }
        break;
      case CHUNK_VIDEO_MODES: // Modes vidéo
        for (index=0; index<(long)(Chunk.Size/sizeof(cfg_video_mode)); index++)
        {
          if (!Read_byte(Handle, &cfg_video_mode.State) ||
              !Read_word_le(Handle, &cfg_video_mode.Width) ||
              !Read_word_le(Handle, &cfg_video_mode.Height) )
            goto Erreur_lecture_config;
#if defined(__GP2X__)
		  index2=0;
#else
		  index2=1;
#endif
          for (/**/; index2>8)
    {
      case 0 :
        cfg_shortcut_info.Key =Config_Key[Ordering[index]&0xFF][0]; 
        cfg_shortcut_info.Key2=Config_Key[Ordering[index]&0xFF][1]; 
        break;
      case 1 :
        cfg_shortcut_info.Key =Buttons_Pool[Ordering[index]&0xFF].Left_shortcut[0]; 
        cfg_shortcut_info.Key2=Buttons_Pool[Ordering[index]&0xFF].Left_shortcut[1]; 
        break;
      case 2 : 
        cfg_shortcut_info.Key =Buttons_Pool[Ordering[index]&0xFF].Right_shortcut[0]; 
        cfg_shortcut_info.Key2=Buttons_Pool[Ordering[index]&0xFF].Right_shortcut[1]; 
        break;
    }
    if (!Write_word_le(Handle, cfg_shortcut_info.Number) ||
        !Write_word_le(Handle, cfg_shortcut_info.Key) ||
        !Write_word_le(Handle, cfg_shortcut_info.Key2) )
      goto Erreur_sauvegarde_config;
  }
  // D'abord compter les modes pour lesquels l'utilisateur a mis une préférence
  modes_to_save=0;
#if defined(__GP2X__)
  index = 0;
#else
  index = 1;
#endif
  for (/**/; index>8)
    {
      case 0 :
        Config_Key[Ordering[index]&0xFF][0]=ConfigKey[index].Key;
        Config_Key[Ordering[index]&0xFF][1]=ConfigKey[index].Key2;
        break;
      case 1 :
        Buttons_Pool[Ordering[index]&0xFF].Left_shortcut[0] = ConfigKey[index].Key;
        Buttons_Pool[Ordering[index]&0xFF].Left_shortcut[1] = ConfigKey[index].Key2;
        break;
      case 2 :
        Buttons_Pool[Ordering[index]&0xFF].Right_shortcut[0] = ConfigKey[index].Key;
        Buttons_Pool[Ordering[index]&0xFF].Right_shortcut[1] = ConfigKey[index].Key2;
        break;
    }
  }
  // Shade
  Shade_current=0;
  for (index=0; index<8; index++)
  {
    Shade_list[index].Step=1;
    Shade_list[index].Mode=0;
    for (index2=0; index2<512; index2++)
      Shade_list[index].List[index2]=256;
  }
  // Shade par défaut pour la palette standard
  for (index=0; index<7; index++)
    for (index2=0; index2<16; index2++)
      Shade_list[0].List[index*17+index2]=index*16+index2+16;
  Shade_list_to_lookup_tables(Shade_list[Shade_current].List,
            Shade_list[Shade_current].Step,
            Shade_list[Shade_current].Mode,
            Shade_table_left,Shade_table_right);
  // Masque
  for (index=0; index<256; index++)
    Mask_table[index]=0;
  // Stencil
  for (index=0; index<256; index++)
    Stencil[index]=1;
  // Dégradés
  Current_gradient=0;
  for(index=0;index<16;index++)
  {
    Gradient_array[index].Start=0;
    Gradient_array[index].End=0;
    Gradient_array[index].Inverse=0;
    Gradient_array[index].Mix=0;
    Gradient_array[index].Technique=0;
  }
  Load_gradient_data(Current_gradient);
  // Smooth
  Smooth_matrix[0][0]=1;
  Smooth_matrix[0][1]=2;
  Smooth_matrix[0][2]=1;
  Smooth_matrix[1][0]=2;
  Smooth_matrix[1][1]=4;
  Smooth_matrix[1][2]=2;
  Smooth_matrix[2][0]=1;
  Smooth_matrix[2][1]=2;
  Smooth_matrix[2][2]=1;
  // Exclude colors
  for (index=0; index<256; index++)
    Exclude_color[index]=0;
  // Quick shade
  Quick_shade_step=1;
  Quick_shade_loop=0;
  // Grille
  Snap_width=Snap_height=8;
  Snap_offset_X=Snap_offset_Y=0;
}
#ifdef GRAFX2_CATCHES_SIGNALS
#if defined(__WIN32__)
  #define SIGHANDLER_T __p_sig_fn_t
#elif defined(__macosx__)
  #define SIGHANDLER_T sig_t
#else
  #define SIGHANDLER_T __sighandler_t
#endif
// Memorize the signal handlers of SDL
SIGHANDLER_T Handler_TERM=SIG_DFL;
SIGHANDLER_T Handler_INT=SIG_DFL;
SIGHANDLER_T Handler_ABRT=SIG_DFL;
SIGHANDLER_T Handler_SEGV=SIG_DFL;
SIGHANDLER_T Handler_FPE=SIG_DFL;
void Sig_handler(int sig)
{
  // Restore default behaviour
  signal(SIGTERM, Handler_TERM);
  signal(SIGINT, Handler_INT);
  signal(SIGABRT, Handler_ABRT);
  signal(SIGSEGV, Handler_SEGV);
  signal(SIGFPE, Handler_FPE);
  
  switch(sig)
  {
    case SIGTERM:
    case SIGINT:
    case SIGABRT:
    case SIGSEGV:
      Image_emergency_backup();
    default:
    break;
   }
}
#endif
void Init_sighandler(void)
{
#ifdef GRAFX2_CATCHES_SIGNALS
  Handler_TERM=signal(SIGTERM,Sig_handler);
  Handler_INT =signal(SIGINT,Sig_handler);
  Handler_ABRT=signal(SIGABRT,Sig_handler);
  Handler_SEGV=signal(SIGSEGV,Sig_handler);
  Handler_FPE =signal(SIGFPE,Sig_handler);
#endif
}
void Init_brush_container(void)
{
  int i;
  
  for (i=0; i