diff --git a/Makefile b/Makefile index d7efa887ed..01dfd10ac6 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ include config.mk TARGET = ssnes tools/ssnes-joyconfig -OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o bps.o strl.o screenshot.o thread.o audio/hermite.o +OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o bps.o strl.o getopt.o screenshot.o thread.o audio/hermite.o JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o strl.o HEADERS = $(wildcard */*.h) $(wildcard *.h) @@ -155,10 +155,6 @@ else LIBS += $(libsnes) endif -ifeq ($(HAVE_STRL), 1) - DEFINES += -DHAVE_STRL -endif - ifeq ($(HAVE_PYTHON), 1) DEFINES += $(PYTHON_CFLAGS) -Wno-unused-parameter LIBS += $(PYTHON_LIBS) diff --git a/Makefile.win b/Makefile.win index 824b7a1331..d9a5619650 100644 --- a/Makefile.win +++ b/Makefile.win @@ -1,6 +1,6 @@ TARGET = ssnes.exe JTARGET = ssnes-joyconfig.exe -OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o bps.o ups.o strl.o screenshot.o audio/hermite.o thread.o +OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o bps.o ups.o strl.o screenshot.o audio/hermite.o thread.o getopt.o JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o strl.o CC = gcc diff --git a/docs/ssnes.1 b/docs/ssnes.1 index 03d3e4e623..ee5fb4bcbe 100644 --- a/docs/ssnes.1 +++ b/docs/ssnes.1 @@ -124,7 +124,7 @@ Connects a four-way multitap into port 2 of the SNES. This allows for up to 5 pl .TP \fB--record PATH, -r PATH\fR Activates video recording of gameplay into PATH. Using .mkv extension is recommended. -Codecs used are FFV1/FLAC, suitable for processing the material further. +Codecs used are (FFV1 or H264 RGB lossless (x264))/FLAC, suitable for processing the material further. .TP \fB--size WIDTHxHEIGHT\fR diff --git a/getopt.c b/getopt.c new file mode 100644 index 0000000000..eca153733c --- /dev/null +++ b/getopt.c @@ -0,0 +1,175 @@ +/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010-2011 - Hans-Kristian Arntzen + * + * Some code herein may be based on code found in BSNES. + * + * SSNES 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SSNES 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 SSNES. + * If not, see . + */ + +#include "getopt_ssnes.h" + +#ifndef HAVE_GETOPT_LONG + +#include +#include +#include + +char *optarg; +int optind, opterr, optopt; + +static bool is_short_option(const char *str) +{ + return str[0] == '-' && str[1] != '-'; +} + +static bool is_long_option(const char *str) +{ + return str[0] == '-' && str[1] == '-'; +} + +static int find_short_index(const char * const argv[]) +{ + for (int index = 0; argv[index]; index++) + { + if (is_short_option(argv[index])) + return index; + } + + return -1; +} + +static int find_long_index(const char * const argv[]) +{ + for (int index = 0; argv[index]; index++) + { + if (is_long_option(argv[index])) + return index; + } + + return -1; +} + +static int parse_short(const char *optstring, char * const argv[]) +{ + char arg = argv[0][1]; + if (arg == ':') + return '?'; + + const char *opt = strchr(optstring, arg); + if (!opt) + return '?'; + + bool multiple_opt = argv[0][2]; + bool takes_arg = opt[1] == ':'; + if (multiple_opt && takes_arg) // Makes little sense to allow. + return '?'; + + if (takes_arg) + { + optarg = argv[1]; + optind += 2; + return optarg ? opt[0] : '?'; + } + else if (multiple_opt) + { + memmove(&argv[0][1], &argv[0][2], strlen(&argv[0][2]) + 1); + return opt[0]; + } + else + { + optind++; + return opt[0]; + } +} + +static int parse_long(const struct option *longopts, char * const argv[]) +{ + const struct option *opt = NULL; + for (size_t indice = 0; longopts[indice].name; indice++) + { + if (strcmp(longopts[indice].name, &argv[0][2]) == 0) + { + opt = &longopts[indice]; + break; + } + } + + if (!opt) + return '?'; + + // getopt_long has an "optional" arg, but we don't bother with that. + if (opt->has_arg && !argv[1]) + return '?'; + + if (opt->has_arg) + { + optarg = argv[1]; + optind += 2; + } + else + optind++; + + if (opt->flag) + { + *opt->flag = opt->val; + return 0; + } + else + return opt->val; +} + +int getopt_long(int argc, char *argv[], + const char *optstring, const struct option *longopts, int *longindex) +{ + (void)longindex; + if (argc == 1) + return -1; + + if (optind == 0) + optind = 1; + + int short_index = find_short_index((const char * const *)&argv[optind]); + int long_index = find_long_index((const char * const *)&argv[optind]); + + // We're done here. + if (short_index == -1 && long_index == -1) + return -1; + + // Reorder argv so that non-options come last. + // Non-POSIXy, but that's what getopt does by default. + if ((short_index > 0) && ((short_index < long_index) || (long_index == -1))) + { + char *tmp = argv[optind]; + argv[optind] = argv[optind + short_index]; + argv[optind + short_index] = tmp; + short_index = 0; + } + else if ((long_index > 0) && ((long_index < short_index) || (short_index == -1))) + { + char *tmp = argv[optind]; + argv[optind] = argv[optind + long_index]; + argv[optind + long_index] = tmp; + long_index = 0; + } + + assert(short_index == 0 || long_index == 0); + + if (short_index == 0) + return parse_short(optstring, &argv[optind]); + else if (long_index == 0) + return parse_long(longopts, &argv[optind]); + else + return '?'; +} + +#endif + diff --git a/getopt_ssnes.h b/getopt_ssnes.h new file mode 100644 index 0000000000..39cac759de --- /dev/null +++ b/getopt_ssnes.h @@ -0,0 +1,52 @@ +/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010-2011 - Hans-Kristian Arntzen + * + * Some code herein may be based on code found in BSNES. + * + * SSNES 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SSNES 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 SSNES. + * If not, see . + */ + +#ifndef __SSNES_GETOPT_H +#define __SSNES_GETOPT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// Custom implementation of the GNU getopt_long for portability. +// Not designed to be fully compatible, +// but compatible with the features SSNES uses. + +#ifdef HAVE_GETOPT_LONG +#include +#else +// Avoid possible naming collitions during link since we prefer to use the actual name. +#define getopt_long(argc, argv, optstring, longopts, longindex) __getopt_long_ssnes(argc, argv, optstring, longopts, longindex) + +struct option +{ + const char *name; + int has_arg; + int *flag; + int val; +}; + +// argv[] is declared with char * const argv[] in GNU, +// but this makes no sense, as non-POSIX getopt_long mutates argv (non-opts are moved to the end). +int getopt_long(int argc, char *argv[], + const char *optstring, const struct option *longopts, int *longindex); +extern char *optarg; +extern int optind, opterr, optopt; +#endif + +#endif + diff --git a/qb/config.libs.sh b/qb/config.libs.sh index f4f8f12ad4..b6e64575c6 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -33,6 +33,7 @@ fi check_lib DYLIB $DYLIB dlopen check_lib NETPLAY -lc socket +check_lib GETOPT_LONG -lc getopt_long if [ $HAVE_DYLIB = no ] && [ $HAVE_DYNAMIC = yes ]; then echo "Dynamic loading of libsnes is enabled, but your platform does not appear to have dlopen(), use --disable-dynamic or --with-libsnes=\"-lsnes\"". @@ -117,7 +118,7 @@ check_pkgconf PYTHON python3 add_define_make OS $OS # Creates config.mk and config.h. -VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM X264RGB" +VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB GETOPT_LONG CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM X264RGB" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/ssnes.c b/ssnes.c index b1f70f0ec8..853cc13993 100644 --- a/ssnes.c +++ b/ssnes.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include "driver.h" #include "file.h" @@ -35,6 +34,7 @@ #include "screenshot.h" #include "cheats.h" #include "audio/utils.h" +#include "getopt_ssnes.h" #include #ifdef _WIN32 @@ -631,8 +631,6 @@ static void parse_input(int argc, char *argv[]) { NULL, 0, NULL, 0 } }; - int option_index = 0; - #ifdef HAVE_FFMPEG #define FFMPEG_RECORD_ARG "r:" #else @@ -655,7 +653,7 @@ static void parse_input(int argc, char *argv[]) for (;;) { val = 0; - int c = getopt_long(argc, argv, optstring, opts, &option_index); + int c = getopt_long(argc, argv, optstring, opts, NULL); int port; if (c == -1) diff --git a/strl.h b/strl.h index a3ad3639db..8e6b89f05c 100644 --- a/strl.h +++ b/strl.h @@ -21,7 +21,7 @@ #include #include -#ifdef HAVE_CONFIG +#ifdef HAVE_CONFIG_H #include "config.h" #endif