/* 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.
*/
// 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
#include
#include
#include
#include
#include
#include
#if defined(__WIN32__)
#include // GetLogicalDrives(), GetDriveType(), DRIVE_*
#endif
#if defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__)
#include
#include
#endif
#ifdef GRAFX2_CATCHES_SIGNALS
#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 "setup.h"
#include "windows.h"
#include "sdlscreen.h"
#include "mountlist.h" // read_file_system_list
#include "loadsave.h" // Image_emergency_backup
// 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
// Fonctions de lecture dans la skin de l'interface graphique
void 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;
}
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",
*start_x, *start_y, section);
Error(ERROR_GUI_CORRUPTED);
}
void 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;
}
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",
*start_x, start_y, section);
Error(ERROR_GUI_CORRUPTED);
}
void 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)
{
printf("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);
Error(ERROR_GUI_CORRUPTED);
}
for (y=start_y; yformat || gui->format->BitsPerPixel != 8)
{
printf("Not a 8-bit image");
Error(ERROR_GUI_CORRUPTED);
}
SDLPal=gui->format->palette;
if (!SDLPal || SDLPal->ncolors!=256)
{
printf("Not a 256-color palette");
Error(ERROR_GUI_CORRUPTED);
}
// Lecture de la palette par défaut
for (i=0; i<256; i++)
{
Default_palette[i].R=SDLPal->colors[i].r;
Default_palette[i].G=SDLPal->colors[i].g;
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)
{
printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
Error(ERROR_GUI_CORRUPTED);
}
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)
{
printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
Error(ERROR_GUI_CORRUPTED);
}
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)
{
printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
Error(ERROR_GUI_CORRUPTED);
}
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)
{
printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
Error(ERROR_GUI_CORRUPTED);
}
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)
{
printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
Error(ERROR_GUI_CORRUPTED);
}
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)
{
printf("Error in GUI skin file: should start with 5 consecutive squares for black, dark, light, white, transparent, then a neutral color\n");
Error(ERROR_GUI_CORRUPTED);
}
}
// Menu
GUI_seek_down(gui, &cursor_x, &cursor_y, neutral_color, "menu");
Read_GUI_block(gui, cursor_x, cursor_y, GFX_menu_block, MENU_WIDTH, MENU_HEIGHT,"menu",0);
cursor_y+=MENU_HEIGHT;
// Effets
for (i=0; i>1);
Preset_paintbrush_offset_Y[index]=(Preset_paintbrush_height[index]>>1);
}
Cursor_offset_X[CURSOR_SHAPE_ARROW]=0;
Cursor_offset_Y[CURSOR_SHAPE_ARROW]=0;
Cursor_offset_X[CURSOR_SHAPE_TARGET]=7;
Cursor_offset_Y[CURSOR_SHAPE_TARGET]=7;
Cursor_offset_X[CURSOR_SHAPE_COLORPICKER]=7;
Cursor_offset_Y[CURSOR_SHAPE_COLORPICKER]=7;
Cursor_offset_X[CURSOR_SHAPE_HOURGLASS]=7;
Cursor_offset_Y[CURSOR_SHAPE_HOURGLASS]=7;
Cursor_offset_X[CURSOR_SHAPE_MULTIDIRECTIONNAL]=7;
Cursor_offset_Y[CURSOR_SHAPE_MULTIDIRECTIONNAL]=7;
Cursor_offset_X[CURSOR_SHAPE_HORIZONTAL]=7;
Cursor_offset_Y[CURSOR_SHAPE_HORIZONTAL]=3;
Cursor_offset_X[CURSOR_SHAPE_THIN_TARGET]=7;
Cursor_offset_Y[CURSOR_SHAPE_THIN_TARGET]=7;
Cursor_offset_X[CURSOR_SHAPE_THIN_COLORPICKER]=7;
Cursor_offset_Y[CURSOR_SHAPE_THIN_COLORPICKER]=7;
}
// 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 Gauche , Func_action Droite,
Func_action Desenclencher,
byte family)
{
Button[btn_number].X_offset =x_offset;
Button[btn_number].Y_offset =y_offset;
Button[btn_number].Width =width-1;
Button[btn_number].Height =height-1;
Button[btn_number].Pressed =0;
Button[btn_number].Shape =shape;
Button[btn_number].Gauche =Gauche;
Button[btn_number].Droite =Droite;
Button[btn_number].Desenclencher =Desenclencher;
Button[btn_number].Famille =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;
}
// Initiliseur de tous les modes video:
void Set_all_video_modes(void)
{ // Numero LargHaut Mode FXFY Ratio Ref WinOnly Pointeur
SDL_Rect** Modes;
Nb_video_modes=0;
// Doit être en premier pour avoir le numéro 0:
Set_video_mode( 640,480,0, 0);
Set_video_mode( 320,200,0, 1);
Set_video_mode( 320,224,0, 1);
Set_video_mode( 320,240,0, 1);
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;
for (index2=1; index2 < Nb_video_modes; index2++)
if (Modes[index]->w == Video_mode[index2].Width &&
Modes[index]->h == Video_mode[index2].Height)
{
// Mode déja prévu: ok
break;
}
if (index2 >= Nb_video_modes && Modes[index]->w>=320 && Modes[index]->h>=200)
{
// Nouveau mode à ajouter à la liste
Set_video_mode(Modes[index]->w,Modes[index]->h,0, 1);
}
}
// Tri des modes : ceux trouvés par SDL ont été listés à la fin.
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 :
Button[Ordering[index2]&0xFF].Left_shortcut[0] = cfg_shortcut_info.Key;
Button[Ordering[index2]&0xFF].Left_shortcut[1] = cfg_shortcut_info.Key2;
break;
case 2 :
Button[Ordering[index2]&0xFF].Right_shortcut[0] = cfg_shortcut_info.Key;
Button[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;
for (index2=1; 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 =Button[Ordering[index]&0xFF].Left_shortcut[0];
cfg_shortcut_info.Key2=Button[Ordering[index]&0xFF].Left_shortcut[1];
break;
case 2 :
cfg_shortcut_info.Key =Button[Ordering[index]&0xFF].Right_shortcut[0];
cfg_shortcut_info.Key2=Button[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;
for (index=1; 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 :
Button[Ordering[index]&0xFF].Left_shortcut[0] = ConfigKey[index].Key;
Button[Ordering[index]&0xFF].Left_shortcut[1] = ConfigKey[index].Key2;
break;
case 2 :
Button[Ordering[index]&0xFF].Right_shortcut[0] = ConfigKey[index].Key;
Button[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
}