2014-10-09 01:30:22 +02:00
/*****************************************************************************
* Copyright ( c ) 2014 Ted John
* OpenRCT2 , an open source clone of Roller Coaster Tycoon 2.
2015-10-20 20:16:30 +02:00
*
2014-10-09 01:30:22 +02:00
* This file is part of OpenRCT2 .
2015-10-20 20:16:30 +02:00
*
2014-10-09 01:30:22 +02:00
* OpenRCT2 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 , either version 3 of the License , or
* ( at your option ) any later version .
* This program 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 this program . If not , see < http : //www.gnu.org/licenses/>.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <time.h>
2014-10-09 02:36:59 +02:00
# include <argparse/argparse.h>
# include "addresses.h"
2014-10-09 01:30:22 +02:00
# include "cmdline.h"
2015-05-29 00:04:02 +02:00
# include "interface/screenshot.h"
2015-02-12 12:30:57 +01:00
# include "network/network.h"
2014-10-09 01:30:22 +02:00
# include "openrct2.h"
2014-10-09 15:03:54 +02:00
# include "platform/platform.h"
2015-05-19 01:26:31 +02:00
# include "util/util.h"
2014-10-09 01:30:22 +02:00
2015-06-04 13:46:17 +02:00
# ifdef ENABLE_TESTS
# include "../test/tests.h"
# else
static int cmdline_for_test_error ( const char * * argv , int argc )
{
printf ( " OpenRCT2 has not been built with the test suite. \n " ) ;
2015-06-04 23:48:47 +02:00
return - 1 ;
2015-06-04 13:46:17 +02:00
}
# endif
2014-10-09 01:30:22 +02:00
typedef struct tm tm_t ;
2014-10-09 02:36:59 +02:00
typedef struct argparse_option argparse_option_t ;
typedef struct argparse argparse_t ;
2014-10-09 01:30:22 +02:00
2015-05-19 01:26:31 +02:00
typedef int ( * cmdline_action ) ( const char * * argv , int argc ) ;
2014-10-09 01:30:22 +02:00
int gExitCode = 0 ;
2015-12-16 21:48:27 +01:00
int sprite_mode ;
2014-10-09 01:30:22 +02:00
2015-08-18 09:30:55 +02:00
# ifndef DISABLE_NETWORK
2015-07-10 21:53:41 +02:00
int gNetworkStart = NETWORK_MODE_NONE ;
char gNetworkStartHost [ 128 ] ;
int gNetworkStartPort = NETWORK_DEFAULT_PORT ;
2015-08-18 09:30:55 +02:00
# endif // DISABLE_NETWORK
2015-07-10 21:53:41 +02:00
2014-10-09 01:30:22 +02:00
static void print_launch_information ( ) ;
2015-11-08 13:49:19 +01:00
static void print_version ( ) ;
2015-05-19 01:26:31 +02:00
static int cmdline_call_action ( const char * * argv , int argc ) ;
2014-10-09 01:30:22 +02:00
2014-10-09 02:36:59 +02:00
static const char * const usage [ ] = {
" openrct2 <command> [options] [<args>] " ,
" openrct2 <path> [options] " ,
" openrct2 intro [options] " ,
" openrct2 edit [path] [options] " ,
NULL
} ;
2014-10-09 01:30:22 +02:00
/**
* A shared entry point to OpenRCT2 . The command lines must be parsed before any further action is done . Invalid command lines
* will then terminate before any initialisation has even been done .
* @ returns 1 if the game should run , otherwise 0.
*/
2014-11-26 17:27:21 +01:00
int cmdline_run ( const char * * argv , int argc )
2014-10-09 01:30:22 +02:00
{
2015-10-20 20:16:30 +02:00
//
2015-08-19 18:07:04 +02:00
int version = 0 , headless = 0 , verbose = 0 , width = 0 , height = 0 , port = 0 ;
2015-02-12 12:30:57 +01:00
char * server = NULL ;
2015-11-03 23:20:35 +01:00
char * userDataPath = NULL ;
2015-12-17 22:49:39 +01:00
char * openrctDataPath = NULL ;
2014-10-09 02:36:59 +02:00
argparse_option_t options [ ] = {
OPT_HELP ( ) ,
OPT_BOOLEAN ( ' v ' , " version " , & version , " show version information and exit " ) ,
2015-08-19 18:07:04 +02:00
OPT_BOOLEAN ( 0 , " headless " , & headless , " run OpenRCT2 headless " ) ,
2014-11-21 19:39:56 +01:00
OPT_BOOLEAN ( 0 , " verbose " , & verbose , " log verbose messages " ) ,
2015-05-31 20:35:40 +02:00
OPT_INTEGER ( ' m ' , " mode " , & sprite_mode , " the type of sprite conversion. 0 = default, 1 = simple closest pixel match, 2 = dithering " ) ,
2015-02-12 12:30:57 +01:00
OPT_STRING ( 0 , " server " , & server , " server to connect to " ) ,
2015-12-17 22:49:39 +01:00
OPT_INTEGER ( 0 , " port " , & port , " Port to use. If used with --server, it will connect to specified server at this port, otherwise it will start the server " ) ,
2015-11-03 23:20:35 +01:00
OPT_STRING ( 0 , " user-data-path " , & userDataPath , " path to the user data directory (containing config.ini) " ) ,
2015-12-17 22:49:39 +01:00
OPT_STRING ( 0 , " openrct-data-path " , & openrctDataPath , " path to the OpenRCT2 data directory (containing languages) " ) ,
2014-10-09 02:36:59 +02:00
OPT_END ( )
} ;
argparse_t argparse ;
argparse_init ( & argparse , options , usage , 0 ) ;
2015-12-12 20:45:51 +01:00
size_t argvsize = sizeof ( char * * ) * argc ;
/**
* argparse_parse ends up inadvertently mutating the argv variable , setting
* a null pointer in the array . Because of this , AppKit in OS X 10.10 will
* dereference it , causing a segmentation fault .
*/
2015-12-20 05:46:52 +01:00
const char * * mutableArgv = malloc ( argvsize ) ;
# ifdef __APPLE__
/**
* Fixes problems with the default settings in the Xcode debugger
* with it adding the option " -NSDocumentRevisionsDebugMode "
*/
int k = 0 ;
for ( int i = 0 ; i < argc ; + + i )
if ( strcmp ( argv [ k ] , " -NSDocumentRevisionsDebugMode " ) ! = 0 )
mutableArgv [ k + + ] = argv [ i ] ;
argc = k ;
# else
2015-12-12 20:45:51 +01:00
memcpy ( mutableArgv , argv , argvsize ) ;
2015-12-20 05:46:52 +01:00
# endif
argc = argparse_parse ( & argparse , argc , mutableArgv ) ;
2014-10-09 01:30:22 +02:00
2014-10-09 02:36:59 +02:00
if ( version ) {
2015-11-08 13:49:19 +01:00
print_version ( ) ;
2015-12-12 20:45:51 +01:00
free ( mutableArgv ) ;
2014-10-09 02:36:59 +02:00
return 0 ;
}
2015-08-19 18:07:04 +02:00
if ( headless )
gOpenRCT2Headless = true ;
2014-11-21 19:39:56 +01:00
if ( verbose )
_log_levels [ DIAGNOSTIC_LEVEL_VERBOSE ] = 1 ;
2015-11-03 23:20:35 +01:00
if ( userDataPath ! = NULL ) {
safe_strncpy ( gCustomUserDataPath , userDataPath , sizeof ( gCustomUserDataPath ) ) ;
}
2015-12-17 22:49:39 +01:00
if ( openrctDataPath ! = NULL ) {
safe_strncpy ( gCustomOpenrctDataPath , openrctDataPath , sizeof ( gCustomOpenrctDataPath ) ) ;
}
2015-08-18 09:30:55 +02:00
# ifndef DISABLE_NETWORK
2015-02-12 12:30:57 +01:00
if ( port ! = 0 ) {
2015-07-10 21:53:41 +02:00
gNetworkStart = NETWORK_MODE_SERVER ;
2015-02-12 12:30:57 +01:00
gNetworkStartPort = port ;
}
if ( server ! = NULL ) {
2015-07-10 21:53:41 +02:00
gNetworkStart = NETWORK_MODE_CLIENT ;
2015-10-30 15:18:29 +01:00
safe_strncpy ( gNetworkStartHost , server , sizeof ( gNetworkStartHost ) ) ;
2015-02-12 12:30:57 +01:00
}
2015-08-18 09:30:55 +02:00
# endif // DISABLE_NETWORK
2015-02-12 12:30:57 +01:00
2014-10-09 02:36:59 +02:00
if ( argc ! = 0 ) {
2015-11-16 13:59:01 +01:00
// see comment next to cmdline_call_action for expected return codes
2015-12-14 22:23:12 +01:00
gExitCode = cmdline_call_action ( ( const char * * ) mutableArgv , argc ) ;
2015-12-12 20:45:51 +01:00
free ( mutableArgv ) ;
2015-11-14 17:36:32 +01:00
if ( gExitCode < 0 ) {
2015-11-16 13:59:01 +01:00
// action failed, don't change exit code
// and don't start the game
2015-11-14 17:36:32 +01:00
return 0 ;
} else if ( gExitCode > 0 ) {
2015-11-16 13:59:01 +01:00
// action successful, but don't start the game
// change exit code to success
2015-11-14 17:36:32 +01:00
gExitCode = 0 ;
2015-05-19 04:53:37 +02:00
return 0 ;
2015-11-08 13:49:19 +01:00
}
2015-11-16 13:59:01 +01:00
// start the game, so far exit code means success.
2015-12-12 20:45:51 +01:00
} else {
free ( mutableArgv ) ;
2014-10-09 01:30:22 +02:00
}
2015-11-08 13:49:19 +01:00
// Headless mode requires a park to open
if ( gOpenRCT2Headless ) {
if ( str_is_null_or_empty ( gOpenRCT2StartupActionPath ) ) {
printf ( " You must specify a park to open in headless mode. \n " ) ;
gExitCode = - 1 ;
return 0 ;
}
}
if ( verbose ) {
print_launch_information ( ) ;
}
2014-10-09 01:30:22 +02:00
return 1 ;
}
static void print_launch_information ( )
{
2015-11-08 13:49:19 +01:00
char buffer [ 256 ] ;
2014-10-09 01:30:22 +02:00
time_t timer ;
tm_t * tmInfo ;
2015-11-08 13:49:19 +01:00
// Print name and version information
openrct2_write_full_version_info ( buffer , sizeof ( buffer ) ) ;
printf ( " %s \n " , buffer ) ;
printf ( " %s (%s) \n " , OPENRCT2_PLATFORM , OPENRCT2_ARCHITECTURE ) ;
printf ( " @ %s \n \n " , OPENRCT2_TIMESTAMP ) ;
2014-10-09 01:30:22 +02:00
// Print current time
time ( & timer ) ;
tmInfo = localtime ( & timer ) ;
strftime ( buffer , sizeof ( buffer ) , " %Y/%m/%d %H:%M:%S " , tmInfo ) ;
2015-11-08 13:49:19 +01:00
printf ( " VERBOSE: time is %s \n " , buffer ) ;
2014-10-09 01:30:22 +02:00
// TODO Print other potential information (e.g. user, hardware)
2015-03-15 05:38:37 +01:00
}
2015-05-19 01:26:31 +02:00
2015-11-08 13:49:19 +01:00
static void print_version ( )
{
char buffer [ 256 ] ;
openrct2_write_full_version_info ( buffer , sizeof ( buffer ) ) ;
printf ( " %s \n " , buffer ) ;
printf ( " %s (%s) \n " , OPENRCT2_PLATFORM , OPENRCT2_ARCHITECTURE ) ;
}
2015-05-19 01:26:31 +02:00
static int cmdline_for_intro ( const char * * argv , int argc )
{
gOpenRCT2StartupAction = STARTUP_ACTION_INTRO ;
return 0 ;
}
static int cmdline_for_edit ( const char * * argv , int argc )
{
gOpenRCT2StartupAction = STARTUP_ACTION_EDIT ;
if ( argc > = 1 )
2015-10-30 15:18:29 +01:00
safe_strncpy ( gOpenRCT2StartupActionPath , argv [ 0 ] , 512 ) ;
2015-05-19 01:26:31 +02:00
return 0 ;
}
static int cmdline_for_none ( const char * * argv , int argc )
{
assert ( argc > = 1 ) ;
if ( platform_file_exists ( argv [ 0 ] ) ) {
gOpenRCT2StartupAction = STARTUP_ACTION_OPEN ;
2015-10-30 15:18:29 +01:00
safe_strncpy ( gOpenRCT2StartupActionPath , argv [ 0 ] , 512 ) ;
2015-05-19 01:26:31 +02:00
return 0 ;
} else {
fprintf ( stderr , " error: %s does not exist \n " , argv [ 0 ] ) ;
return - 1 ;
}
}
2015-11-16 13:59:01 +01:00
// see comment next to cmdline_call_action for expected return codes
2015-05-19 01:26:31 +02:00
struct { const char * firstArg ; cmdline_action action ; } cmdline_table [ ] = {
{ " intro " , cmdline_for_intro } ,
{ " edit " , cmdline_for_edit } ,
2015-05-29 00:04:02 +02:00
{ " sprite " , cmdline_for_sprite } ,
2015-06-02 03:41:40 +02:00
{ " screenshot " , cmdline_for_screenshot } ,
2015-06-04 13:46:17 +02:00
# ifdef ENABLE_TESTS
{ " test " , cmdline_for_test } ,
# else
{ " test " , cmdline_for_test_error } ,
# endif
2015-05-19 01:26:31 +02:00
} ;
2015-11-16 13:59:01 +01:00
/**
* This function delegates starting the game to different handlers , if found .
2015-12-14 22:23:12 +01:00
*
2015-11-16 13:59:01 +01:00
* Three cases of return values are supported :
* - result < 0 means failure , will exit with error code
* This case is useful when user provided wrong arguments or the requested
* action failed
* - result > 0 means success , won ' t start game , will exit program with success code
* This case is useful when you want to do some batch action and signalize
* success to the user .
* - result = = 0 means success , will launch the game .
* This is default when ran with no arguments .
*/
2015-05-19 01:26:31 +02:00
static int cmdline_call_action ( const char * * argv , int argc )
{
for ( int i = 0 ; i < countof ( cmdline_table ) ; i + + ) {
if ( _stricmp ( cmdline_table [ i ] . firstArg , argv [ 0 ] ) ! = 0 )
continue ;
return cmdline_table [ i ] . action ( argv + 1 , argc - 1 ) ;
}
return cmdline_for_none ( argv , argc ) ;
2015-08-18 09:30:55 +02:00
}