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