1446 lines
44 KiB
C
1446 lines
44 KiB
C
/* vim:expandtab:ts=2 sw=2:
|
|
*/
|
|
/* Grafx2 - The Ultimate 256-color bitmap paint program
|
|
|
|
Copyright 2018 Thomas Bernard
|
|
Copyright 2011 Pawel Góralski
|
|
Copyright 2009 Pasi Kallinen
|
|
Copyright 2008 Peter Gordon
|
|
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 <http://www.gnu.org/licenses/>
|
|
*/
|
|
/**
|
|
* @file main.c
|
|
* Program entry point, global variables and global functions.
|
|
*
|
|
* @mainpage
|
|
* GrafX2 is a bitmap paint program inspired by the Amiga programs
|
|
* Deluxe Paint and Brilliance. Specialized in 256-color drawing,
|
|
* it includes a very large number of tools and effects that make
|
|
* it particularly suitable for pixel art, game graphics,
|
|
* and generally any detailed graphics painted with a mouse.
|
|
*
|
|
* The program is mostly developed on Haiku, Linux, FreeBSD
|
|
* and Windows, but is also portable on many other platforms :
|
|
* It can be built using SDL 1.x or SDL 2.x libraries (see https://www.libsdl.org/)
|
|
* or Xlib or Win32 API.
|
|
*
|
|
* Web for the project users is http://grafx2.tk/.
|
|
*
|
|
* Developpers are welcome to contribute :
|
|
* the code is hosted on gitlab https://gitlab.com/GrafX2/grafX2
|
|
* and a bug tracker, wiki, etc. is available on
|
|
* https://pulkomandy.tk/projects/GrafX2.
|
|
*
|
|
* This Doxygen documentation is browsable on
|
|
* https://pulkomandy.tk/projects/GrafX2/doxygen/ (updated nightly).
|
|
*
|
|
* The inline help is also available here :
|
|
* http://pulkomandy.tk/GrafX2/
|
|
*/
|
|
/// declare global variables in main.c
|
|
#define GLOBAL_VARIABLES
|
|
|
|
// time.h defines timeval which conflicts with the one in amiga SDK
|
|
#ifdef __amigaos__
|
|
#include <devices/timer.h>
|
|
#else
|
|
#include <time.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#ifndef _MSC_VER
|
|
#include <unistd.h>
|
|
#else
|
|
#if _MSC_VER < 1900
|
|
#define snprintf _snprintf
|
|
#endif
|
|
#endif
|
|
#if defined(USE_SDL) || defined(USE_SDL2)
|
|
#include <SDL.h>
|
|
#include <SDL_image.h>
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
#include <windows.h>
|
|
#include <shlwapi.h>
|
|
#elif defined (__MINT__)
|
|
#include <mint/osbind.h>
|
|
#elif defined(__macosx__)
|
|
#import <CoreFoundation/CoreFoundation.h>
|
|
#import <sys/param.h>
|
|
#elif defined(__FreeBSD__)
|
|
#include <sys/param.h>
|
|
#endif
|
|
|
|
#if defined(__macosx__)
|
|
#include <machine/endian.h>
|
|
#elif defined(__FreeBSD__)
|
|
#include <sys/endian.h>
|
|
#elif !defined(WIN32)
|
|
#include <endian.h>
|
|
#endif
|
|
|
|
#include "gfx2log.h"
|
|
#include "const.h"
|
|
#include "struct.h"
|
|
#include "global.h"
|
|
#include "graph.h"
|
|
#include "misc.h"
|
|
#include "init.h"
|
|
#include "buttons.h"
|
|
#include "engine.h"
|
|
#include "pages.h"
|
|
#include "loadsave.h"
|
|
#include "screen.h"
|
|
#include "errors.h"
|
|
#include "readini.h"
|
|
#include "saveini.h"
|
|
#include "io.h"
|
|
#include "text.h"
|
|
#include "setup.h"
|
|
#include "windows.h"
|
|
#include "brush.h"
|
|
#include "palette.h"
|
|
#include "realpath.h"
|
|
#include "input.h"
|
|
#include "help.h"
|
|
#include "filesel.h"
|
|
#if defined(WIN32) && !(defined(USE_SDL) || defined(USE_SDL2))
|
|
#include "win32screen.h"
|
|
#endif
|
|
|
|
|
|
#if defined (WIN32) && (defined(USE_SDL) || defined(USE_SDL2))
|
|
// On Windows, SDL_putenv is not present in any compilable header.
|
|
// It can be linked anyway, this declaration only avoids
|
|
// a compilation warning.
|
|
extern DECLSPEC int SDLCALL SDL_putenv(const char *variable);
|
|
#endif
|
|
|
|
extern char Program_version[]; // generated in pversion.c
|
|
|
|
static int setsize_width;
|
|
static int setsize_height;
|
|
|
|
#if (defined(USE_SDL) || defined(USE_SDL2)) && defined(USE_JOYSTICK)
|
|
/// Pointer to the current joystick controller.
|
|
static SDL_Joystick* Joystick;
|
|
#endif
|
|
|
|
/**
|
|
* Show the command line syntax and available video modes.
|
|
*
|
|
* Output to standard outout (stdout) and show a message box under MS Windows,
|
|
* where standard output is not available
|
|
*/
|
|
void Display_syntax(void)
|
|
{
|
|
int mode_index, i;
|
|
char modes[1024*2];
|
|
const char * syntax =
|
|
"Syntax: grafx2 [<arguments>] [<picture1>] [<picture2>]\n\n"
|
|
"<arguments> can be:\n"
|
|
"\t-? -h -H -help for this help screen\n"
|
|
"\t-verbose to increase log verbosity\n"
|
|
"\t-wide to emulate a video mode with wide pixels (2x1)\n"
|
|
"\t-tall to emulate a video mode with tall pixels (1x2)\n"
|
|
"\t-double to emulate a video mode with double pixels (2x2)\n"
|
|
"\t-wide2 to emulate a video mode with double wide pixels (4x2)\n"
|
|
"\t-tall2 to emulate a video mode with double tall pixels (2x4)\n"
|
|
"\t-triple to emulate a video mode with triple pixels (3x3)\n"
|
|
"\t-quadruple to emulate a video mode with quadruple pixels (4x4)\n"
|
|
"\t-rgb n to reduce RGB precision (2 to 256, 256=max precision)\n"
|
|
"\t-gamma n to adjust Gamma correction (1 to 30, 10=no correction)\n"
|
|
"\t-skin <filename> to use an alternate file with the menu graphics\n"
|
|
"\t-mode <videomode> to set a video mode\n"
|
|
"\t-size <resolution> to set the image size\n"
|
|
"Arguments can be prefixed either by / - or --\n"
|
|
"They can also be abbreviated.\n\n";
|
|
fputs(syntax, stdout);
|
|
|
|
i = snprintf(modes, sizeof(modes), "Available video modes:\n\n");
|
|
for (mode_index = 0; mode_index < Nb_video_modes; mode_index += 6)
|
|
{
|
|
int k;
|
|
for (k = 0; k < 6; k++)
|
|
{
|
|
if (mode_index + k >= Nb_video_modes) break;
|
|
i += snprintf(modes + i, sizeof(modes) - i, "%12s", Mode_label(mode_index + k));
|
|
}
|
|
i += snprintf(modes + i, sizeof(modes) - i, "\n");
|
|
}
|
|
fputs(modes, stdout);
|
|
|
|
#if defined(WIN32)
|
|
MessageBoxA(GFX2_Get_Window_Handle(), syntax, "GrafX2", MB_OK);
|
|
MessageBoxA(GFX2_Get_Window_Handle(), modes, "GrafX2", MB_OK);
|
|
#endif
|
|
}
|
|
|
|
// ---------------------------- Sortie impromptue ----------------------------
|
|
void Warning_function(const char *message, const char *filename, int line_number, const char *function_name)
|
|
{
|
|
GFX2_Log(GFX2_WARNING, "Warning in file %s, line %d, function %s : %s\n", filename, line_number, function_name, message);
|
|
}
|
|
|
|
|
|
// ---------------------------- Sortie impromptue ----------------------------
|
|
void Error_function(int error_code, const char *filename, int line_number, const char *function_name)
|
|
{
|
|
T_Palette temp_palette;
|
|
T_Palette backup_palette;
|
|
int index;
|
|
char msg_buffer[512];
|
|
|
|
snprintf(msg_buffer, sizeof(msg_buffer), "Error number %d occurred in file %s, line %d, function %s.\n", error_code, filename,line_number,function_name);
|
|
fputs(msg_buffer, stderr);
|
|
#if defined(_MSC_VER) && defined(_DEBUG)
|
|
OutputDebugStringA(msg_buffer);
|
|
#endif
|
|
|
|
if (error_code==0)
|
|
{
|
|
// L'erreur 0 n'est pas une vraie erreur, elle fait seulement un flash rouge de l'écran pour dire qu'il y a un problème.
|
|
// Toutes les autres erreurs déclenchent toujours une sortie en catastrophe du programme !
|
|
memcpy(backup_palette, Get_current_palette(), sizeof(T_Palette));
|
|
memcpy(temp_palette, backup_palette, sizeof(T_Palette));
|
|
for (index=0;index<=255;index++)
|
|
temp_palette[index].R=255;
|
|
Set_palette(temp_palette);
|
|
Delay_with_active_mouse(50); // Half a second of red flash
|
|
Set_palette(backup_palette);
|
|
}
|
|
else
|
|
{
|
|
const char * msg = NULL;
|
|
switch (error_code)
|
|
{
|
|
case ERROR_GUI_MISSING : snprintf(msg_buffer, sizeof(msg_buffer), "Error: File containing the GUI graphics is missing!\n"
|
|
"This program cannot run without this file.\n"
|
|
"\n%s", Gui_loading_error_message);
|
|
msg = msg_buffer;
|
|
break;
|
|
case ERROR_GUI_CORRUPTED : msg = "Error: File containing the GUI graphics couldn't be parsed!\n"
|
|
"This program cannot run without a correct version of this file.\n";
|
|
break;
|
|
case ERROR_INI_MISSING : msg = "Error: File gfx2def.ini is missing!\n"
|
|
"This program cannot run without this file.\n";
|
|
break;
|
|
case ERROR_MEMORY : msg = "Error: Not enough memory!\n\n"
|
|
"You should try exiting other programs to free some bytes for Grafx2.\n\n";
|
|
break;
|
|
case ERROR_FORBIDDEN_MODE : msg = "Error: The requested video mode has been disabled from the resolution menu!\n"
|
|
"If you want to run the program in this mode, you'll have to start it with an\n"
|
|
"enabled mode, then enter the resolution menu and enable the mode you want.\n"
|
|
"Check also if the 'Default_video_mode' parameter in gfx2.ini is correct.\n";
|
|
break;
|
|
case ERROR_FORBIDDEN_SIZE : msg = "Error: The image dimensions all have to be in the range 1-9999!\n";
|
|
break;
|
|
case ERROR_COMMAND_LINE : msg = "Error: Invalid parameter or file not found.\n\n";
|
|
break;
|
|
case ERROR_SAVING_CFG : msg = "Error: Write error while saving settings!\n"
|
|
"Settings have not been saved correctly, and the gfx2.cfg file may have been\n"
|
|
"corrupt. If so, please delete it and Grafx2 will restore default settings.\n";
|
|
break;
|
|
case ERROR_MISSING_DIRECTORY : msg = "Error: Directory you ran the program from not found!\n";
|
|
break;
|
|
case ERROR_INI_CORRUPTED : snprintf(msg_buffer, sizeof(msg_buffer), "Error: File gfx2.ini is corrupt!\n"
|
|
"It contains bad values at line %d.\n"
|
|
"You can re-generate it by deleting the file and running GrafX2 again.\n",
|
|
Line_number_in_INI_file);
|
|
msg = msg_buffer;
|
|
break;
|
|
case ERROR_SAVING_INI : msg = "Error: Cannot rewrite file gfx2.ini!\n";
|
|
break;
|
|
case ERROR_SORRY_SORRY_SORRY : msg = "Error: Sorry! Sorry! Sorry! Please forgive me!\n";
|
|
break;
|
|
}
|
|
|
|
if(msg != NULL)
|
|
{
|
|
fputs(msg, stderr);
|
|
#if defined(WIN32)
|
|
#if defined(_DEBUG)
|
|
OutputDebugStringA(msg);
|
|
#endif
|
|
MessageBoxA(GFX2_Get_Window_Handle(), msg, "GrafX2 error", MB_OK | MB_ICONERROR);
|
|
#endif
|
|
}
|
|
|
|
if (error_code == ERROR_COMMAND_LINE)
|
|
Display_syntax();
|
|
|
|
#if defined(USE_SDL) || defined(USE_SDL2)
|
|
SDL_Quit();
|
|
#endif
|
|
exit(error_code);
|
|
}
|
|
}
|
|
|
|
enum CMD_PARAMS
|
|
{
|
|
CMDPARAM_HELP,
|
|
CMDPARAM_MODE,
|
|
CMDPARAM_PIXELRATIO_TALL,
|
|
CMDPARAM_PIXELRATIO_WIDE,
|
|
CMDPARAM_PIXELRATIO_DOUBLE,
|
|
CMDPARAM_PIXELRATIO_TRIPLE,
|
|
CMDPARAM_PIXELRATIO_QUAD,
|
|
CMDPARAM_PIXELRATIO_TALL2,
|
|
CMDPARAM_PIXELRATIO_TALL3,
|
|
CMDPARAM_PIXELRATIO_WIDE2,
|
|
CMDPARAM_RGB,
|
|
CMDPARAM_GAMMA,
|
|
CMDPARAM_SKIN,
|
|
CMDPARAM_SIZE,
|
|
CMDPARAM_VERBOSE,
|
|
};
|
|
|
|
struct {
|
|
const char *param;
|
|
int id;
|
|
} cmdparams[] = {
|
|
{"?", CMDPARAM_HELP},
|
|
{"h", CMDPARAM_HELP},
|
|
{"H", CMDPARAM_HELP},
|
|
{"help", CMDPARAM_HELP},
|
|
{"mode", CMDPARAM_MODE},
|
|
{"tall", CMDPARAM_PIXELRATIO_TALL},
|
|
{"wide", CMDPARAM_PIXELRATIO_WIDE},
|
|
{"double", CMDPARAM_PIXELRATIO_DOUBLE},
|
|
{"triple", CMDPARAM_PIXELRATIO_TRIPLE},
|
|
{"quadruple", CMDPARAM_PIXELRATIO_QUAD},
|
|
{"tall2", CMDPARAM_PIXELRATIO_TALL2},
|
|
{"tall3", CMDPARAM_PIXELRATIO_TALL3},
|
|
{"wide2", CMDPARAM_PIXELRATIO_WIDE2},
|
|
{"rgb", CMDPARAM_RGB},
|
|
{"gamma", CMDPARAM_GAMMA},
|
|
{"skin", CMDPARAM_SKIN},
|
|
{"size", CMDPARAM_SIZE},
|
|
{"verbose", CMDPARAM_VERBOSE},
|
|
};
|
|
|
|
#define ARRAY_SIZE(x) (int)(sizeof(x) / sizeof(x[0]))
|
|
|
|
/**
|
|
* Parse the command line.
|
|
*
|
|
* @param argc argument count
|
|
* @param argv argument values
|
|
* @param main_filename pointer to receive 1st file name
|
|
* @param main_directory pointer to receive 1st file directory
|
|
* @param spare_filename pointer to receive 2nd file name
|
|
* @param spare_directory pointer to receive 2nd file directory
|
|
* @return the number of file to open (0, 1 or 2)
|
|
*/
|
|
int Analyze_command_line(int argc, char * argv[], char *main_filename, char *main_directory, char *spare_filename, char *spare_directory)
|
|
{
|
|
char *buffer ;
|
|
int index;
|
|
int file_in_command_line;
|
|
|
|
file_in_command_line = 0;
|
|
Resolution_in_command_line = 0;
|
|
|
|
Current_resolution = Config.Default_resolution;
|
|
|
|
for (index = 1; index<argc; index++)
|
|
{
|
|
char *s = argv[index];
|
|
int is_switch = ((strchr(s,'/') == s) || (strchr(s,'-') == s) || (strstr(s,"--") == s));
|
|
int tmpi;
|
|
char *tmpcp;
|
|
int paramtype = -1;
|
|
if (is_switch)
|
|
{
|
|
int param_matches = 0;
|
|
int param_match = -1;
|
|
if (*s == '-')
|
|
{
|
|
s++;
|
|
if (*s == '-')
|
|
s++;
|
|
}
|
|
else
|
|
s++;
|
|
|
|
#ifdef __macosx__
|
|
// ignore -psn argument
|
|
if (memcmp(s, "psn_", 4) ==0)
|
|
continue;
|
|
#endif
|
|
|
|
for (tmpi = 0; tmpi < ARRAY_SIZE(cmdparams); tmpi++)
|
|
{
|
|
if (!strcmp(s, cmdparams[tmpi].param))
|
|
{
|
|
paramtype = cmdparams[tmpi].id;
|
|
break;
|
|
}
|
|
else if (strstr(cmdparams[tmpi].param, s))
|
|
{
|
|
param_matches++;
|
|
param_match = cmdparams[tmpi].id;
|
|
}
|
|
}
|
|
if (paramtype == -1 && param_matches == 1)
|
|
paramtype = param_match;
|
|
|
|
}
|
|
switch (paramtype)
|
|
{
|
|
case CMDPARAM_HELP:
|
|
Display_syntax();
|
|
exit(0);
|
|
case CMDPARAM_MODE:
|
|
index++;
|
|
if (index<argc)
|
|
{
|
|
Resolution_in_command_line = 1;
|
|
Current_resolution = Convert_videomode_arg(argv[index]);
|
|
if (Current_resolution == -1)
|
|
{
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
if ((Video_mode[Current_resolution].State & 0x7F) == 3)
|
|
{
|
|
Error(ERROR_FORBIDDEN_MODE);
|
|
exit(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
break;
|
|
case CMDPARAM_PIXELRATIO_TALL:
|
|
Pixel_ratio = PIXEL_TALL;
|
|
break;
|
|
case CMDPARAM_PIXELRATIO_WIDE:
|
|
Pixel_ratio = PIXEL_WIDE;
|
|
break;
|
|
case CMDPARAM_PIXELRATIO_DOUBLE:
|
|
Pixel_ratio = PIXEL_DOUBLE;
|
|
break;
|
|
case CMDPARAM_PIXELRATIO_TRIPLE:
|
|
Pixel_ratio = PIXEL_TRIPLE;
|
|
break;
|
|
case CMDPARAM_PIXELRATIO_QUAD:
|
|
Pixel_ratio = PIXEL_QUAD;
|
|
break;
|
|
case CMDPARAM_PIXELRATIO_TALL2:
|
|
Pixel_ratio = PIXEL_TALL2;
|
|
break;
|
|
case CMDPARAM_PIXELRATIO_TALL3:
|
|
Pixel_ratio = PIXEL_TALL3;
|
|
break;
|
|
case CMDPARAM_PIXELRATIO_WIDE2:
|
|
Pixel_ratio = PIXEL_WIDE2;
|
|
break;
|
|
case CMDPARAM_RGB:
|
|
/* RGB scale */
|
|
index++;
|
|
if (index<argc)
|
|
{
|
|
int scale;
|
|
scale = atoi(argv[index]);
|
|
if (scale < 2 || scale > 256)
|
|
{
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
Set_palette_RGB_scale(scale);
|
|
}
|
|
else
|
|
{
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
break;
|
|
case CMDPARAM_GAMMA:
|
|
/* Gamma correction */
|
|
index++;
|
|
if (index<argc)
|
|
{
|
|
int scale;
|
|
scale = atoi(argv[index]);
|
|
if (scale < 1 || scale > 30)
|
|
{
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
Set_palette_Gamma(scale);
|
|
}
|
|
else
|
|
{
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
break;
|
|
case CMDPARAM_SKIN:
|
|
// GUI skin file
|
|
index++;
|
|
if (index<argc)
|
|
{
|
|
strcpy(Config.Skin_file,argv[index]);
|
|
}
|
|
else
|
|
{
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
break;
|
|
case CMDPARAM_SIZE:
|
|
index++;
|
|
if (index<argc)
|
|
{
|
|
setsize_width = atoi(argv[index]);
|
|
tmpcp = strchr(argv[index], 'x');
|
|
if (tmpcp == NULL)
|
|
tmpcp = strchr(argv[index], 'X');
|
|
if (tmpcp == NULL)
|
|
{
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
setsize_height = atoi(++tmpcp);
|
|
if (setsize_height < 1 || setsize_height > 9999 ||
|
|
setsize_width < 1 || setsize_width > 9999)
|
|
{
|
|
Error(ERROR_FORBIDDEN_SIZE);
|
|
exit(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
break;
|
|
case CMDPARAM_VERBOSE:
|
|
GFX2_verbosity_level++;
|
|
break;
|
|
default:
|
|
// Si ce n'est pas un paramètre, c'est le nom du fichier à ouvrir
|
|
if (file_in_command_line > 1)
|
|
{
|
|
// Il y a déjà 2 noms de fichiers et on vient d'en trouver un 3ème
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
else if (File_exists(argv[index]))
|
|
{
|
|
file_in_command_line ++;
|
|
buffer = Realpath(argv[index], NULL);
|
|
|
|
if (file_in_command_line == 1)
|
|
{
|
|
// Separate path from filename
|
|
Extract_path(main_directory, buffer);
|
|
Extract_filename(main_filename, buffer);
|
|
}
|
|
else
|
|
{
|
|
// Separate path from filename
|
|
Extract_path(spare_directory, buffer);
|
|
Extract_filename(spare_filename, buffer);
|
|
}
|
|
free(buffer);
|
|
buffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
Error(ERROR_COMMAND_LINE);
|
|
exit(0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return file_in_command_line;
|
|
}
|
|
|
|
// Compile-time assertions:
|
|
#define CT_ASSERT(e) extern char (*ct_assert(void)) [sizeof(char[1 - 2*!(e)])]
|
|
|
|
// This line will raise an error at compile time
|
|
// when sizeof(T_Components) is not 3.
|
|
CT_ASSERT(sizeof(T_Components)==3);
|
|
|
|
// This line will raise an error at compile time
|
|
// when sizeof(T_Palette) is not 768.
|
|
CT_ASSERT(sizeof(T_Palette)==768);
|
|
|
|
#if defined(__MINT__)
|
|
static void Exit_handler(void)
|
|
{
|
|
printf("Press any key to quit.\n");
|
|
(void)Cnecin();
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Initialize the program.
|
|
*
|
|
* @param argc command line argument count
|
|
* @param argv command line argument values
|
|
* @return 0 on fail
|
|
* @return 1 on success
|
|
*/
|
|
int Init_program(int argc,char * argv[])
|
|
{
|
|
int temp;
|
|
int starting_videomode;
|
|
enum IMAGE_MODES starting_image_mode;
|
|
static char program_directory[MAX_PATH_CHARACTERS];
|
|
T_Gui_skin *gfx;
|
|
int file_in_command_line;
|
|
T_Gradient_array initial_gradients;
|
|
static char main_filename [MAX_PATH_CHARACTERS];
|
|
static char main_directory[MAX_PATH_CHARACTERS];
|
|
static char spare_filename [MAX_PATH_CHARACTERS];
|
|
static char spare_directory[MAX_PATH_CHARACTERS];
|
|
static word filename_unicode[MAX_PATH_CHARACTERS];
|
|
|
|
#if defined(__MINT__)
|
|
printf("===============================\n");
|
|
printf(" /|\\ GrafX2 %.19s\n", Program_version);
|
|
printf(" compilation date: %.16s\n", __DATE__);
|
|
printf("===============================\n");
|
|
atexit(Exit_handler);
|
|
#endif
|
|
|
|
#ifdef ENABLE_FILENAMES_ICONV
|
|
// iconv is used to convert filenames
|
|
cd = iconv_open(TOCODE, FROMCODE); // From UTF8 to ANSI
|
|
cd_inv = iconv_open(FROMCODE, TOCODE); // From ANSI to UTF8
|
|
#if (defined(SDL_BYTEORDER) && (SDL_BYTEORDER == SDL_BIG_ENDIAN)) || (defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN))
|
|
cd_utf16 = iconv_open("UTF-16BE", FROMCODE); // From UTF8 to UTF16
|
|
cd_utf16_inv = iconv_open(FROMCODE, "UTF-16BE"); // From UTF16 to UTF8
|
|
#else
|
|
cd_utf16 = iconv_open("UTF-16LE", FROMCODE); // From UTF8 to UTF16
|
|
cd_utf16_inv = iconv_open(FROMCODE, "UTF-16LE"); // From UTF16 to UTF8
|
|
#endif
|
|
#endif /* ENABLE_FILENAMES_ICONV */
|
|
|
|
// On crée dès maintenant les descripteurs des listes de pages pour la page
|
|
// principale et la page de brouillon afin que leurs champs ne soient pas
|
|
// invalide lors des appels aux multiples fonctions manipulées à
|
|
// l'initialisation du programme.
|
|
Main.backups=(T_List_of_pages *)malloc(sizeof(T_List_of_pages));
|
|
Spare.backups=(T_List_of_pages *)malloc(sizeof(T_List_of_pages));
|
|
Init_list_of_pages(Main.backups);
|
|
Init_list_of_pages(Spare.backups);
|
|
|
|
// Determine the executable directory
|
|
Set_program_directory(argv[0],program_directory);
|
|
// Choose directory for data (read only)
|
|
Set_data_directory(program_directory,Data_directory);
|
|
// Choose directory for settings (read/write)
|
|
Set_config_directory(program_directory,Config_directory);
|
|
// On détermine le répertoire courant:
|
|
Get_current_directory(Main.selector.Directory,Main.selector.Directory_unicode,MAX_PATH_CHARACTERS);
|
|
|
|
// On en profite pour le mémoriser dans le répertoire principal:
|
|
strcpy(Initial_directory,Main.selector.Directory);
|
|
|
|
// On initialise les données sur le nom de fichier de l'image de brouillon:
|
|
strcpy(Spare.selector.Directory,Main.selector.Directory);
|
|
|
|
Main.fileformat=DEFAULT_FILEFORMAT;
|
|
Spare.fileformat =DEFAULT_FILEFORMAT;
|
|
|
|
strcpy(Brush_selector.Directory,Main.selector.Directory);
|
|
strcpy(Brush_file_directory,Main.selector.Directory);
|
|
strcpy(Brush_filename ,"NO_NAME.GIF");
|
|
Brush_filename_unicode[0] = 0;
|
|
Brush_fileformat =DEFAULT_FILEFORMAT;
|
|
|
|
strcpy(Palette_selector.Directory,Main.selector.Directory);
|
|
|
|
// On initialise ce qu'il faut pour que les fileselects ne plantent pas:
|
|
|
|
Main.selector.Position=0; // Au début, le fileselect est en haut de la liste des fichiers
|
|
Main.selector.Offset=0; // Au début, le fileselect est en haut de la liste des fichiers
|
|
Main.selector.Format_filter=FORMAT_ALL_IMAGES;
|
|
|
|
Main.current_layer=0;
|
|
Main.layers_visible=0xFFFFFFFF;
|
|
Main.layers_visible_backup=0xFFFFFFFF;
|
|
Spare.current_layer=0;
|
|
Spare.layers_visible=0xFFFFFFFF;
|
|
Spare.layers_visible_backup=0xFFFFFFFF;
|
|
|
|
Spare.selector.Position=0;
|
|
Spare.selector.Offset=0;
|
|
Spare.selector.Format_filter=FORMAT_ALL_IMAGES;
|
|
Brush_selector.Position=0;
|
|
Brush_selector.Offset=0;
|
|
Brush_selector.Format_filter=FORMAT_ALL_IMAGES;
|
|
|
|
Palette_selector.Position=0;
|
|
Palette_selector.Offset=0;
|
|
Palette_selector.Format_filter=FORMAT_ALL_PALETTES;
|
|
|
|
// On initialise d'ot' trucs
|
|
Main.offset_X=0;
|
|
Main.offset_Y=0;
|
|
Main.separator_position=0;
|
|
Main.X_zoom=0;
|
|
Main.separator_proportion=INITIAL_SEPARATOR_PROPORTION;
|
|
Main.magnifier_mode=0;
|
|
Main.magnifier_factor=DEFAULT_ZOOM_FACTOR;
|
|
Main.magnifier_height=0;
|
|
Main.magnifier_width=0;
|
|
Main.magnifier_offset_X=0;
|
|
Main.magnifier_offset_Y=0;
|
|
Spare.offset_X=0;
|
|
Spare.offset_Y=0;
|
|
Spare.separator_position=0;
|
|
Spare.X_zoom=0;
|
|
Spare.separator_proportion=INITIAL_SEPARATOR_PROPORTION;
|
|
Spare.magnifier_mode=0;
|
|
Spare.magnifier_factor=DEFAULT_ZOOM_FACTOR;
|
|
Spare.magnifier_height=0;
|
|
Spare.magnifier_width=0;
|
|
Spare.magnifier_offset_X=0;
|
|
Spare.magnifier_offset_Y=0;
|
|
Keyboard_click_allowed = 1;
|
|
|
|
Main.safety_backup_prefix = SAFETYBACKUP_PREFIX_A[0];
|
|
Spare.safety_backup_prefix = SAFETYBACKUP_PREFIX_B[0];
|
|
Main.time_of_safety_backup = 0;
|
|
Spare.time_of_safety_backup = 0;
|
|
|
|
|
|
#if defined(USE_SDL) || defined(USE_SDL2)
|
|
// SDL
|
|
if (SDL_Init(SDL_INIT_VIDEO
|
|
#if defined(USE_JOYSTICK)
|
|
| SDL_INIT_JOYSTICK
|
|
#endif
|
|
) < 0)
|
|
{
|
|
// The program can't continue without that anyway
|
|
printf("Couldn't initialize SDL.\n");
|
|
return(0);
|
|
}
|
|
|
|
#if defined(USE_SDL2)
|
|
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE);
|
|
#endif
|
|
#endif
|
|
#if defined(USE_SDL)
|
|
SDL_EnableKeyRepeat(250, 32);
|
|
SDL_EnableUNICODE(SDL_ENABLE);
|
|
SDL_WM_SetCaption("GrafX2","GrafX2");
|
|
#endif
|
|
Define_icon();
|
|
|
|
// Texte
|
|
Init_text();
|
|
|
|
// Initialize all video modes
|
|
Set_all_video_modes();
|
|
|
|
// Analyse command-line as soon as possible.
|
|
// This must come after video mode initialization because
|
|
// a video mode may be requested as a command-line parameter
|
|
file_in_command_line=Analyze_command_line(argc, argv, main_filename, main_directory, spare_filename, spare_directory);
|
|
|
|
#if defined(USE_JOYSTICK) && (defined(USE_SDL) || defined(USE_SDL2))
|
|
GFX2_Log(GFX2_DEBUG, "%d joystick(s) attached\n", SDL_NumJoysticks());
|
|
if (SDL_NumJoysticks() > 0)
|
|
{
|
|
Joystick = SDL_JoystickOpen(0);
|
|
if (Joystick == NULL)
|
|
{
|
|
GFX2_Log(GFX2_ERROR, "Failed to open joystick #0 : %s\n", SDL_GetError());
|
|
}
|
|
else
|
|
{
|
|
GFX2_Log(GFX2_DEBUG, "Joystick #0 open : \"%s\" %d axes, %d buttons, %d balls, %d hats\n",
|
|
SDL_JoystickName(Joystick), SDL_JoystickNumAxes(Joystick),
|
|
SDL_JoystickNumButtons(Joystick), SDL_JoystickNumBalls(Joystick),
|
|
SDL_JoystickNumHats(Joystick));
|
|
SDL_JoystickEventState(SDL_ENABLE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Pixel_ratio=PIXEL_SIMPLE;
|
|
// On initialise les données sur l'état du programme:
|
|
// Donnée sur la sortie du programme:
|
|
Quit_is_required=0;
|
|
Quitting=0;
|
|
// Données sur l'état du menu:
|
|
Menu_is_visible=1;
|
|
// Données sur les couleurs et la palette:
|
|
First_color_in_palette=0;
|
|
// Données sur le curseur:
|
|
Cursor_shape=CURSOR_SHAPE_TARGET;
|
|
Cursor_hidden=0;
|
|
// Données sur le pinceau:
|
|
Paintbrush_X=0;
|
|
Paintbrush_Y=0;
|
|
Paintbrush_hidden=0;
|
|
|
|
// On initialise tout ce qui concerne les opérations et les effets
|
|
Operation_stack_size=0;
|
|
Selected_freehand_mode=OPERATION_CONTINUOUS_DRAW;
|
|
Selected_line_mode =OPERATION_LINE;
|
|
Selected_curve_mode =OPERATION_3_POINTS_CURVE;
|
|
Effect_function=No_effect;
|
|
// On initialise les infos de la loupe:
|
|
Main.magnifier_mode=0;
|
|
Main.magnifier_factor=DEFAULT_ZOOM_FACTOR;
|
|
Main.separator_proportion=INITIAL_SEPARATOR_PROPORTION;
|
|
Spare.separator_proportion=INITIAL_SEPARATOR_PROPORTION;
|
|
// On initialise les infos du mode smear:
|
|
Smear_mode=0;
|
|
Smear_brush_width=PAINTBRUSH_WIDTH;
|
|
Smear_brush_height=PAINTBRUSH_HEIGHT;
|
|
// On initialise les infos du mode smooth:
|
|
Smooth_mode=0;
|
|
// On initialise les infos du mode shade:
|
|
Shade_mode=0; // Les autres infos du Shade sont chargées avec la config
|
|
Quick_shade_mode=0; // idem
|
|
// On initialise les infos sur les dégradés:
|
|
Gradient_pixel =Display_pixel; // Les autres infos sont chargées avec la config
|
|
// On initialise les infos de la grille:
|
|
Snap_mode=0;
|
|
Snap_width=8;
|
|
Snap_height=8;
|
|
Snap_offset_X=0;
|
|
Snap_offset_Y=0;
|
|
// On initialise les infos du mode Colorize:
|
|
Colorize_mode=0; // Mode colorize inactif par défaut
|
|
Colorize_opacity=50; // Une interpolation de 50% par défaut
|
|
Colorize_current_mode=0; // Par défaut, la méthode par interpolation
|
|
Compute_colorize_table();
|
|
// On initialise les infos du mode Tiling:
|
|
Tiling_mode=0; // Pas besoin d'initialiser les décalages car ça se fait
|
|
// en prenant une brosse (toujours mis à 0).
|
|
// On initialise les infos du mode Mask:
|
|
Mask_mode=0;
|
|
|
|
// Infos du Spray
|
|
Airbrush_mode=1; // Mode Mono
|
|
Airbrush_size=31;
|
|
Airbrush_delay=1;
|
|
Airbrush_mono_flow=10;
|
|
memset(Airbrush_multi_flow,0,256);
|
|
srand(time(NULL)); // On randomize un peu tout ça...
|
|
|
|
// Initialisation des boutons
|
|
Init_buttons();
|
|
// Initialisation des opérations
|
|
Init_operations();
|
|
|
|
// Initialize the brush container
|
|
Init_brush_container();
|
|
|
|
Windows_open=0;
|
|
|
|
// Paintbrush
|
|
if (!(Paintbrush_sprite=(byte *)malloc(MAX_PAINTBRUSH_SIZE*MAX_PAINTBRUSH_SIZE))) Error(ERROR_MEMORY);
|
|
|
|
// Load preset paintbrushes (uses Paintbrush_ variables)
|
|
Init_paintbrushes();
|
|
|
|
// Set a valid paintbrush afterwards
|
|
*Paintbrush_sprite=1;
|
|
Paintbrush_width=1;
|
|
Paintbrush_height=1;
|
|
Paintbrush_offset_X=0;
|
|
Paintbrush_offset_Y=0;
|
|
Paintbrush_shape=PAINTBRUSH_SHAPE_ROUND;
|
|
|
|
#if defined(__GP2X__) || defined(__WIZ__) || defined(__CAANOO__)
|
|
// Prefer cycling active by default
|
|
Cycling_mode=1;
|
|
#endif
|
|
|
|
// Charger la configuration des touches
|
|
Set_config_defaults();
|
|
|
|
switch(Load_CFG(1))
|
|
{
|
|
case ERROR_CFG_MISSING:
|
|
// Pas un problème, on a les valeurs par défaut.
|
|
break;
|
|
case ERROR_CFG_CORRUPTED:
|
|
GFX2_Log(GFX2_ERROR, "Corrupted CFG file.\n");
|
|
break;
|
|
case ERROR_CFG_OLD:
|
|
GFX2_Log(GFX2_WARNING, "Unknown CFG file version, not loaded.\n");
|
|
break;
|
|
}
|
|
// Charger la configuration du .INI
|
|
temp=Load_INI(&Config);
|
|
if (temp)
|
|
Error(temp);
|
|
|
|
if(!Config.Allow_multi_shortcuts)
|
|
{
|
|
Remove_duplicate_shortcuts();
|
|
}
|
|
|
|
Compute_menu_offsets();
|
|
|
|
Current_help_section=0;
|
|
Help_position=0;
|
|
|
|
// Load sprites, palette etc.
|
|
gfx = Load_graphics(Config.Skin_file, &initial_gradients);
|
|
if (gfx == NULL)
|
|
{
|
|
gfx = Load_graphics(DEFAULT_SKIN_FILENAME, &initial_gradients);
|
|
if (gfx == NULL)
|
|
{
|
|
Error(ERROR_GUI_MISSING);
|
|
}
|
|
}
|
|
Set_current_skin(Config.Skin_file, gfx);
|
|
// Override colors
|
|
Gfx->Default_palette[MC_Black]=Config.Fav_menu_colors[0];
|
|
Gfx->Default_palette[MC_Dark] =Config.Fav_menu_colors[1];
|
|
Gfx->Default_palette[MC_Light]=Config.Fav_menu_colors[2];
|
|
Gfx->Default_palette[MC_White]=Config.Fav_menu_colors[3];
|
|
|
|
// Even when using the skin's palette, if RGB range is small
|
|
// the colors will be unusable.
|
|
Compute_optimal_menu_colors(Gfx->Default_palette);
|
|
|
|
// Infos sur les trames (Sieve)
|
|
Sieve_mode=0;
|
|
Copy_preset_sieve(0);
|
|
|
|
// Font
|
|
if (!(Menu_font=Load_font(Config.Font_file, 1)))
|
|
if (!(Menu_font=Load_font(DEFAULT_FONT_FILENAME, 1)))
|
|
{
|
|
snprintf(Gui_loading_error_message, sizeof(Gui_loading_error_message),
|
|
"Unable to open the default font file: %s\n", DEFAULT_FONT_FILENAME);
|
|
Error(ERROR_GUI_MISSING);
|
|
}
|
|
Load_Unicode_fonts();
|
|
|
|
memcpy(Main.palette, Gfx->Default_palette, sizeof(T_Palette));
|
|
|
|
Fore_color=Best_color_range(255,255,255,Config.Palette_cells_X*Config.Palette_cells_Y);
|
|
Back_color=Best_color_range(0,0,0,Config.Palette_cells_X*Config.Palette_cells_Y);
|
|
|
|
// Allocation de mémoire pour la brosse
|
|
if (!(Brush =(byte *)malloc( 1* 1))) Error(ERROR_MEMORY);
|
|
if (!(Smear_brush =(byte *)malloc(MAX_PAINTBRUSH_SIZE*MAX_PAINTBRUSH_SIZE))) Error(ERROR_MEMORY);
|
|
|
|
|
|
starting_videomode=Current_resolution;
|
|
Horizontal_line_buffer=NULL;
|
|
Screen_width=Screen_height=Current_resolution=0;
|
|
|
|
Init_mode_video(
|
|
Video_mode[starting_videomode].Width,
|
|
Video_mode[starting_videomode].Height,
|
|
Video_mode[starting_videomode].Fullscreen,
|
|
Pixel_ratio);
|
|
|
|
// Windows only: move back the window to its original position.
|
|
#if defined(WIN32)
|
|
if (!Video_mode[starting_videomode].Fullscreen)
|
|
{
|
|
if (Config.Window_pos_x != 9999 && Config.Window_pos_y != 9999)
|
|
{
|
|
//RECT r;
|
|
#if defined(USE_SDL) || defined(USE_SDL2)
|
|
//GetWindowRect(window, &r);
|
|
SetWindowPos(GFX2_Get_Window_Handle(), 0, Config.Window_pos_x, Config.Window_pos_y, 0, 0, SWP_NOSIZE);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Open a console for debugging...
|
|
//ActivateConsole();
|
|
#endif
|
|
|
|
Main.image_width=Screen_width/Pixel_width;
|
|
Main.image_height=Screen_height/Pixel_height;
|
|
Spare.image_width=Screen_width/Pixel_width;
|
|
Spare.image_height=Screen_height/Pixel_height;
|
|
|
|
// set the Mouse cursor at the center of the screen
|
|
Mouse_X = Screen_width / 2;
|
|
Mouse_Y = Screen_height / 2;
|
|
|
|
starting_image_mode = Config.Default_mode_layers ?
|
|
IMAGE_MODE_LAYERED : IMAGE_MODE_ANIMATION;
|
|
// Allocation de mémoire pour les différents écrans virtuels (et brosse)
|
|
if (Init_all_backup_lists(starting_image_mode , Screen_width, Screen_height)==0)
|
|
Error(ERROR_MEMORY);
|
|
// Update toolbars' visibility, now that the current image has a mode
|
|
Check_menu_mode();
|
|
|
|
// Nettoyage de l'écran virtuel (les autres recevront celui-ci par copie)
|
|
memset(Main_screen,0,Main.image_width*Main.image_height);
|
|
|
|
// If image size was specified on command line, set that now
|
|
if (setsize_width != 0 && setsize_height != 0)
|
|
{
|
|
Main.image_width=setsize_width;
|
|
Main.image_height=setsize_height;
|
|
Spare.image_width=setsize_width;
|
|
Spare.image_height=setsize_height;
|
|
}
|
|
|
|
// Now that the backup system is there, we can store the gradients.
|
|
memcpy(Main.backups->Pages->Gradients->Range, initial_gradients.Range, sizeof(initial_gradients.Range));
|
|
memcpy(Spare.backups->Pages->Gradients->Range, initial_gradients.Range, sizeof(initial_gradients.Range));
|
|
|
|
Gradient_function=Gradient_basic;
|
|
Gradient_lower_bound=0;
|
|
Gradient_upper_bound=0;
|
|
Gradient_random_factor=1;
|
|
Gradient_bounds_range=1;
|
|
|
|
Current_gradient=0;
|
|
|
|
// Initialisation de diverses variables par calcul:
|
|
Compute_magnifier_data();
|
|
Compute_limits();
|
|
Compute_paintbrush_coordinates();
|
|
|
|
// On affiche le menu:
|
|
Display_paintbrush_in_menu();
|
|
Display_sprite_in_menu(BUTTON_PAL_LEFT,Config.Palette_vertical?MENU_SPRITE_VERTICAL_PALETTE_SCROLL:-1);
|
|
Display_menu();
|
|
Draw_menu_button(BUTTON_PAL_LEFT,BUTTON_RELEASED);
|
|
Draw_menu_button(BUTTON_PAL_RIGHT,BUTTON_RELEASED);
|
|
|
|
// On affiche le curseur pour débuter correctement l'état du programme:
|
|
Display_cursor();
|
|
|
|
Spare.image_is_modified=0;
|
|
Main.image_is_modified=0;
|
|
|
|
// Gestionnaire de signaux, quand il ne reste plus aucun espoir
|
|
Init_sighandler();
|
|
|
|
// Le programme débute en mode de dessin à la main
|
|
Select_button(BUTTON_DRAW,LEFT_SIDE);
|
|
|
|
// On initialise la brosse initiale à 1 pixel blanc:
|
|
Brush_width=1;
|
|
Brush_height=1;
|
|
for (temp=0;temp<256;temp++)
|
|
Brush_colormap[temp]=temp;
|
|
Capture_brush(0,0,0,0,0);
|
|
*Brush=MC_White;
|
|
*Brush_original_pixels=MC_White;
|
|
|
|
// Make sure the load dialog points to the right place when first shown.
|
|
// Done after loading everything else, but before checking for emergency
|
|
// backups
|
|
if (file_in_command_line > 0)
|
|
{
|
|
strcpy(Main.selector.Directory, main_directory);
|
|
}
|
|
|
|
// Test de recuperation de fichiers sauvés
|
|
switch (Check_recovery())
|
|
{
|
|
T_IO_Context context;
|
|
|
|
default:
|
|
// Some files were loaded from last crash-exit.
|
|
// Do not load files from command-line, nor show splash screen.
|
|
Compute_optimal_menu_colors(Main.palette);
|
|
Check_menu_mode();
|
|
Display_all_screen();
|
|
Display_menu();
|
|
Display_cursor();
|
|
Verbose_message("Images recovered",
|
|
"Grafx2 has recovered images from\n"
|
|
"last session, before a crash or\n"
|
|
"shutdown. Browse the history using\n"
|
|
"the Undo/Redo button, and when\n"
|
|
"you find a state that you want to\n"
|
|
"save, use the 'Save as' button to\n"
|
|
"save the image.\n"
|
|
"Some backups can be present in\n"
|
|
"the spare page too.\n");
|
|
break;
|
|
|
|
case -1: // Unable to write lock file
|
|
Verbose_message("Warning",
|
|
"Safety backups (every minute) are\n"
|
|
"disabled because Grafx2 is running\n"
|
|
"from a read-only device, or other\n"
|
|
"instances are running.");
|
|
break;
|
|
|
|
case 0:
|
|
|
|
switch (file_in_command_line)
|
|
{
|
|
case 0:
|
|
if (Config.Opening_message)
|
|
Button_Message_initial();
|
|
// Load default palette
|
|
{
|
|
FILE * f;
|
|
Init_context_layered_image(&context, DEFAULTPAL_FILENAME, Config_directory);
|
|
context.Type = CONTEXT_PALETTE;
|
|
context.Format = FORMAT_PAL;
|
|
f = Open_file_read(&context);
|
|
if (f != NULL) // silently fail if the file cannot be open
|
|
{
|
|
fclose(f);
|
|
Load_image(&context);
|
|
if (File_error == 0)
|
|
{
|
|
Hide_cursor();
|
|
Compute_optimal_menu_colors(Main.palette);
|
|
Display_menu();
|
|
Display_cursor();
|
|
memcpy(Spare.palette, Main.palette, sizeof(T_Palette));
|
|
}
|
|
}
|
|
Destroy_context(&context);
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
// Load this file
|
|
Init_context_layered_image(&context, spare_filename, spare_directory);
|
|
if (Get_Unicode_Filename(filename_unicode, spare_filename, spare_directory))
|
|
context.File_name_unicode = filename_unicode;
|
|
Load_image(&context);
|
|
Destroy_context(&context);
|
|
Redraw_layered_image();
|
|
End_of_modification();
|
|
|
|
Button_Page(BUTTON_PAGE);
|
|
// no break ! proceed with the other file now
|
|
#if defined(__GNUC__) && (__GNUC__ >= 7)
|
|
__attribute__ ((fallthrough));
|
|
#endif
|
|
case 1:
|
|
Init_context_layered_image(&context, main_filename, main_directory);
|
|
if (Get_Unicode_Filename(filename_unicode, main_filename, main_directory))
|
|
context.File_name_unicode = filename_unicode;
|
|
Load_image(&context);
|
|
Destroy_context(&context);
|
|
Redraw_layered_image();
|
|
End_of_modification();
|
|
|
|
// If only one image was loaded, assume the spare has same image type
|
|
if (file_in_command_line==1)
|
|
{
|
|
if (Main.backups->Pages->Image_mode <= IMAGE_MODE_ANIMATION)
|
|
Spare.backups->Pages->Image_mode = Main.backups->Pages->Image_mode;
|
|
}
|
|
|
|
Hide_cursor();
|
|
Compute_optimal_menu_colors(Main.palette);
|
|
Back_color=Main.backups->Pages->Background_transparent ?
|
|
Main.backups->Pages->Transparent_color :
|
|
Best_color_range(0,0,0,Config.Palette_cells_X*Config.Palette_cells_Y);
|
|
Fore_color=Main.palette[Back_color].R+Main.palette[Back_color].G+Main.palette[Back_color].B < 3*127 ?
|
|
Best_color_range(255,255,255,Config.Palette_cells_X*Config.Palette_cells_Y) :
|
|
Best_color_range(0,0,0,Config.Palette_cells_X*Config.Palette_cells_Y);
|
|
Check_menu_mode();
|
|
Display_all_screen();
|
|
Display_menu();
|
|
Display_cursor();
|
|
Resolution_in_command_line = 0;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Allow_drag_and_drop(1);
|
|
|
|
return(1);
|
|
}
|
|
|
|
#define FREE_POINTER(p) free(p); p = NULL //!< Make free the memory and make sure the pointer is set to NULL
|
|
|
|
/**
|
|
* Program Shutdown.
|
|
*
|
|
* Free all allocated resources.
|
|
*/
|
|
void Program_shutdown(void)
|
|
{
|
|
int i;
|
|
int return_code;
|
|
|
|
// Windows only: Recover the window position.
|
|
#if defined(WIN32)
|
|
#if defined(USE_SDL) || defined(USE_SDL2)
|
|
{
|
|
RECT r;
|
|
GetWindowRect(GFX2_Get_Window_Handle(), &r);
|
|
|
|
Config.Window_pos_x = r.left;
|
|
Config.Window_pos_y = r.top;
|
|
}
|
|
#endif
|
|
// Config.Window_pos_x / Config.Window_pos_y are set in win32screen.c
|
|
#elif !defined(USE_X11)
|
|
// All other targets: irrelevant dimensions.
|
|
// Do not attempt to force them back on next program run.
|
|
Config.Window_pos_x = 9999;
|
|
Config.Window_pos_y = 9999;
|
|
#endif
|
|
|
|
// Remove the safety backups, this is normal exit
|
|
Delete_safety_backups();
|
|
|
|
// On libère le buffer de gestion de lignes
|
|
free(Horizontal_line_buffer);
|
|
Horizontal_line_buffer = NULL;
|
|
|
|
// On libère le pinceau spécial
|
|
free(Paintbrush_sprite);
|
|
Paintbrush_sprite = NULL;
|
|
|
|
// Free Brushes
|
|
FREE_POINTER(Brush);
|
|
FREE_POINTER(Smear_brush);
|
|
FREE_POINTER(Brush_original_pixels);
|
|
|
|
// Free all images
|
|
Set_number_of_backups(-1); // even delete the main page
|
|
|
|
FREE_POINTER(Main.visible_image.Image);
|
|
FREE_POINTER(Spare.visible_image.Image);
|
|
FREE_POINTER(Main_visible_image_backup.Image);
|
|
FREE_POINTER(Main_visible_image_depth_buffer.Image);
|
|
|
|
FREE_POINTER(Main.backups);
|
|
FREE_POINTER(Spare.backups);
|
|
|
|
// Free the skin (Gui graphics) data
|
|
free(Gfx);
|
|
Gfx=NULL;
|
|
|
|
free(Menu_font);
|
|
Menu_font = NULL;
|
|
while (Unicode_fonts != NULL)
|
|
{
|
|
T_Unicode_Font * ufont = Unicode_fonts->Next;
|
|
free(Unicode_fonts->FontData);
|
|
free(Unicode_fonts);
|
|
Unicode_fonts = ufont;
|
|
}
|
|
|
|
// On prend bien soin de passer dans le répertoire initial:
|
|
if (Change_directory(Initial_directory)==0)
|
|
{
|
|
// On sauvegarde les données dans le .CFG et dans le .INI
|
|
if (Config.Auto_save)
|
|
{
|
|
return_code=Save_CFG();
|
|
if (return_code)
|
|
Error(return_code);
|
|
return_code=Save_INI(&Config);
|
|
if (return_code)
|
|
Error(return_code);
|
|
}
|
|
}
|
|
else
|
|
Error(ERROR_MISSING_DIRECTORY);
|
|
|
|
// Free Config
|
|
FREE_POINTER(Config.Skin_file);
|
|
FREE_POINTER(Config.Font_file);
|
|
for (i=0;i<NB_BOOKMARKS;i++)
|
|
{
|
|
FREE_POINTER(Config.Bookmark_directory[i]);
|
|
}
|
|
|
|
Uninit_text();
|
|
|
|
#ifdef ENABLE_FILENAMES_ICONV
|
|
if (cd != (iconv_t)-1)
|
|
iconv_close(cd);
|
|
if (cd_inv != (iconv_t)-1)
|
|
iconv_close(cd_inv);
|
|
if (cd_utf16 != (iconv_t)-1)
|
|
iconv_close(cd_utf16);
|
|
if (cd_utf16_inv != (iconv_t)-1)
|
|
iconv_close(cd_utf16_inv);
|
|
#endif
|
|
|
|
#if defined(USE_SDL) || defined(USE_SDL2)
|
|
SDL_Quit();
|
|
#endif
|
|
|
|
#if defined(__GP2X__) || defined(__WIZ__) || defined(__CAANOO__)
|
|
chdir("/usr/gp2x");
|
|
execl("/usr/gp2x/gp2xmenu", "/usr/gp2x/gp2xmenu", NULL);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Program entry point
|
|
*/
|
|
#if defined(WIN32) && !defined(USE_SDL) && !defined(USE_SDL2)
|
|
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
|
#else
|
|
int main(int argc,char * argv[])
|
|
#endif
|
|
{
|
|
#if defined(WIN32) && !defined(USE_SDL) && !defined(USE_SDL2)
|
|
TCHAR ModuleFileName[MAX_PATH];
|
|
TCHAR TmpArg[MAX_PATH];
|
|
TCHAR ShortFileName[MAX_PATH];
|
|
int i, j, k;
|
|
int inquote = 0;
|
|
int argc = 0;
|
|
char arg_buffer[4096];
|
|
char * argv[64] = {NULL};
|
|
|
|
Init_Win32(hInstance, hPrevInstance);
|
|
if (GetModuleFileName(NULL, ModuleFileName, MAX_PATH) == 0)
|
|
{
|
|
MessageBoxA(NULL, "Error initializing program", NULL, MB_OK | MB_ICONERROR);
|
|
return 1;
|
|
}
|
|
GetShortPathName(ModuleFileName, ShortFileName, MAX_PATH);
|
|
argv[argc++] = arg_buffer;
|
|
for (i = 0; i < (int)sizeof(arg_buffer); )
|
|
{
|
|
arg_buffer[i] = (char)ShortFileName[i];
|
|
if (arg_buffer[i++] == 0) break;
|
|
}
|
|
k = 0;
|
|
for (j = 0; pCmdLine[j] != 0 && k < MAX_PATH - 1; j++)
|
|
{
|
|
if (inquote)
|
|
{
|
|
if (pCmdLine[j] == '"')
|
|
{
|
|
inquote = 0;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pCmdLine[j] == '"')
|
|
{
|
|
inquote = 1;
|
|
continue;
|
|
}
|
|
if (pCmdLine[j] == ' ' || pCmdLine[j] == '\t')
|
|
{ // next argument
|
|
TmpArg[k++] = '\0';
|
|
argv[argc++] = arg_buffer + i;
|
|
if (GetShortPathName(TmpArg, ShortFileName, MAX_PATH) > 0)
|
|
{
|
|
for (k = 0; ShortFileName[k] != 0; k++)
|
|
arg_buffer[i++] = ShortFileName[k];
|
|
}
|
|
else
|
|
{
|
|
for (k = 0; TmpArg[k] != 0; k++)
|
|
arg_buffer[i++] = TmpArg[k];
|
|
}
|
|
arg_buffer[i++] = 0;
|
|
k = 0;
|
|
continue;
|
|
}
|
|
}
|
|
TmpArg[k++] = pCmdLine[j];
|
|
}
|
|
TmpArg[k] = '\0';
|
|
if (k > 0)
|
|
{
|
|
argv[argc++] = arg_buffer + i;
|
|
if (GetShortPathName(TmpArg, ShortFileName, MAX_PATH) > 0)
|
|
{
|
|
for (k = 0; ShortFileName[k] != 0; k++)
|
|
arg_buffer[i++] = ShortFileName[k];
|
|
}
|
|
else
|
|
{
|
|
for (k = 0; TmpArg[k] != 0; k++)
|
|
arg_buffer[i++] = TmpArg[k];
|
|
}
|
|
arg_buffer[i++] = 0;
|
|
}
|
|
|
|
// TODO : nCmdShow indicates if the window must be maximized, etc.
|
|
#endif
|
|
if(!Init_program(argc,argv))
|
|
{
|
|
Program_shutdown();
|
|
return 0;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
GFX2_Log(GFX2_DEBUG, "built with _MSC_VER=%d Windows ANSI Code Page=%u\n", _MSC_VER, GetACP());
|
|
#endif
|
|
Main_handler();
|
|
|
|
Program_shutdown();
|
|
return 0;
|
|
}
|
|
|
|
#if defined(WIN32) && !defined(USE_SDL) && !defined(USE_SDL2) && !defined(_MSC_VER)
|
|
/**
|
|
* MS Window entry point.
|
|
*
|
|
* This function is used when building with MinGW
|
|
*/
|
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR _lpCmdLine, int nCmdShow)
|
|
{
|
|
WCHAR *lpCmdLine = GetCommandLineW();
|
|
if (__argc == 1)
|
|
{ // avoids GetCommandLineW bug that does not always quote the program name if no arguments
|
|
do { ++lpCmdLine; } while (*lpCmdLine);
|
|
}
|
|
else
|
|
{
|
|
BOOL quoted = lpCmdLine[0] == L'"';
|
|
++lpCmdLine; // skips the " or the first letter (all paths are at least 1 letter)
|
|
while (*lpCmdLine)
|
|
{
|
|
if (quoted && lpCmdLine[0] == L'"') quoted = FALSE; // found end quote
|
|
else if (!quoted && lpCmdLine[0] == L' ')
|
|
{ // found an unquoted space, now skip all spaces
|
|
do { ++lpCmdLine; } while (lpCmdLine[0] == L' ');
|
|
break;
|
|
}
|
|
++lpCmdLine;
|
|
}
|
|
}
|
|
return wWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
|
|
}
|
|
#endif
|