diff --git a/.gitignore b/.gitignore
index c6127b3..f280047 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,52 +1,51 @@
-# Prerequisites
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows shortcuts
+*.lnk
+
+# build files
+*.o
*.d
-# Object files
-*.o
-*.ko
-*.obj
-*.elf
-
-# Linker output
-*.ilk
-*.map
-*.exp
-
-# Precompiled Headers
-*.gch
-*.pch
-
-# Libraries
-*.lib
-*.a
-*.la
-*.lo
-
-# Shared objects (inc. Windows DLLs)
-*.dll
+#binaries
*.so
-*.so.*
-*.dylib
-# Executables
-*.exe
-*.out
-*.app
-*.i*86
-*.x86_64
-*.hex
+#libs
+*.a
-# Debug files
-*.dSYM/
-*.su
-*.idb
-*.pdb
-
-# Kernel Module Compile Results
-*.mod*
-*.cmd
-.tmp_versions/
-modules.order
-Module.symvers
-Mkfile.old
-dkms.conf
+#files created by build
+.idea
+*.apk
+*.ap_
+*.class
+projects/Android/build/*
+assets/oculussig*
+Projects/Android/.externalNativeBuild/ndkBuild/debug/arm64-v8a/android_gradle_build.json
+*.json
+Projects/Android/.externalNativeBuild/ndkBuild/debug/arm64-v8a/ndkBuild_build_command.txt
+Projects/Android/.externalNativeBuild/ndkBuild/debug/arm64-v8a/ndkBuild_build_output.txt
+Projects/Android/.externalNativeBuild/ndkBuild/release/arm64-v8a/ndkBuild_build_command.txt
+Projects/Android/.externalNativeBuild/ndkBuild/release/armeabi-v7a/ndkBuild_build_output.txt
+*.bin
+Projects/Android/.gradle/4.4/fileHashes/fileHashes.lock
+*.ser
+Projects/Android/.idea/codeStyles/Project.xml
+Projects/Android/.externalNativeBuild/ndkBuild/debug/armeabi-v7a/ndkBuild_build_command.txt
+Projects/Android/.externalNativeBuild/ndkBuild/release/armeabi-v7a/ndkBuild_build_command.txt
+Projects/Android/.externalNativeBuild/ndkBuild/debug/armeabi-v7a/ndkBuild_build_output.txt
+Projects/Android/.externalNativeBuild/ndkBuild/release/arm64-v8a/ndkBuild_build_output.txt
+Projects/Android/.gradle/4.6/fileHashes/fileHashes.lock
+Projects/Android/.gradle/vcsWorkingDirs/gc.properties
+drbeef-release-key.keystore
+assets/pak6.pak
+Projects/Android/Android.iml
+Projects/Projects.iml
+VrSamples-Quake2Quest.iml
diff --git a/Projects/Android/AndroidManifest.xml b/Projects/Android/AndroidManifest.xml
new file mode 100644
index 0000000..5b7f173
--- /dev/null
+++ b/Projects/Android/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Projects/Android/android.debug.keystore b/Projects/Android/android.debug.keystore
new file mode 100644
index 0000000..cdc70bf
Binary files /dev/null and b/Projects/Android/android.debug.keystore differ
diff --git a/Projects/Android/build.bat b/Projects/Android/build.bat
new file mode 100644
index 0000000..facf79f
--- /dev/null
+++ b/Projects/Android/build.bat
@@ -0,0 +1,29 @@
+@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject
+
+@setlocal enableextensions enabledelayedexpansion
+
+@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort
+
+@set P=..
+
+:TryAgain
+
+@rem @echo P = %P%
+
+@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found
+
+@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort
+
+@set P=%P%\..
+
+@goto :TryAgain
+
+:Found
+
+@set P=%P%\bin\scripts\build
+@call %P%\build.py.bat %1 %2 %3 %4 %5
+@goto :End
+
+:Abort
+
+:End
diff --git a/Projects/Android/build.gradle b/Projects/Android/build.gradle
new file mode 100644
index 0000000..6b1b697
--- /dev/null
+++ b/Projects/Android/build.gradle
@@ -0,0 +1,59 @@
+apply plugin: 'com.android.application'
+apply from: "${rootProject.projectDir}/VrApp.gradle"
+
+android {
+ // This is the name of the generated apk file, which will have
+ // -debug.apk or -release.apk appended to it.
+ // The filename doesn't effect the Android installation process.
+ // Use only letters to remain compatible with the package name.
+ project.archivesBaseName = "quake2quest"
+
+ defaultConfig {
+ // Gradle replaces the manifest package with this value, which must
+ // be unique on a system. If you don't change it, a new app
+ // will replace an older one.
+ applicationId "com.drbeef." + project.archivesBaseName
+
+ // override app plugin abiFilters for both 32 and 64-bit support
+ externalNativeBuild {
+ ndk {
+ abiFilters 'armeabi-v7a'
+ }
+ ndkBuild {
+ abiFilters 'armeabi-v7a'
+ }
+ }
+ minSdkVersion 24
+ targetSdkVersion 26
+ }
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['../../java']
+ jniLibs.srcDir 'libs'
+ res.srcDirs = ['../../res']
+ assets.srcDirs = ['../../assets']
+ }
+ }
+ compileOptions {
+ sourceCompatibility = '1.8'
+ targetCompatibility = '1.8'
+ }
+ compileSdkVersion = 24
+ buildToolsVersion = '29.0.1'
+}
+
+dependencies {
+ implementation "com.android.support:support-compat:24.2.0"
+ implementation "com.android.support:support-core-utils:24.2.0"
+}
+
+repositories {
+ google()
+}
+buildscript {
+ repositories {
+ google()
+ }
+}
\ No newline at end of file
diff --git a/Projects/Android/build.py b/Projects/Android/build.py
new file mode 100644
index 0000000..36335c3
--- /dev/null
+++ b/Projects/Android/build.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+# This first bit of code is common bootstrapping code
+# to determine the SDK root, and to set up the import
+# path for additional python code.
+
+#begin bootstrap
+import os
+import sys
+
+def init():
+ root = os.path.realpath( os.path.dirname(os.path.realpath(__file__) ) )
+ os.chdir(root) # make sure we are always executing from the project directory
+ while( os.path.isdir( os.path.join(root, 'bin/scripts/build') ) == False ):
+ root = os.path.realpath( os.path.join(root, '..') )
+ if( len(root) <= 5 ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\')
+ print('Unable to find SDK root. Exiting.')
+ sys.exit(1)
+ root = os.path.abspath(root)
+ os.environ['OCULUS_SDK_PATH'] = root
+ sys.path.append( root + "/bin/scripts/build" )
+
+init()
+import ovrbuild
+ovrbuild.init()
+#end bootstrap
+
+
+ovrbuild.build()
diff --git a/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Projects/Android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..727b688
Binary files /dev/null and b/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Projects/Android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..c9ebeef
--- /dev/null
+++ b/Projects/Android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sat Jul 27 06:47:04 BST 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/Projects/Android/gradlew b/Projects/Android/gradlew
new file mode 100644
index 0000000..cccdd3d
--- /dev/null
+++ b/Projects/Android/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/Projects/Android/gradlew.bat b/Projects/Android/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/Projects/Android/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Projects/Android/jni/Android.mk b/Projects/Android/jni/Android.mk
new file mode 100644
index 0000000..1782f4e
--- /dev/null
+++ b/Projects/Android/jni/Android.mk
@@ -0,0 +1,10 @@
+include $(GL4ES_PATH)/Android.mk
+include $(SUPPORT_LIBS)/SDL2/SDL2/Android.mk
+include $(SUPPORT_LIBS)/SDL2/SDL2_mixer/Android.mk
+include $(SUPPORT_LIBS)/jpeg8d/Android.mk
+include $(SUPPORT_LIBS)/libzip/Android.mk
+include $(SUPPORT_LIBS)/libpng/Android.mk
+include $(SUPPORT_LIBS)/sigc++/Android.mk
+include $(TOP_DIR)/quake2/Android.mk
+include $(TOP_DIR)/quake2/Android_game.mk
+include $(TOP_DIR)/quake2/Android_gl1.mk
diff --git a/Projects/Android/jni/Application.mk b/Projects/Android/jni/Application.mk
new file mode 100644
index 0000000..b129e88
--- /dev/null
+++ b/Projects/Android/jni/Application.mk
@@ -0,0 +1,22 @@
+# MAKEFILE_LIST specifies the current used Makefiles, of which this is the last
+# one. I use that to obtain the Application.mk dir then import the root
+# Application.mk.
+ROOT_DIR := $(dir $(lastword $(MAKEFILE_LIST)))../../../../..
+NDK_MODULE_PATH := $(ROOT_DIR)
+
+APP_PLATFORM := android-19
+
+APP_CFLAGS += -Wl,--no-undefined
+
+APPLICATIONMK_PATH = $(call my-dir)
+
+TOP_DIR := $(APPLICATIONMK_PATH)
+SUPPORT_LIBS := $(APPLICATIONMK_PATH)/SupportLibs
+GL4ES_PATH := $(SUPPORT_LIBS)/gl4es
+
+APP_ALLOW_MISSING_DEPS=true
+
+APP_MODULES := yquake2 yquake2_game gl4es yquake2_gl1
+APP_STL := c++_shared
+
+
diff --git a/Projects/Android/jni/RTCWVR/RTCWVR_SurfaceView.c b/Projects/Android/jni/RTCWVR/RTCWVR_SurfaceView.c
new file mode 100644
index 0000000..c863162
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/RTCWVR_SurfaceView.c
@@ -0,0 +1,2015 @@
+/************************************************************************************
+
+Filename : Q2VR_SurfaceView.c based on VrCubeWorld_SurfaceView.c
+Content : This sample uses a plain Android SurfaceView and handles all
+ Activity and Surface life cycle events in native code. This sample
+ does not use the application framework and also does not use LibOVR.
+ This sample only uses the VrApi.
+Created : March, 2015
+Authors : J.M.P. van Waveren / Simon Brown
+
+Copyright : Copyright 2015 Oculus VR, LLC. All Rights reserved.
+
+*************************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include // for prctl( PR_SET_NAME )
+#include
+#include // for native window JNI
+#include
+
+#include "argtable3.h"
+#include "VrInput.h"
+#include "VrCvars.h"
+
+#include
+#include
+#include
+#include
+
+
+#include "VrApi.h"
+#include "VrApi_Helpers.h"
+#include "VrApi_SystemUtils.h"
+#include "VrApi_Input.h"
+#include "VrApi_Types.h"
+
+#include
+
+#include
+#include
+#include
+
+#include "VrCompositor.h"
+#include "VrInput.h"
+
+#include "../quake2/src/client/header/client.h"
+
+#if !defined( EGL_OPENGL_ES3_BIT_KHR )
+#define EGL_OPENGL_ES3_BIT_KHR 0x0040
+#endif
+
+// EXT_texture_border_clamp
+#ifndef GL_CLAMP_TO_BORDER
+#define GL_CLAMP_TO_BORDER 0x812D
+#endif
+
+#ifndef GL_TEXTURE_BORDER_COLOR
+#define GL_TEXTURE_BORDER_COLOR 0x1004
+#endif
+
+
+// Must use EGLSyncKHR because the VrApi still supports OpenGL ES 2.0
+#define EGL_SYNC
+
+#if defined EGL_SYNC
+// EGL_KHR_reusable_sync
+PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;
+PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR;
+PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR;
+PFNEGLSIGNALSYNCKHRPROC eglSignalSyncKHR;
+PFNEGLGETSYNCATTRIBKHRPROC eglGetSyncAttribKHR;
+#endif
+
+//Let's go to the maximum!
+int CPU_LEVEL = 4;
+int GPU_LEVEL = 4;
+int NUM_MULTI_SAMPLES = 1;
+float SS_MULTIPLIER = 1.25f;
+
+vec2_t cylinderSize = {1280, 720};
+
+jclass clazz;
+
+float radians(float deg) {
+ return (deg * M_PI) / 180.0;
+}
+
+float degrees(float rad) {
+ return (rad * 180.0) / M_PI;
+}
+
+/* global arg_xxx structs */
+struct arg_dbl *ss;
+struct arg_int *cpu;
+struct arg_int *gpu;
+struct arg_end *end;
+
+char **argv;
+int argc=0;
+
+extern cvar_t *r_lefthand;
+extern cvar_t *cl_paused;
+
+enum control_scheme {
+ RIGHT_HANDED_DEFAULT = 0,
+ LEFT_HANDED_DEFAULT = 10,
+ GAMEPAD = 20 //Not implemented, someone else can do this!
+};
+
+/*
+================================================================================
+
+System Clock Time in millis
+
+================================================================================
+*/
+
+double GetTimeInMilliSeconds()
+{
+ struct timespec now;
+ clock_gettime( CLOCK_MONOTONIC, &now );
+ return ( now.tv_sec * 1e9 + now.tv_nsec ) * (double)(1e-6);
+}
+
+/*
+================================================================================
+
+LAMBDA1VR Stuff
+
+================================================================================
+*/
+
+void Qcommon_Init (int argc, char **argv);
+
+bool useScreenLayer()
+{
+ //TODO
+ return (showingScreenLayer || (cls.state != ca_connected && cls.state != ca_active) || cls.key_dest != key_game) || cl.cinematictime != 0;
+}
+
+int runStatus = -1;
+void Q2VR_exit(int exitCode)
+{
+ runStatus = exitCode;
+}
+
+static void UnEscapeQuotes( char *arg )
+{
+ char *last = NULL;
+ while( *arg ) {
+ if( *arg == '"' && *last == '\\' ) {
+ char *c_curr = arg;
+ char *c_last = last;
+ while( *c_curr ) {
+ *c_last = *c_curr;
+ c_last = c_curr;
+ c_curr++;
+ }
+ *c_last = '\0';
+ }
+ last = arg;
+ arg++;
+ }
+}
+
+static int ParseCommandLine(char *cmdline, char **argv)
+{
+ char *bufp;
+ char *lastp = NULL;
+ int argc, last_argc;
+ argc = last_argc = 0;
+ for ( bufp = cmdline; *bufp; ) {
+ while ( isspace(*bufp) ) {
+ ++bufp;
+ }
+ if ( *bufp == '"' ) {
+ ++bufp;
+ if ( *bufp ) {
+ if ( argv ) {
+ argv[argc] = bufp;
+ }
+ ++argc;
+ }
+ while ( *bufp && ( *bufp != '"' || *lastp == '\\' ) ) {
+ lastp = bufp;
+ ++bufp;
+ }
+ } else {
+ if ( *bufp ) {
+ if ( argv ) {
+ argv[argc] = bufp;
+ }
+ ++argc;
+ }
+ while ( *bufp && ! isspace(*bufp) ) {
+ ++bufp;
+ }
+ }
+ if ( *bufp ) {
+ if ( argv ) {
+ *bufp = '\0';
+ }
+ ++bufp;
+ }
+ if( argv && last_argc != argc ) {
+ UnEscapeQuotes( argv[last_argc] );
+ }
+ last_argc = argc;
+ }
+ if ( argv ) {
+ argv[argc] = NULL;
+ }
+ return(argc);
+}
+
+/*
+================================================================================
+
+OpenGL-ES Utility Functions
+
+================================================================================
+*/
+
+typedef struct
+{
+ bool multi_view; // GL_OVR_multiview, GL_OVR_multiview2
+ bool EXT_texture_border_clamp; // GL_EXT_texture_border_clamp, GL_OES_texture_border_clamp
+} OpenGLExtensions_t;
+
+OpenGLExtensions_t glExtensions;
+
+static void EglInitExtensions()
+{
+#if defined EGL_SYNC
+ eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress( "eglCreateSyncKHR" );
+ eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress( "eglDestroySyncKHR" );
+ eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress( "eglClientWaitSyncKHR" );
+ eglSignalSyncKHR = (PFNEGLSIGNALSYNCKHRPROC) eglGetProcAddress( "eglSignalSyncKHR" );
+ eglGetSyncAttribKHR = (PFNEGLGETSYNCATTRIBKHRPROC) eglGetProcAddress( "eglGetSyncAttribKHR" );
+#endif
+
+ const char * allExtensions = (const char *)glGetString( GL_EXTENSIONS );
+ if ( allExtensions != NULL )
+ {
+ glExtensions.multi_view = strstr( allExtensions, "GL_OVR_multiview2" ) &&
+ strstr( allExtensions, "GL_OVR_multiview_multisampled_render_to_texture" );
+
+ glExtensions.EXT_texture_border_clamp = false;//strstr( allExtensions, "GL_EXT_texture_border_clamp" ) ||
+ //strstr( allExtensions, "GL_OES_texture_border_clamp" );
+ }
+}
+
+static const char * EglErrorString( const EGLint error )
+{
+ switch ( error )
+ {
+ case EGL_SUCCESS: return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
+ case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
+ case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
+ case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
+ case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
+ case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
+ default: return "unknown";
+ }
+}
+
+static const char * GlFrameBufferStatusString( GLenum status )
+{
+ switch ( status )
+ {
+ case GL_FRAMEBUFFER_UNDEFINED: return "GL_FRAMEBUFFER_UNDEFINED";
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_UNSUPPORTED: return "GL_FRAMEBUFFER_UNSUPPORTED";
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
+ default: return "unknown";
+ }
+}
+
+
+/*
+================================================================================
+
+ovrEgl
+
+================================================================================
+*/
+
+typedef struct
+{
+ EGLint MajorVersion;
+ EGLint MinorVersion;
+ EGLDisplay Display;
+ EGLConfig Config;
+ EGLSurface TinySurface;
+ EGLSurface MainSurface;
+ EGLContext Context;
+} ovrEgl;
+
+static void ovrEgl_Clear( ovrEgl * egl )
+{
+ egl->MajorVersion = 0;
+ egl->MinorVersion = 0;
+ egl->Display = 0;
+ egl->Config = 0;
+ egl->TinySurface = EGL_NO_SURFACE;
+ egl->MainSurface = EGL_NO_SURFACE;
+ egl->Context = EGL_NO_CONTEXT;
+}
+
+static void ovrEgl_CreateContext( ovrEgl * egl, const ovrEgl * shareEgl )
+{
+ if ( egl->Display != 0 )
+ {
+ return;
+ }
+
+ egl->Display = eglGetDisplay( EGL_DEFAULT_DISPLAY );
+ ALOGV( " eglInitialize( Display, &MajorVersion, &MinorVersion )" );
+ eglInitialize( egl->Display, &egl->MajorVersion, &egl->MinorVersion );
+ // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample
+ // flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in
+ // settings, and that is completely wasted for our warp target.
+ const int MAX_CONFIGS = 1024;
+ EGLConfig configs[MAX_CONFIGS];
+ EGLint numConfigs = 0;
+ if ( eglGetConfigs( egl->Display, configs, MAX_CONFIGS, &numConfigs ) == EGL_FALSE )
+ {
+ ALOGE( " eglGetConfigs() failed: %s", EglErrorString( eglGetError() ) );
+ return;
+ }
+ const EGLint configAttribs[] =
+ {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8, // need alpha for the multi-pass timewarp compositor
+ EGL_DEPTH_SIZE, 0,
+ EGL_STENCIL_SIZE, 0,
+ EGL_SAMPLES, 0,
+ EGL_NONE
+ };
+ egl->Config = 0;
+ for ( int i = 0; i < numConfigs; i++ )
+ {
+ EGLint value = 0;
+
+ eglGetConfigAttrib( egl->Display, configs[i], EGL_RENDERABLE_TYPE, &value );
+ if ( ( value & EGL_OPENGL_ES3_BIT_KHR ) != EGL_OPENGL_ES3_BIT_KHR )
+ {
+ continue;
+ }
+
+ // The pbuffer config also needs to be compatible with normal window rendering
+ // so it can share textures with the window context.
+ eglGetConfigAttrib( egl->Display, configs[i], EGL_SURFACE_TYPE, &value );
+ if ( ( value & ( EGL_WINDOW_BIT | EGL_PBUFFER_BIT ) ) != ( EGL_WINDOW_BIT | EGL_PBUFFER_BIT ) )
+ {
+ continue;
+ }
+
+ int j = 0;
+ for ( ; configAttribs[j] != EGL_NONE; j += 2 )
+ {
+ eglGetConfigAttrib( egl->Display, configs[i], configAttribs[j], &value );
+ if ( value != configAttribs[j + 1] )
+ {
+ break;
+ }
+ }
+ if ( configAttribs[j] == EGL_NONE )
+ {
+ egl->Config = configs[i];
+ break;
+ }
+ }
+ if ( egl->Config == 0 )
+ {
+ ALOGE( " eglChooseConfig() failed: %s", EglErrorString( eglGetError() ) );
+ return;
+ }
+ EGLint contextAttribs[] =
+ {
+ EGL_CONTEXT_CLIENT_VERSION, 3,
+ EGL_NONE
+ };
+ ALOGV( " Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )" );
+ egl->Context = eglCreateContext( egl->Display, egl->Config, ( shareEgl != NULL ) ? shareEgl->Context : EGL_NO_CONTEXT, contextAttribs );
+ if ( egl->Context == EGL_NO_CONTEXT )
+ {
+ ALOGE( " eglCreateContext() failed: %s", EglErrorString( eglGetError() ) );
+ return;
+ }
+ const EGLint surfaceAttribs[] =
+ {
+ EGL_WIDTH, 16,
+ EGL_HEIGHT, 16,
+ EGL_NONE
+ };
+ ALOGV( " TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )" );
+ egl->TinySurface = eglCreatePbufferSurface( egl->Display, egl->Config, surfaceAttribs );
+ if ( egl->TinySurface == EGL_NO_SURFACE )
+ {
+ ALOGE( " eglCreatePbufferSurface() failed: %s", EglErrorString( eglGetError() ) );
+ eglDestroyContext( egl->Display, egl->Context );
+ egl->Context = EGL_NO_CONTEXT;
+ return;
+ }
+ ALOGV( " eglMakeCurrent( Display, TinySurface, TinySurface, Context )" );
+ if ( eglMakeCurrent( egl->Display, egl->TinySurface, egl->TinySurface, egl->Context ) == EGL_FALSE )
+ {
+ ALOGE( " eglMakeCurrent() failed: %s", EglErrorString( eglGetError() ) );
+ eglDestroySurface( egl->Display, egl->TinySurface );
+ eglDestroyContext( egl->Display, egl->Context );
+ egl->Context = EGL_NO_CONTEXT;
+ return;
+ }
+}
+
+static void ovrEgl_DestroyContext( ovrEgl * egl )
+{
+ if ( egl->Display != 0 )
+ {
+ ALOGE( " eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )" );
+ if ( eglMakeCurrent( egl->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ) == EGL_FALSE )
+ {
+ ALOGE( " eglMakeCurrent() failed: %s", EglErrorString( eglGetError() ) );
+ }
+ }
+ if ( egl->Context != EGL_NO_CONTEXT )
+ {
+ ALOGE( " eglDestroyContext( Display, Context )" );
+ if ( eglDestroyContext( egl->Display, egl->Context ) == EGL_FALSE )
+ {
+ ALOGE( " eglDestroyContext() failed: %s", EglErrorString( eglGetError() ) );
+ }
+ egl->Context = EGL_NO_CONTEXT;
+ }
+ if ( egl->TinySurface != EGL_NO_SURFACE )
+ {
+ ALOGE( " eglDestroySurface( Display, TinySurface )" );
+ if ( eglDestroySurface( egl->Display, egl->TinySurface ) == EGL_FALSE )
+ {
+ ALOGE( " eglDestroySurface() failed: %s", EglErrorString( eglGetError() ) );
+ }
+ egl->TinySurface = EGL_NO_SURFACE;
+ }
+ if ( egl->Display != 0 )
+ {
+ ALOGE( " eglTerminate( Display )" );
+ if ( eglTerminate( egl->Display ) == EGL_FALSE )
+ {
+ ALOGE( " eglTerminate() failed: %s", EglErrorString( eglGetError() ) );
+ }
+ egl->Display = 0;
+ }
+}
+
+/*
+================================================================================
+
+ovrFramebuffer
+
+================================================================================
+*/
+
+
+static void ovrFramebuffer_Clear( ovrFramebuffer * frameBuffer )
+{
+ frameBuffer->Width = 0;
+ frameBuffer->Height = 0;
+ frameBuffer->Multisamples = 0;
+ frameBuffer->TextureSwapChainLength = 0;
+ frameBuffer->TextureSwapChainIndex = 0;
+ frameBuffer->ColorTextureSwapChain = NULL;
+ frameBuffer->DepthBuffers = NULL;
+ frameBuffer->FrameBuffers = NULL;
+}
+
+static bool ovrFramebuffer_Create( ovrFramebuffer * frameBuffer, const GLenum colorFormat, const int width, const int height, const int multisamples )
+{
+ LOAD_GLES2(glBindTexture);
+ LOAD_GLES2(glTexParameteri);
+ LOAD_GLES2(glGenRenderbuffers);
+ LOAD_GLES2(glBindRenderbuffer);
+ LOAD_GLES2(glRenderbufferStorage);
+ LOAD_GLES2(glGenFramebuffers);
+ LOAD_GLES2(glBindFramebuffer);
+ LOAD_GLES2(glFramebufferRenderbuffer);
+ LOAD_GLES2(glFramebufferTexture2D);
+ LOAD_GLES2(glCheckFramebufferStatus);
+
+ frameBuffer->Width = width;
+ frameBuffer->Height = height;
+ frameBuffer->Multisamples = multisamples;
+
+ frameBuffer->ColorTextureSwapChain = vrapi_CreateTextureSwapChain3( VRAPI_TEXTURE_TYPE_2D, colorFormat, frameBuffer->Width, frameBuffer->Height, 1, 3 );
+ frameBuffer->TextureSwapChainLength = vrapi_GetTextureSwapChainLength( frameBuffer->ColorTextureSwapChain );
+ frameBuffer->DepthBuffers = (GLuint *)malloc( frameBuffer->TextureSwapChainLength * sizeof( GLuint ) );
+ frameBuffer->FrameBuffers = (GLuint *)malloc( frameBuffer->TextureSwapChainLength * sizeof( GLuint ) );
+
+ for ( int i = 0; i < frameBuffer->TextureSwapChainLength; i++ )
+ {
+ // Create the color buffer texture.
+ const GLuint colorTexture = vrapi_GetTextureSwapChainHandle( frameBuffer->ColorTextureSwapChain, i );
+ GLenum colorTextureTarget = GL_TEXTURE_2D;
+ GL( gles_glBindTexture( colorTextureTarget, colorTexture ) );
+ // Just clamp to edge. However, this requires manually clearing the border
+ // around the layer to clear the edge texels.
+ GL( gles_glTexParameteri( colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ) );
+ GL( gles_glTexParameteri( colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ) );
+
+ GL( gles_glTexParameteri( colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR ) );
+ GL( gles_glTexParameteri( colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) );
+ GL( gles_glBindTexture( colorTextureTarget, 0 ) );
+
+ {
+ {
+ // Create depth buffer.
+ GL( gles_glGenRenderbuffers( 1, &frameBuffer->DepthBuffers[i] ) );
+ GL( gles_glBindRenderbuffer( GL_RENDERBUFFER, frameBuffer->DepthBuffers[i] ) );
+ GL( gles_glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, frameBuffer->Width, frameBuffer->Height ) );
+ GL( gles_glBindRenderbuffer( GL_RENDERBUFFER, 0 ) );
+
+ // Create the frame buffer.
+ GL( gles_glGenFramebuffers( 1, &frameBuffer->FrameBuffers[i] ) );
+ GL( gles_glBindFramebuffer( GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i] ) );
+ GL( gles_glFramebufferRenderbuffer( GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i] ) );
+ GL( gles_glFramebufferTexture2D( GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0 ) );
+ GL( GLenum renderFramebufferStatus = gles_glCheckFramebufferStatus( GL_DRAW_FRAMEBUFFER ) );
+ GL( gles_glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 ) );
+ if ( renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE )
+ {
+ ALOGE( "Incomplete frame buffer object: %s", GlFrameBufferStatusString( renderFramebufferStatus ) );
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+void ovrFramebuffer_Destroy( ovrFramebuffer * frameBuffer )
+{
+ LOAD_GLES2(glDeleteFramebuffers);
+ LOAD_GLES2(glDeleteRenderbuffers);
+
+ GL( gles_glDeleteFramebuffers( frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers ) );
+ GL( gles_glDeleteRenderbuffers( frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers ) );
+
+ vrapi_DestroyTextureSwapChain( frameBuffer->ColorTextureSwapChain );
+
+ free( frameBuffer->DepthBuffers );
+ free( frameBuffer->FrameBuffers );
+
+ ovrFramebuffer_Clear( frameBuffer );
+}
+
+void ovrFramebuffer_SetCurrent( ovrFramebuffer * frameBuffer )
+{
+ LOAD_GLES2(glBindFramebuffer);
+ GL( gles_glBindFramebuffer( GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex] ) );
+}
+
+void ovrFramebuffer_SetNone()
+{
+ LOAD_GLES2(glBindFramebuffer);
+ GL( gles_glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 ) );
+}
+
+void ovrFramebuffer_Resolve( ovrFramebuffer * frameBuffer )
+{
+ // Discard the depth buffer, so the tiler won't need to write it back out to memory.
+ const GLenum depthAttachment[1] = { GL_DEPTH_ATTACHMENT };
+ glInvalidateFramebuffer( GL_DRAW_FRAMEBUFFER, 1, depthAttachment );
+
+ // Flush this frame worth of commands.
+ glFlush();
+}
+
+void ovrFramebuffer_Advance( ovrFramebuffer * frameBuffer )
+{
+ // Advance to the next texture from the set.
+ frameBuffer->TextureSwapChainIndex = ( frameBuffer->TextureSwapChainIndex + 1 ) % frameBuffer->TextureSwapChainLength;
+}
+
+
+void ovrFramebuffer_ClearEdgeTexels( ovrFramebuffer * frameBuffer )
+{
+ LOAD_GLES2(glEnable);
+ LOAD_GLES2(glDisable);
+ LOAD_GLES2(glViewport);
+ LOAD_GLES2(glScissor);
+ LOAD_GLES2(glClearColor);
+ LOAD_GLES2(glClear);
+
+ GL( gles_glEnable( GL_SCISSOR_TEST ) );
+ GL( gles_glViewport( 0, 0, frameBuffer->Width, frameBuffer->Height ) );
+
+ // Explicitly clear the border texels to black because OpenGL-ES does not support GL_CLAMP_TO_BORDER.
+ // Clear to fully opaque black.
+ GL( gles_glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ) );
+
+ //Glide comfort mask in and out
+ static float currentVLevel = 0.0f;
+ if (player_moving)
+ {
+ if (currentVLevel < vr_comfort_mask->value)
+ currentVLevel += vr_comfort_mask->value * 0.05;
+ } else{
+ if (currentVLevel > 0.0f)
+ currentVLevel -= vr_comfort_mask->value * 0.05;
+ }
+
+
+ bool useMask = (currentVLevel > 0.0f && currentVLevel <= 1.0f);
+
+ float width = useMask ? (frameBuffer->Width / 2.0f) * currentVLevel : 1;
+ float height = useMask ? (frameBuffer->Height / 2.0f) * currentVLevel : 1;
+
+ // bottom
+ GL( gles_glScissor( 0, 0, frameBuffer->Width, width ) );
+ GL( gles_glClear( GL_COLOR_BUFFER_BIT ) );
+ // top
+ GL( gles_glScissor( 0, frameBuffer->Height - height, frameBuffer->Width, height ) );
+ GL( gles_glClear( GL_COLOR_BUFFER_BIT ) );
+ // left
+ GL( gles_glScissor( 0, 0, width, frameBuffer->Height ) );
+ GL( gles_glClear( GL_COLOR_BUFFER_BIT ) );
+ // right
+ GL( gles_glScissor( frameBuffer->Width - width, 0, width, frameBuffer->Height ) );
+ GL( gles_glClear( GL_COLOR_BUFFER_BIT ) );
+
+
+ GL( gles_glScissor( 0, 0, 0, 0 ) );
+ GL( gles_glDisable( GL_SCISSOR_TEST ) );
+}
+
+
+/*
+================================================================================
+
+ovrRenderer
+
+================================================================================
+*/
+
+
+void ovrRenderer_Clear( ovrRenderer * renderer )
+{
+ for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )
+ {
+ ovrFramebuffer_Clear( &renderer->FrameBuffer[eye] );
+ }
+ renderer->ProjectionMatrix = ovrMatrix4f_CreateIdentity();
+ renderer->NumBuffers = VRAPI_FRAME_LAYER_EYE_MAX;
+}
+
+
+void ovrRenderer_Create( int width, int height, ovrRenderer * renderer, const ovrJava * java )
+{
+ renderer->NumBuffers = VRAPI_FRAME_LAYER_EYE_MAX;
+
+ //Now using a symmetrical render target, based on the horizontal FOV
+ vrFOV = vrapi_GetSystemPropertyInt( java, VRAPI_SYS_PROP_SUGGESTED_EYE_FOV_DEGREES_X);
+
+ // Create the render Textures.
+ for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )
+ {
+ ovrFramebuffer_Create( &renderer->FrameBuffer[eye],
+ GL_RGBA8,
+ width,
+ height,
+ NUM_MULTI_SAMPLES );
+ }
+
+ // Setup the projection matrix.
+ renderer->ProjectionMatrix = ovrMatrix4f_CreateProjectionFov(
+ vrFOV, vrFOV, 0.0f, 0.0f, 1.0f, 0.0f );
+
+}
+
+void ovrRenderer_Destroy( ovrRenderer * renderer )
+{
+ for ( int eye = 0; eye < renderer->NumBuffers; eye++ )
+ {
+ ovrFramebuffer_Destroy( &renderer->FrameBuffer[eye] );
+ }
+ renderer->ProjectionMatrix = ovrMatrix4f_CreateIdentity();
+}
+
+
+#ifndef EPSILON
+#define EPSILON 0.001f
+#endif
+
+static ovrVector3f normalizeVec(ovrVector3f vec) {
+ //NOTE: leave w-component untouched
+ //@@const float EPSILON = 0.000001f;
+ float xxyyzz = vec.x*vec.x + vec.y*vec.y + vec.z*vec.z;
+ //@@if(xxyyzz < EPSILON)
+ //@@ return *this; // do nothing if it is zero vector
+
+ //float invLength = invSqrt(xxyyzz);
+ ovrVector3f result;
+ float invLength = 1.0f / sqrtf(xxyyzz);
+ result.x = vec.x * invLength;
+ result.y = vec.y * invLength;
+ result.z = vec.z * invLength;
+ return result;
+}
+
+void NormalizeAngles(vec3_t angles)
+{
+ while (angles[0] >= 90) angles[0] -= 180;
+ while (angles[1] >= 180) angles[1] -= 360;
+ while (angles[2] >= 180) angles[2] -= 360;
+ while (angles[0] < -90) angles[0] += 180;
+ while (angles[1] < -180) angles[1] += 360;
+ while (angles[2] < -180) angles[2] += 360;
+}
+
+void GetAnglesFromVectors(const ovrVector3f forward, const ovrVector3f right, const ovrVector3f up, vec3_t angles)
+{
+ float sr, sp, sy, cr, cp, cy;
+
+ sp = -forward.z;
+
+ float cp_x_cy = forward.x;
+ float cp_x_sy = forward.y;
+ float cp_x_sr = -right.z;
+ float cp_x_cr = up.z;
+
+ float yaw = atan2(cp_x_sy, cp_x_cy);
+ float roll = atan2(cp_x_sr, cp_x_cr);
+
+ cy = cos(yaw);
+ sy = sin(yaw);
+ cr = cos(roll);
+ sr = sin(roll);
+
+ if (fabs(cy) > EPSILON)
+ {
+ cp = cp_x_cy / cy;
+ }
+ else if (fabs(sy) > EPSILON)
+ {
+ cp = cp_x_sy / sy;
+ }
+ else if (fabs(sr) > EPSILON)
+ {
+ cp = cp_x_sr / sr;
+ }
+ else if (fabs(cr) > EPSILON)
+ {
+ cp = cp_x_cr / cr;
+ }
+ else
+ {
+ cp = cos(asin(sp));
+ }
+
+ float pitch = atan2(sp, cp);
+
+ angles[0] = pitch / (M_PI*2.f / 360.f);
+ angles[1] = yaw / (M_PI*2.f / 360.f);
+ angles[2] = roll / (M_PI*2.f / 360.f);
+
+ NormalizeAngles(angles);
+}
+
+void QuatToYawPitchRoll(ovrQuatf q, float pitchAdjust, vec3_t out) {
+
+ ovrMatrix4f mat = ovrMatrix4f_CreateFromQuaternion( &q );
+
+ if (pitchAdjust != 0.0f)
+ {
+ ovrMatrix4f rot = ovrMatrix4f_CreateRotation(radians(pitchAdjust), 0.0f, 0.0f);
+ mat = ovrMatrix4f_Multiply(&mat, &rot);
+ }
+
+ ovrVector4f v1 = {0, 0, -1, 0};
+ ovrVector4f v2 = {1, 0, 0, 0};
+ ovrVector4f v3 = {0, 1, 0, 0};
+
+ ovrVector4f forwardInVRSpace = ovrVector4f_MultiplyMatrix4f(&mat, &v1);
+ ovrVector4f rightInVRSpace = ovrVector4f_MultiplyMatrix4f(&mat, &v2);
+ ovrVector4f upInVRSpace = ovrVector4f_MultiplyMatrix4f(&mat, &v3);
+
+ ovrVector3f forward = {-forwardInVRSpace.z, -forwardInVRSpace.x, forwardInVRSpace.y};
+ ovrVector3f right = {-rightInVRSpace.z, -rightInVRSpace.x, rightInVRSpace.y};
+ ovrVector3f up = {-upInVRSpace.z, -upInVRSpace.x, upInVRSpace.y};
+
+ ovrVector3f forwardNormal = normalizeVec(forward);
+ ovrVector3f rightNormal = normalizeVec(right);
+ ovrVector3f upNormal = normalizeVec(up);
+
+ GetAnglesFromVectors(forwardNormal, rightNormal, upNormal, out);
+ return;
+}
+
+void setWorldPosition( float x, float y, float z )
+{
+ positionDeltaThisFrame[0] = (worldPosition[0] - x);
+ positionDeltaThisFrame[1] = (worldPosition[1] - y);
+ positionDeltaThisFrame[2] = (worldPosition[2] - z);
+
+ worldPosition[0] = x;
+ worldPosition[1] = y;
+ worldPosition[2] = z;
+}
+
+void setHMDPosition( float x, float y, float z, float yaw )
+{
+ static bool s_useScreen = false;
+
+ VectorSet(hmdPosition, x, y, z);
+
+ if (s_useScreen != useScreenLayer())
+ {
+ s_useScreen = useScreenLayer();
+
+ //Record player height on transition
+ playerHeight = y;
+ }
+
+ if (!useScreenLayer())
+ {
+ playerYaw = yaw;
+ }
+}
+
+qboolean isMultiplayer()
+{
+ return Cvar_VariableValue("maxclients") > 1;
+}
+
+
+/*
+========================
+Android_Vibrate
+========================
+*/
+
+//0 = left, 1 = right
+float vibration_channel_duration[2] = {0.0f, 0.0f};
+float vibration_channel_intensity[2] = {0.0f, 0.0f};
+
+void Android_Vibrate( float duration, int channel, float intensity )
+{
+ if (vibration_channel_duration[channel] > 0.0f)
+ return;
+
+ if (vibration_channel_duration[channel] == -1.0f && duration != 0.0f)
+ return;
+
+ vibration_channel_duration[channel] = duration;
+ vibration_channel_intensity[channel] = intensity;
+}
+
+void getVROrigins(vec3_t _weaponoffset, vec3_t _weaponangles, vec3_t _hmdPosition)
+{
+ VectorCopy(weaponoffset, _weaponoffset);
+ VectorCopy(weaponangles, _weaponangles);
+ VectorCopy(hmdPosition, _hmdPosition);
+}
+
+
+void Qcommon_BeginFrame (int time);
+void Qcommon_Frame (int eye);
+void Qcommon_EndFrame (int time);
+
+void VR_GetMove( float *forward, float *side, float *up, float *yaw, float *pitch, float *roll )
+{
+ *forward = remote_movementForward + positional_movementForward;
+ *up = remote_movementUp;
+ *side = remote_movementSideways + positional_movementSideways;
+ *yaw = hmdorientation[YAW] + snapTurn;
+ *pitch = hmdorientation[PITCH];
+ *roll = hmdorientation[ROLL];
+}
+
+//TODO
+static inline bool isHostAlive()
+{
+ return true; //(host.state != HOST_SHUTDOWN &&
+ //host.state != HOST_CRASHED);
+}
+
+static int oldtime = 0;
+
+void RenderFrame( ovrRenderer * renderer, const ovrJava * java,
+ const ovrTracking2 * tracking, ovrMobile * ovr )
+{
+ //if we are now shutting down, drop out here
+ //TODO
+ if (true)//isHostAlive()) {
+ {
+ int time;
+
+ // find time spent rendering last frame
+ do {
+ global_time = Sys_Milliseconds ();
+ time = global_time - oldtime;
+ } while (time < 1);
+
+ Qcommon_BeginFrame (time * 1000);
+
+ // Render the eye images.
+ for (int eye = 0; eye < renderer->NumBuffers && isHostAlive(); eye++) {
+ ovrFramebuffer *frameBuffer = &(renderer->FrameBuffer[eye]);
+ ovrFramebuffer_SetCurrent(frameBuffer);
+
+ {
+ GL(glEnable(GL_SCISSOR_TEST));
+ GL(glDepthMask(GL_TRUE));
+ GL(glEnable(GL_DEPTH_TEST));
+ GL(glDepthFunc(GL_LEQUAL));
+
+ //Weusing the size of the render target
+ GL(glViewport(0, 0, frameBuffer->Width, frameBuffer->Height));
+ GL(glScissor(0, 0, frameBuffer->Width, frameBuffer->Height));
+
+ GL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
+ GL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
+ GL(glDisable(GL_SCISSOR_TEST));
+
+ //Now do the drawing for this eye (or draw for left eye twice if using screen layer)
+ Qcommon_Frame(useScreenLayer() ? 0 : eye);
+ }
+
+ //Clear edge to prevent smearing
+ ovrFramebuffer_ClearEdgeTexels(frameBuffer);
+ ovrFramebuffer_Resolve(frameBuffer);
+ ovrFramebuffer_Advance(frameBuffer);
+ }
+
+ Qcommon_EndFrame(time * 1000);
+
+ oldtime = global_time;
+ }
+
+ ovrFramebuffer_SetNone();
+}
+
+
+/*
+================================================================================
+
+ovrRenderThread
+
+================================================================================
+*/
+
+
+/*
+================================================================================
+
+ovrApp
+
+================================================================================
+*/
+
+typedef struct
+{
+ ovrJava Java;
+ ovrEgl Egl;
+ ANativeWindow * NativeWindow;
+ bool Resumed;
+ ovrMobile * Ovr;
+ ovrScene Scene;
+ long long FrameIndex;
+ double DisplayTime;
+ int SwapInterval;
+ int CpuLevel;
+ int GpuLevel;
+ int MainThreadTid;
+ int RenderThreadTid;
+ ovrLayer_Union2 Layers[ovrMaxLayerCount];
+ int LayerCount;
+ ovrRenderer Renderer;
+} ovrApp;
+
+static void ovrApp_Clear( ovrApp * app )
+{
+ app->Java.Vm = NULL;
+ app->Java.Env = NULL;
+ app->Java.ActivityObject = NULL;
+ app->Ovr = NULL;
+ app->FrameIndex = 1;
+ app->DisplayTime = 0;
+ app->SwapInterval = 1;
+ app->CpuLevel = 2;
+ app->GpuLevel = 2;
+ app->MainThreadTid = 0;
+ app->RenderThreadTid = 0;
+
+ ovrEgl_Clear( &app->Egl );
+
+ ovrScene_Clear( &app->Scene );
+ ovrRenderer_Clear( &app->Renderer );
+}
+
+static void ovrApp_PushBlackFinal( ovrApp * app )
+{
+ int frameFlags = 0;
+ frameFlags |= VRAPI_FRAME_FLAG_FLUSH | VRAPI_FRAME_FLAG_FINAL;
+
+ ovrLayerProjection2 layer = vrapi_DefaultLayerBlackProjection2();
+ layer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_INHIBIT_SRGB_FRAMEBUFFER;
+
+ const ovrLayerHeader2 * layers[] =
+ {
+ &layer.Header
+ };
+
+ ovrSubmitFrameDescription2 frameDesc = {};
+ frameDesc.Flags = frameFlags;
+ frameDesc.SwapInterval = 1;
+ frameDesc.FrameIndex = app->FrameIndex;
+ frameDesc.DisplayTime = app->DisplayTime;
+ frameDesc.LayerCount = 1;
+ frameDesc.Layers = layers;
+
+ vrapi_SubmitFrame2( app->Ovr, &frameDesc );
+}
+
+static void ovrApp_HandleVrModeChanges( ovrApp * app )
+{
+ if ( app->Resumed != false && app->NativeWindow != NULL )
+ {
+ if ( app->Ovr == NULL )
+ {
+ ovrModeParms parms = vrapi_DefaultModeParms( &app->Java );
+ // Must reset the FLAG_FULLSCREEN window flag when using a SurfaceView
+ parms.Flags |= VRAPI_MODE_FLAG_RESET_WINDOW_FULLSCREEN;
+
+ parms.Flags |= VRAPI_MODE_FLAG_NATIVE_WINDOW;
+ parms.Display = (size_t)app->Egl.Display;
+ parms.WindowSurface = (size_t)app->NativeWindow;
+ parms.ShareContext = (size_t)app->Egl.Context;
+
+ ALOGV( " eglGetCurrentSurface( EGL_DRAW ) = %p", eglGetCurrentSurface( EGL_DRAW ) );
+
+ ALOGV( " vrapi_EnterVrMode()" );
+
+ app->Ovr = vrapi_EnterVrMode( &parms );
+
+ ALOGV( " eglGetCurrentSurface( EGL_DRAW ) = %p", eglGetCurrentSurface( EGL_DRAW ) );
+
+ // If entering VR mode failed then the ANativeWindow was not valid.
+ if ( app->Ovr == NULL )
+ {
+ ALOGE( "Invalid ANativeWindow!" );
+ app->NativeWindow = NULL;
+ }
+
+ // Set performance parameters once we have entered VR mode and have a valid ovrMobile.
+ if ( app->Ovr != NULL )
+ {
+ vrapi_SetClockLevels( app->Ovr, app->CpuLevel, app->GpuLevel );
+
+ ALOGV( " vrapi_SetClockLevels( %d, %d )", app->CpuLevel, app->GpuLevel );
+
+ vrapi_SetPerfThread( app->Ovr, VRAPI_PERF_THREAD_TYPE_MAIN, app->MainThreadTid );
+
+ ALOGV( " vrapi_SetPerfThread( MAIN, %d )", app->MainThreadTid );
+
+ vrapi_SetPerfThread( app->Ovr, VRAPI_PERF_THREAD_TYPE_RENDERER, app->RenderThreadTid );
+
+ ALOGV( " vrapi_SetPerfThread( RENDERER, %d )", app->RenderThreadTid );
+ }
+ }
+ }
+ else
+ {
+ if ( app->Ovr != NULL )
+ {
+ ALOGV( " eglGetCurrentSurface( EGL_DRAW ) = %p", eglGetCurrentSurface( EGL_DRAW ) );
+
+ ALOGV( " vrapi_LeaveVrMode()" );
+
+ vrapi_LeaveVrMode( app->Ovr );
+ app->Ovr = NULL;
+
+ ALOGV( " eglGetCurrentSurface( EGL_DRAW ) = %p", eglGetCurrentSurface( EGL_DRAW ) );
+ }
+ }
+}
+
+
+/*
+================================================================================
+
+ovrMessageQueue
+
+================================================================================
+*/
+
+typedef enum
+{
+ MQ_WAIT_NONE, // don't wait
+ MQ_WAIT_RECEIVED, // wait until the consumer thread has received the message
+ MQ_WAIT_PROCESSED // wait until the consumer thread has processed the message
+} ovrMQWait;
+
+#define MAX_MESSAGE_PARMS 8
+#define MAX_MESSAGES 1024
+
+typedef struct
+{
+ int Id;
+ ovrMQWait Wait;
+ long long Parms[MAX_MESSAGE_PARMS];
+} ovrMessage;
+
+static void ovrMessage_Init( ovrMessage * message, const int id, const int wait )
+{
+ message->Id = id;
+ message->Wait = wait;
+ memset( message->Parms, 0, sizeof( message->Parms ) );
+}
+
+static void ovrMessage_SetPointerParm( ovrMessage * message, int index, void * ptr ) { *(void **)&message->Parms[index] = ptr; }
+static void * ovrMessage_GetPointerParm( ovrMessage * message, int index ) { return *(void **)&message->Parms[index]; }
+static void ovrMessage_SetIntegerParm( ovrMessage * message, int index, int value ) { message->Parms[index] = value; }
+static int ovrMessage_GetIntegerParm( ovrMessage * message, int index ) { return (int)message->Parms[index]; }
+static void ovrMessage_SetFloatParm( ovrMessage * message, int index, float value ) { *(float *)&message->Parms[index] = value; }
+static float ovrMessage_GetFloatParm( ovrMessage * message, int index ) { return *(float *)&message->Parms[index]; }
+
+// Cyclic queue with messages.
+typedef struct
+{
+ ovrMessage Messages[MAX_MESSAGES];
+ volatile int Head; // dequeue at the head
+ volatile int Tail; // enqueue at the tail
+ ovrMQWait Wait;
+ volatile bool EnabledFlag;
+ volatile bool PostedFlag;
+ volatile bool ReceivedFlag;
+ volatile bool ProcessedFlag;
+ pthread_mutex_t Mutex;
+ pthread_cond_t PostedCondition;
+ pthread_cond_t ReceivedCondition;
+ pthread_cond_t ProcessedCondition;
+} ovrMessageQueue;
+
+static void ovrMessageQueue_Create( ovrMessageQueue * messageQueue )
+{
+ messageQueue->Head = 0;
+ messageQueue->Tail = 0;
+ messageQueue->Wait = MQ_WAIT_NONE;
+ messageQueue->EnabledFlag = false;
+ messageQueue->PostedFlag = false;
+ messageQueue->ReceivedFlag = false;
+ messageQueue->ProcessedFlag = false;
+
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init( &attr );
+ pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
+ pthread_mutex_init( &messageQueue->Mutex, &attr );
+ pthread_mutexattr_destroy( &attr );
+ pthread_cond_init( &messageQueue->PostedCondition, NULL );
+ pthread_cond_init( &messageQueue->ReceivedCondition, NULL );
+ pthread_cond_init( &messageQueue->ProcessedCondition, NULL );
+}
+
+static void ovrMessageQueue_Destroy( ovrMessageQueue * messageQueue )
+{
+ pthread_mutex_destroy( &messageQueue->Mutex );
+ pthread_cond_destroy( &messageQueue->PostedCondition );
+ pthread_cond_destroy( &messageQueue->ReceivedCondition );
+ pthread_cond_destroy( &messageQueue->ProcessedCondition );
+}
+
+static void ovrMessageQueue_Enable( ovrMessageQueue * messageQueue, const bool set )
+{
+ messageQueue->EnabledFlag = set;
+}
+
+static void ovrMessageQueue_PostMessage( ovrMessageQueue * messageQueue, const ovrMessage * message )
+{
+ if ( !messageQueue->EnabledFlag )
+ {
+ return;
+ }
+ while ( messageQueue->Tail - messageQueue->Head >= MAX_MESSAGES )
+ {
+ usleep( 1000 );
+ }
+ pthread_mutex_lock( &messageQueue->Mutex );
+ messageQueue->Messages[messageQueue->Tail & ( MAX_MESSAGES - 1 )] = *message;
+ messageQueue->Tail++;
+ messageQueue->PostedFlag = true;
+ pthread_cond_broadcast( &messageQueue->PostedCondition );
+ if ( message->Wait == MQ_WAIT_RECEIVED )
+ {
+ while ( !messageQueue->ReceivedFlag )
+ {
+ pthread_cond_wait( &messageQueue->ReceivedCondition, &messageQueue->Mutex );
+ }
+ messageQueue->ReceivedFlag = false;
+ }
+ else if ( message->Wait == MQ_WAIT_PROCESSED )
+ {
+ while ( !messageQueue->ProcessedFlag )
+ {
+ pthread_cond_wait( &messageQueue->ProcessedCondition, &messageQueue->Mutex );
+ }
+ messageQueue->ProcessedFlag = false;
+ }
+ pthread_mutex_unlock( &messageQueue->Mutex );
+}
+
+static void ovrMessageQueue_SleepUntilMessage( ovrMessageQueue * messageQueue )
+{
+ if ( messageQueue->Wait == MQ_WAIT_PROCESSED )
+ {
+ messageQueue->ProcessedFlag = true;
+ pthread_cond_broadcast( &messageQueue->ProcessedCondition );
+ messageQueue->Wait = MQ_WAIT_NONE;
+ }
+ pthread_mutex_lock( &messageQueue->Mutex );
+ if ( messageQueue->Tail > messageQueue->Head )
+ {
+ pthread_mutex_unlock( &messageQueue->Mutex );
+ return;
+ }
+ while ( !messageQueue->PostedFlag )
+ {
+ pthread_cond_wait( &messageQueue->PostedCondition, &messageQueue->Mutex );
+ }
+ messageQueue->PostedFlag = false;
+ pthread_mutex_unlock( &messageQueue->Mutex );
+}
+
+static bool ovrMessageQueue_GetNextMessage( ovrMessageQueue * messageQueue, ovrMessage * message, bool waitForMessages )
+{
+ if ( messageQueue->Wait == MQ_WAIT_PROCESSED )
+ {
+ messageQueue->ProcessedFlag = true;
+ pthread_cond_broadcast( &messageQueue->ProcessedCondition );
+ messageQueue->Wait = MQ_WAIT_NONE;
+ }
+ if ( waitForMessages )
+ {
+ ovrMessageQueue_SleepUntilMessage( messageQueue );
+ }
+ pthread_mutex_lock( &messageQueue->Mutex );
+ if ( messageQueue->Tail <= messageQueue->Head )
+ {
+ pthread_mutex_unlock( &messageQueue->Mutex );
+ return false;
+ }
+ *message = messageQueue->Messages[messageQueue->Head & ( MAX_MESSAGES - 1 )];
+ messageQueue->Head++;
+ pthread_mutex_unlock( &messageQueue->Mutex );
+ if ( message->Wait == MQ_WAIT_RECEIVED )
+ {
+ messageQueue->ReceivedFlag = true;
+ pthread_cond_broadcast( &messageQueue->ReceivedCondition );
+ }
+ else if ( message->Wait == MQ_WAIT_PROCESSED )
+ {
+ messageQueue->Wait = MQ_WAIT_PROCESSED;
+ }
+ return true;
+}
+
+/*
+================================================================================
+
+ovrAppThread
+
+================================================================================
+*/
+
+enum
+{
+ MESSAGE_ON_CREATE,
+ MESSAGE_ON_START,
+ MESSAGE_ON_RESUME,
+ MESSAGE_ON_PAUSE,
+ MESSAGE_ON_STOP,
+ MESSAGE_ON_DESTROY,
+ MESSAGE_ON_SURFACE_CREATED,
+ MESSAGE_ON_SURFACE_DESTROYED
+};
+
+typedef struct
+{
+ JavaVM * JavaVm;
+ jobject ActivityObject;
+ jclass ActivityClass;
+ pthread_t Thread;
+ ovrMessageQueue MessageQueue;
+ ANativeWindow * NativeWindow;
+} ovrAppThread;
+
+long shutdownCountdown;
+
+int m_width;
+int m_height;
+
+qboolean R_SetMode( void );
+
+void Android_GetScreenRes(int *width, int *height)
+{
+ if (useScreenLayer())
+ {
+ *width = cylinderSize[0];
+ *height = cylinderSize[1];
+ } else {
+ *width = m_width;
+ *height = m_height;
+ }
+}
+
+void Android_MessageBox(const char *title, const char *text)
+{
+ ALOGE("%s %s", title, text);
+}
+
+void initialize_gl4es();
+
+void VR_Init()
+{
+ //Initialise all our variables
+ playerYaw = 0.0f;
+ showingScreenLayer = true;
+ remote_movementSideways = 0.0f;
+ remote_movementForward = 0.0f;
+ remote_movementUp = 0.0f;
+ positional_movementSideways = 0.0f;
+ positional_movementForward = 0.0f;
+ snapTurn = 0.0f;
+ ducked = DUCK_NOTDUCKED;
+
+ //init randomiser
+ srand(time(NULL));
+
+ //Create Cvars
+ vr_snapturn_angle = Cvar_Get( "vr_snapturn_angle", "45", CVAR_ARCHIVE);
+ vr_positional_factor = Cvar_Get( "vr_positional_factor", "2000", CVAR_ARCHIVE);
+ vr_walkdirection = Cvar_Get( "vr_walkdirection", "0", CVAR_ARCHIVE);
+ vr_weapon_pitchadjust = Cvar_Get( "vr_weapon_pitchadjust", "-20.0", CVAR_ARCHIVE);
+ vr_control_scheme = Cvar_Get( "vr_control_scheme", "0", CVAR_ARCHIVE);
+ vr_height_adjust = Cvar_Get( "vr_height_adjust", "0.0", CVAR_ARCHIVE);
+ vr_weaponscale = Cvar_Get( "vr_weaponscale", "0.56", CVAR_ARCHIVE);
+ vr_weapon_stabilised = Cvar_Get( "vr_weapon_stabilised", "0.0", CVAR_LATCH);
+ vr_lasersight = Cvar_Get( "vr_lasersight", "0", CVAR_LATCH);
+ vr_comfort_mask = Cvar_Get( "vr_comfort_mask", "0.0", CVAR_ARCHIVE);
+
+ //The Engine (which is a derivative of Quake) uses a very specific unit size:
+ //Wolfenstein 3D, DOOM and QUAKE use the same coordinate/unit system:
+ //8 foot (96 inch) height wall == 64 units, 1.5 inches per pixel unit
+ //1.0 pixel unit / 1.5 inch == 0.666666 pixel units per inch
+ //This make a world scale of: 26.2467
+ vr_worldscale = Cvar_Get( "vr_worldscale", "26.2467", CVAR_ARCHIVE);
+}
+
+/* Called before SDL_main() to initialize JNI bindings in SDL library */
+extern void SDL_Android_Init(JNIEnv* env, jclass cls);
+void FS_AddDirToSearchPath(char *dir, qboolean create);
+
+void * AppThreadFunction( void * parm )
+{
+ ovrAppThread * appThread = (ovrAppThread *)parm;
+
+ ovrJava java;
+ java.Vm = appThread->JavaVm;
+ (*java.Vm)->AttachCurrentThread( java.Vm, &java.Env, NULL );
+ java.ActivityObject = appThread->ActivityObject;
+
+ jclass cls = (*java.Env)->GetObjectClass(java.Env, java.ActivityObject);
+
+ /* This interface could expand with ABI negotiation, callbacks, etc. */
+ SDL_Android_Init(java.Env, cls);
+
+ SDL_SetMainReady();
+
+ // Note that AttachCurrentThread will reset the thread name.
+ prctl( PR_SET_NAME, (long)"OVR::Main", 0, 0, 0 );
+
+ quake2_initialised = false;
+
+ const ovrInitParms initParms = vrapi_DefaultInitParms( &java );
+ int32_t initResult = vrapi_Initialize( &initParms );
+ if ( initResult != VRAPI_INITIALIZE_SUCCESS )
+ {
+ // If intialization failed, vrapi_* function calls will not be available.
+ exit( 0 );
+ }
+
+ ovrApp appState;
+ ovrApp_Clear( &appState );
+ appState.Java = java;
+
+ // This app will handle android gamepad events itself.
+ vrapi_SetPropertyInt( &appState.Java, VRAPI_EAT_NATIVE_GAMEPAD_EVENTS, 0 );
+
+ //Using a symmetrical render target
+ m_width=vrapi_GetSystemPropertyInt( &java, VRAPI_SYS_PROP_SUGGESTED_EYE_TEXTURE_WIDTH ) * SS_MULTIPLIER;
+ m_height=m_width;
+
+
+ ovrEgl_CreateContext( &appState.Egl, NULL );
+
+ EglInitExtensions();
+
+ appState.CpuLevel = CPU_LEVEL;
+ appState.GpuLevel = GPU_LEVEL;
+ appState.MainThreadTid = gettid();
+
+ ovrRenderer_Create( m_width, m_height, &appState.Renderer, &java );
+
+ chdir("/sdcard");
+
+ for ( bool destroyed = false; destroyed == false; )
+ {
+ for ( ; ; )
+ {
+ ovrMessage message;
+ const bool waitForMessages = ( appState.Ovr == NULL && destroyed == false );
+ if ( !ovrMessageQueue_GetNextMessage( &appThread->MessageQueue, &message, waitForMessages ) )
+ {
+ break;
+ }
+
+ switch ( message.Id )
+ {
+ case MESSAGE_ON_CREATE:
+ {
+ break;
+ }
+ case MESSAGE_ON_START:
+ {
+ if (!quake2_initialised)
+ {
+ ALOGV( " Initialising Quake2 Engine" );
+
+ if (argc != 0)
+ {
+ //TODO
+ Qcommon_Init(argc, (const char**)argv);
+ }
+ else
+ {
+ int argc = 1; char *argv[] = { "quake2" };
+
+ Qcommon_Init(argc, (const char**)argv);
+ }
+
+ FS_AddDirToSearchPath("/sdcard/Quake2Quest", true);
+
+ quake2_initialised = true;
+ }
+ break;
+ }
+ case MESSAGE_ON_RESUME:
+ {
+ //If we get here, then user has opted not to quit
+ //jni_resumeAudio();
+ appState.Resumed = true;
+ break;
+ }
+ case MESSAGE_ON_PAUSE:
+ {
+ //jni_pauseAudio();
+ appState.Resumed = false;
+ break;
+ }
+ case MESSAGE_ON_STOP:
+ {
+ break;
+ }
+ case MESSAGE_ON_DESTROY:
+ {
+ appState.NativeWindow = NULL;
+ destroyed = true;
+ break;
+ }
+ case MESSAGE_ON_SURFACE_CREATED: { appState.NativeWindow = (ANativeWindow *)ovrMessage_GetPointerParm( &message, 0 ); break; }
+ case MESSAGE_ON_SURFACE_DESTROYED: { appState.NativeWindow = NULL; break; }
+ }
+
+ ovrApp_HandleVrModeChanges( &appState );
+ }
+
+ if ( appState.Ovr == NULL )
+ {
+ continue;
+ }
+
+ //Use floor based tracking space
+ vrapi_SetTrackingSpace(appState.Ovr, VRAPI_TRACKING_SPACE_LOCAL_FLOOR);
+
+ // Create the scene if not yet created.
+ // The scene is created here to be able to show a loading icon.
+ if ( !ovrScene_IsCreated( &appState.Scene ) )
+ {
+ ovrScene_Create( cylinderSize[0], cylinderSize[1], &appState.Scene, &java );
+ }
+
+ // This is the only place the frame index is incremented, right before
+ // calling vrapi_GetPredictedDisplayTime().
+ appState.FrameIndex++;
+
+ // Create the scene if not yet created.
+ // The scene is created here to be able to show a loading icon.
+ if (!quake2_initialised || runStatus != -1)
+ {
+ // Show a loading icon.
+ int frameFlags = 0;
+ frameFlags |= VRAPI_FRAME_FLAG_FLUSH;
+
+ ovrLayerProjection2 blackLayer = vrapi_DefaultLayerBlackProjection2();
+ blackLayer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_INHIBIT_SRGB_FRAMEBUFFER;
+
+ ovrLayerLoadingIcon2 iconLayer = vrapi_DefaultLayerLoadingIcon2();
+ iconLayer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_INHIBIT_SRGB_FRAMEBUFFER;
+
+ const ovrLayerHeader2 * layers[] =
+ {
+ &blackLayer.Header,
+ &iconLayer.Header,
+ };
+
+ ovrSubmitFrameDescription2 frameDesc = {};
+ frameDesc.Flags = frameFlags;
+ frameDesc.SwapInterval = 1;
+ frameDesc.FrameIndex = appState.FrameIndex;
+ frameDesc.DisplayTime = appState.DisplayTime;
+ frameDesc.LayerCount = 2;
+ frameDesc.Layers = layers;
+
+ vrapi_SubmitFrame2( appState.Ovr, &frameDesc );
+ }
+
+ //Handle haptics
+ static float lastFrameTime = 0.0f;
+ float timestamp = (float)(GetTimeInMilliSeconds());
+ float frametime = timestamp - lastFrameTime;
+ lastFrameTime = timestamp;
+
+ for (int i = 0; i < 2; ++i) {
+ if (vibration_channel_duration[i] > 0.0f ||
+ vibration_channel_duration[i] == -1.0f) {
+ vrapi_SetHapticVibrationSimple(appState.Ovr, controllerIDs[i],
+ vibration_channel_intensity[i]);
+
+ if (vibration_channel_duration[i] != -1.0f) {
+ vibration_channel_duration[i] -= frametime;
+
+ if (vibration_channel_duration[i] < 0.0f) {
+ vibration_channel_duration[i] = 0.0f;
+ vibration_channel_intensity[i] = 0.0f;
+ }
+ }
+ } else {
+ vrapi_SetHapticVibrationSimple(appState.Ovr, controllerIDs[i], 0.0f);
+ }
+ }
+
+ if (runStatus == -1) {
+#ifndef NDEBUG
+ if (appState.FrameIndex > 10800)
+ {
+ //Trigger shutdown after a couple of minutes in debug mode
+ //runStatus = 0;
+ }
+#endif
+
+ // Get the HMD pose, predicted for the middle of the time period during which
+ // the new eye images will be displayed. The number of frames predicted ahead
+ // depends on the pipeline depth of the engine and the synthesis rate.
+ // The better the prediction, the less black will be pulled in at the edges.
+ const double predictedDisplayTime = vrapi_GetPredictedDisplayTime(appState.Ovr,
+ appState.FrameIndex);
+ const ovrTracking2 tracking = vrapi_GetPredictedTracking2(appState.Ovr,
+ predictedDisplayTime);
+
+ appState.DisplayTime = predictedDisplayTime;
+
+ //Get orientation
+ // We extract Yaw, Pitch, Roll instead of directly using the orientation
+ // to allow "additional" yaw manipulation with mouse/controller.
+ const ovrQuatf quatHmd = tracking.HeadPose.Pose.Orientation;
+ const ovrVector3f positionHmd = tracking.HeadPose.Pose.Position;
+ QuatToYawPitchRoll(quatHmd, 0.0f, hmdorientation);
+ setHMDPosition(positionHmd.x, positionHmd.y, positionHmd.z, hmdorientation[YAW]);
+
+ //TODO: fix - set to use HMD position for world position
+ setWorldPosition(positionHmd.x, positionHmd.y, positionHmd.z);
+
+ ALOGV(" HMD-Position: %f, %f, %f", positionHmd.x, positionHmd.y, positionHmd.z);
+
+
+
+
+ static bool s_paused = false;
+ if (s_paused != (cl_paused->value == 1))
+ {
+ s_paused = (cl_paused->value == 1);
+
+ //If we switch from paused to unpaused, go full screen
+ if (cl_paused->value != 1)
+ {
+ showingScreenLayer = false;
+ }
+ }
+
+
+
+ //Get info for tracked remotes
+ acquireTrackedRemotesData(appState.Ovr, appState.DisplayTime);
+
+ //Call additional control schemes here
+ switch ((int)vr_control_scheme->value)
+ {
+ case RIGHT_HANDED_DEFAULT:
+ HandleInput_Default(&rightTrackedRemoteState_new, &rightTrackedRemoteState_old, &rightRemoteTracking_new,
+ &leftTrackedRemoteState_new, &leftTrackedRemoteState_old, &leftRemoteTracking_new,
+ ovrButton_A, ovrButton_B, ovrButton_X, ovrButton_Y);
+ break;
+ case LEFT_HANDED_DEFAULT:
+ HandleInput_Default(&leftTrackedRemoteState_new, &leftTrackedRemoteState_old, &leftRemoteTracking_new,
+ &rightTrackedRemoteState_new, &rightTrackedRemoteState_old, &rightRemoteTracking_new,
+ ovrButton_X, ovrButton_Y, ovrButton_A, ovrButton_B);
+ break;
+ }
+
+ static bool usingScreenLayer = true; //Starts off using the screen layer
+ if (usingScreenLayer != useScreenLayer())
+ {
+ usingScreenLayer = useScreenLayer();
+ R_SetMode();
+ }
+
+ ovrSubmitFrameDescription2 frameDesc = { 0 };
+ if (!useScreenLayer()) {
+
+ ovrLayerProjection2 layer = vrapi_DefaultLayerProjection2();
+ layer.HeadPose = tracking.HeadPose;
+ for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )
+ {
+ ovrFramebuffer * frameBuffer = &appState.Renderer.FrameBuffer[appState.Renderer.NumBuffers == 1 ? 0 : eye];
+ layer.Textures[eye].ColorSwapChain = frameBuffer->ColorTextureSwapChain;
+ layer.Textures[eye].SwapChainIndex = frameBuffer->TextureSwapChainIndex;
+
+ ovrMatrix4f projectionMatrix;
+ projectionMatrix = ovrMatrix4f_CreateProjectionFov(vrFOV, vrFOV,
+ 0.0f, 0.0f, 0.1f, 0.0f);
+
+ layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection(&projectionMatrix);
+
+ layer.Textures[eye].TextureRect.x = 0;
+ layer.Textures[eye].TextureRect.y = 0;
+ layer.Textures[eye].TextureRect.width = 1.0f;
+ layer.Textures[eye].TextureRect.height = 1.0f;
+ }
+ layer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_CHROMATIC_ABERRATION_CORRECTION;
+
+ //Call the game drawing code to populate the cylinder layer texture
+ RenderFrame(&appState.Renderer,
+ &appState.Java,
+ &tracking,
+ appState.Ovr);
+
+ // Set up the description for this frame.
+ const ovrLayerHeader2 *layers[] =
+ {
+ &layer.Header
+ };
+
+ ovrSubmitFrameDescription2 frameDesc = {};
+ frameDesc.Flags = 0;
+ frameDesc.SwapInterval = appState.SwapInterval;
+ frameDesc.FrameIndex = appState.FrameIndex;
+ frameDesc.DisplayTime = appState.DisplayTime;
+ frameDesc.LayerCount = 1;
+ frameDesc.Layers = layers;
+
+ // Hand over the eye images to the time warp.
+ vrapi_SubmitFrame2(appState.Ovr, &frameDesc);
+
+ } else {
+ // Set-up the compositor layers for this frame.
+ // NOTE: Multiple independent layers are allowed, but they need to be added
+ // in a depth consistent order.
+ memset( appState.Layers, 0, sizeof( ovrLayer_Union2 ) * ovrMaxLayerCount );
+ appState.LayerCount = 0;
+
+ // Add a simple cylindrical layer
+ appState.Layers[appState.LayerCount++].Cylinder =
+ BuildCylinderLayer( &appState.Scene.CylinderRenderer,
+ appState.Scene.CylinderWidth, appState.Scene.CylinderHeight, &tracking, radians(playerYaw) );
+
+ //Call the game drawing code to populate the cylinder layer texture
+ RenderFrame(&appState.Scene.CylinderRenderer,
+ &appState.Java,
+ &tracking,
+ appState.Ovr);
+
+
+ // Compose the layers for this frame.
+ const ovrLayerHeader2 * layerHeaders[ovrMaxLayerCount] = { 0 };
+ for ( int i = 0; i < appState.LayerCount; i++ )
+ {
+ layerHeaders[i] = &appState.Layers[i].Header;
+ }
+
+ // Set up the description for this frame.
+ frameDesc.Flags = 0;
+ frameDesc.SwapInterval = appState.SwapInterval;
+ frameDesc.FrameIndex = appState.FrameIndex;
+ frameDesc.DisplayTime = appState.DisplayTime;
+ frameDesc.LayerCount = appState.LayerCount;
+ frameDesc.Layers = layerHeaders;
+
+ // Hand over the eye images to the time warp.
+ vrapi_SubmitFrame2(appState.Ovr, &frameDesc);
+ }
+ }
+ else
+ {
+ //We are now shutting down
+ if (runStatus == 0)
+ {
+ //Give us half a second (36 frames)
+ shutdownCountdown = 36;
+ runStatus++;
+ } else if (runStatus == 1)
+ {
+ if (--shutdownCountdown == 0) {
+ runStatus++;
+ }
+ } else if (runStatus == 2)
+ {
+ //TODO
+ //Host_Shutdown();
+ runStatus++;
+ } else if (runStatus == 3)
+ {
+ ovrRenderer_Destroy( &appState.Renderer );
+ ovrEgl_DestroyContext( &appState.Egl );
+ (*java.Vm)->DetachCurrentThread( java.Vm );
+ vrapi_Shutdown();
+ exit( 0 );
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void ovrAppThread_Create( ovrAppThread * appThread, JNIEnv * env, jobject activityObject, jclass activityClass )
+{
+ (*env)->GetJavaVM( env, &appThread->JavaVm );
+ appThread->ActivityObject = (*env)->NewGlobalRef( env, activityObject );
+ appThread->ActivityClass = (*env)->NewGlobalRef( env, activityClass );
+ appThread->Thread = 0;
+ appThread->NativeWindow = NULL;
+ ovrMessageQueue_Create( &appThread->MessageQueue );
+
+ const int createErr = pthread_create( &appThread->Thread, NULL, AppThreadFunction, appThread );
+ if ( createErr != 0 )
+ {
+ ALOGE( "pthread_create returned %i", createErr );
+ }
+}
+
+static void ovrAppThread_Destroy( ovrAppThread * appThread, JNIEnv * env )
+{
+ pthread_join( appThread->Thread, NULL );
+ (*env)->DeleteGlobalRef( env, appThread->ActivityObject );
+ (*env)->DeleteGlobalRef( env, appThread->ActivityClass );
+ ovrMessageQueue_Destroy( &appThread->MessageQueue );
+}
+
+/*
+================================================================================
+
+Activity lifecycle
+
+================================================================================
+*/
+
+JNIEXPORT jint JNICALL SDL_JNI_OnLoad(JavaVM* vm, void* reserved);
+
+int JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+ JNIEnv *env;
+ if((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
+ {
+ ALOGE("Failed JNI_OnLoad");
+ return -1;
+ }
+
+ return SDL_JNI_OnLoad(vm, reserved);
+}
+
+JNIEXPORT jlong JNICALL Java_com_drbeef_quake2quest_GLES3JNILib_onCreate( JNIEnv * env, jclass activityClass, jobject activity,
+ jstring commandLineParams)
+{
+ ALOGV( " GLES3JNILib::onCreate()" );
+
+ /* the global arg_xxx structs are initialised within the argtable */
+ void *argtable[] = {
+ ss = arg_dbl0("s", "supersampling", "", "super sampling value (e.g. 1.0)"),
+ cpu = arg_int0("c", "cpu", "", "CPU perf index 1-4 (default: 2)"),
+ gpu = arg_int0("g", "gpu", "", "GPU perf index 1-4 (default: 3)"),
+ end = arg_end(20)
+ };
+
+ jboolean iscopy;
+ const char *arg = (*env)->GetStringUTFChars(env, commandLineParams, &iscopy);
+
+ char *cmdLine = NULL;
+ if (arg && strlen(arg))
+ {
+ cmdLine = strdup(arg);
+ }
+
+ (*env)->ReleaseStringUTFChars(env, commandLineParams, arg);
+
+ ALOGV("Command line %s", cmdLine);
+ argv = malloc(sizeof(char*) * 255);
+ argc = ParseCommandLine(strdup(cmdLine), argv);
+
+ /* verify the argtable[] entries were allocated sucessfully */
+ if (arg_nullcheck(argtable) == 0) {
+ /* Parse the command line as defined by argtable[] */
+ arg_parse(argc, argv, argtable);
+
+ if (ss->count > 0 && ss->dval[0] > 0.0)
+ {
+ SS_MULTIPLIER = ss->dval[0];
+ }
+
+ if (cpu->count > 0 && cpu->ival[0] > 0 && cpu->ival[0] < 10)
+ {
+ CPU_LEVEL = cpu->ival[0];
+ }
+
+ if (gpu->count > 0 && gpu->ival[0] > 0 && gpu->ival[0] < 10)
+ {
+ GPU_LEVEL = gpu->ival[0];
+ }
+ }
+
+ initialize_gl4es();
+
+ ovrAppThread * appThread = (ovrAppThread *) malloc( sizeof( ovrAppThread ) );
+ ovrAppThread_Create( appThread, env, activity, activityClass );
+
+ ovrMessageQueue_Enable( &appThread->MessageQueue, true );
+ ovrMessage message;
+ ovrMessage_Init( &message, MESSAGE_ON_CREATE, MQ_WAIT_PROCESSED );
+ ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
+
+ return (jlong)((size_t)appThread);
+}
+
+
+JNIEXPORT void JNICALL Java_com_drbeef_quake2quest_GLES3JNILib_onStart( JNIEnv * env, jobject obj, jlong handle)
+{
+ ALOGV( " GLES3JNILib::onStart()" );
+
+ ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
+ ovrMessage message;
+ ovrMessage_Init( &message, MESSAGE_ON_START, MQ_WAIT_PROCESSED );
+ ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
+}
+
+JNIEXPORT void JNICALL Java_com_drbeef_quake2quest_GLES3JNILib_onResume( JNIEnv * env, jobject obj, jlong handle )
+{
+ ALOGV( " GLES3JNILib::onResume()" );
+ ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
+ ovrMessage message;
+ ovrMessage_Init( &message, MESSAGE_ON_RESUME, MQ_WAIT_PROCESSED );
+ ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
+}
+
+JNIEXPORT void JNICALL Java_com_drbeef_quake2quest_GLES3JNILib_onPause( JNIEnv * env, jobject obj, jlong handle )
+{
+ ALOGV( " GLES3JNILib::onPause()" );
+ ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
+ ovrMessage message;
+ ovrMessage_Init( &message, MESSAGE_ON_PAUSE, MQ_WAIT_PROCESSED );
+ ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
+}
+
+JNIEXPORT void JNICALL Java_com_drbeef_quake2quest_GLES3JNILib_onStop( JNIEnv * env, jobject obj, jlong handle )
+{
+ ALOGV( " GLES3JNILib::onStop()" );
+ ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
+ ovrMessage message;
+ ovrMessage_Init( &message, MESSAGE_ON_STOP, MQ_WAIT_PROCESSED );
+ ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
+}
+
+JNIEXPORT void JNICALL Java_com_drbeef_quake2quest_GLES3JNILib_onDestroy( JNIEnv * env, jobject obj, jlong handle )
+{
+ ALOGV( " GLES3JNILib::onDestroy()" );
+ ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
+ ovrMessage message;
+ ovrMessage_Init( &message, MESSAGE_ON_DESTROY, MQ_WAIT_PROCESSED );
+ ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
+ ovrMessageQueue_Enable( &appThread->MessageQueue, false );
+
+ ovrAppThread_Destroy( appThread, env );
+ free( appThread );
+}
+
+/*
+================================================================================
+
+Surface lifecycle
+
+================================================================================
+*/
+
+JNIEXPORT void JNICALL Java_com_drbeef_quake2quest_GLES3JNILib_onSurfaceCreated( JNIEnv * env, jobject obj, jlong handle, jobject surface )
+{
+ ALOGV( " GLES3JNILib::onSurfaceCreated()" );
+ ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
+
+ ANativeWindow * newNativeWindow = ANativeWindow_fromSurface( env, surface );
+ if ( ANativeWindow_getWidth( newNativeWindow ) < ANativeWindow_getHeight( newNativeWindow ) )
+ {
+ // An app that is relaunched after pressing the home button gets an initial surface with
+ // the wrong orientation even though android:screenOrientation="landscape" is set in the
+ // manifest. The choreographer callback will also never be called for this surface because
+ // the surface is immediately replaced with a new surface with the correct orientation.
+ ALOGE( " Surface not in landscape mode!" );
+ }
+
+ ALOGV( " NativeWindow = ANativeWindow_fromSurface( env, surface )" );
+ appThread->NativeWindow = newNativeWindow;
+ ovrMessage message;
+ ovrMessage_Init( &message, MESSAGE_ON_SURFACE_CREATED, MQ_WAIT_PROCESSED );
+ ovrMessage_SetPointerParm( &message, 0, appThread->NativeWindow );
+ ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
+}
+
+JNIEXPORT void JNICALL Java_com_drbeef_quake2quest_GLES3JNILib_onSurfaceChanged( JNIEnv * env, jobject obj, jlong handle, jobject surface )
+{
+ ALOGV( " GLES3JNILib::onSurfaceChanged()" );
+ ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
+
+ ANativeWindow * newNativeWindow = ANativeWindow_fromSurface( env, surface );
+ if ( ANativeWindow_getWidth( newNativeWindow ) < ANativeWindow_getHeight( newNativeWindow ) )
+ {
+ // An app that is relaunched after pressing the home button gets an initial surface with
+ // the wrong orientation even though android:screenOrientation="landscape" is set in the
+ // manifest. The choreographer callback will also never be called for this surface because
+ // the surface is immediately replaced with a new surface with the correct orientation.
+ ALOGE( " Surface not in landscape mode!" );
+ }
+
+ if ( newNativeWindow != appThread->NativeWindow )
+ {
+ if ( appThread->NativeWindow != NULL )
+ {
+ ovrMessage message;
+ ovrMessage_Init( &message, MESSAGE_ON_SURFACE_DESTROYED, MQ_WAIT_PROCESSED );
+ ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
+ ALOGV( " ANativeWindow_release( NativeWindow )" );
+ ANativeWindow_release( appThread->NativeWindow );
+ appThread->NativeWindow = NULL;
+ }
+ if ( newNativeWindow != NULL )
+ {
+ ALOGV( " NativeWindow = ANativeWindow_fromSurface( env, surface )" );
+ appThread->NativeWindow = newNativeWindow;
+ ovrMessage message;
+ ovrMessage_Init( &message, MESSAGE_ON_SURFACE_CREATED, MQ_WAIT_PROCESSED );
+ ovrMessage_SetPointerParm( &message, 0, appThread->NativeWindow );
+ ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
+ }
+ }
+ else if ( newNativeWindow != NULL )
+ {
+ ANativeWindow_release( newNativeWindow );
+ }
+}
+
+JNIEXPORT void JNICALL Java_com_drbeef_quake2quest_GLES3JNILib_onSurfaceDestroyed( JNIEnv * env, jobject obj, jlong handle )
+{
+ ALOGV( " GLES3JNILib::onSurfaceDestroyed()" );
+ ovrAppThread * appThread = (ovrAppThread *)((size_t)handle);
+ ovrMessage message;
+ ovrMessage_Init( &message, MESSAGE_ON_SURFACE_DESTROYED, MQ_WAIT_PROCESSED );
+ ovrMessageQueue_PostMessage( &appThread->MessageQueue, &message );
+ ALOGV( " ANativeWindow_release( NativeWindow )" );
+ ANativeWindow_release( appThread->NativeWindow );
+ appThread->NativeWindow = NULL;
+}
+
diff --git a/Projects/Android/jni/RTCWVR/VrCommon.h b/Projects/Android/jni/RTCWVR/VrCommon.h
new file mode 100644
index 0000000..58d09d9
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/VrCommon.h
@@ -0,0 +1,67 @@
+#if !defined(vrcommon_h)
+#define vrcommon_h
+
+#include
+#include
+
+#include
+
+#include "mathlib.h"
+
+#define LOG_TAG "Quake2VR"
+
+#ifndef NDEBUG
+#define DEBUG 1
+#endif
+
+#define ALOGE(...) __android_log_print( ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__ )
+
+#if DEBUG
+#define ALOGV(...) __android_log_print( ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__ )
+#else
+#define ALOGV(...)
+#endif
+
+bool quake2_initialised;
+
+long long global_time;
+
+float playerHeight;
+float playerYaw;
+
+bool showingScreenLayer;
+float vrFOV;
+
+vec3_t worldPosition;
+
+vec3_t hmdPosition;
+vec3_t hmdorientation;
+vec3_t positionDeltaThisFrame;
+
+vec3_t weaponangles;
+vec3_t weaponoffset;
+
+vec3_t flashlightangles;
+vec3_t flashlightoffset;
+
+#define DUCK_NOTDUCKED 0
+#define DUCK_BUTTON 1
+#define DUCK_CROUCHED 2
+int ducked;
+
+bool player_moving;
+
+
+float radians(float deg);
+float degrees(float rad);
+qboolean isMultiplayer();
+double GetTimeInMilliSeconds();
+float length(float x, float y);
+float nonLinearFilter(float in);
+bool between(float min, float val, float max);
+void rotateAboutOrigin(float v1, float v2, float rotation, vec2_t out);
+void QuatToYawPitchRoll(ovrQuatf q, float pitchAdjust, vec3_t out);
+bool useScreenLayer();
+void handleTrackedControllerButton(ovrInputStateTrackedRemote * trackedRemoteState, ovrInputStateTrackedRemote * prevTrackedRemoteState, uint32_t button, int key);
+
+#endif //vrcommon_h
\ No newline at end of file
diff --git a/Projects/Android/jni/RTCWVR/VrCompositor.c b/Projects/Android/jni/RTCWVR/VrCompositor.c
new file mode 100644
index 0000000..314ff5b
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/VrCompositor.c
@@ -0,0 +1,595 @@
+/************************************************************************************
+
+Filename : VrCompositor.c
+
+*************************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include // for prctl( PR_SET_NAME )
+#include
+#include // for AWINDOW_FLAG_KEEP_SCREEN_ON
+
+#include
+#include
+#include
+#include
+
+
+
+#include
+#include
+
+#include "VrCompositor.h"
+
+
+
+/*
+================================================================================
+
+renderState
+
+================================================================================
+*/
+
+
+void getCurrentRenderState( renderState * state)
+{
+ state->VertexBuffer = 0;
+ state->IndexBuffer = 0;
+ state->VertexArrayObject = 0;
+ state->Program = 0;
+
+ glGetIntegerv(GL_ARRAY_BUFFER, &state->VertexBuffer );
+ glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER, &state->IndexBuffer );
+ glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &state->VertexArrayObject );
+ glGetIntegerv(GL_CURRENT_PROGRAM, &state->Program );
+}
+
+void restoreRenderState( renderState * state )
+{
+ GL( glUseProgram( state->Program ) );
+ GL( glBindVertexArray( state->VertexArrayObject ) );
+ GL( glBindBuffer( GL_ARRAY_BUFFER, state->VertexBuffer ) );
+ GL( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, state->IndexBuffer ) );
+}
+
+
+/*
+================================================================================
+
+ovrGeometry
+
+================================================================================
+*/
+
+enum VertexAttributeLocation
+{
+ VERTEX_ATTRIBUTE_LOCATION_POSITION,
+ VERTEX_ATTRIBUTE_LOCATION_COLOR,
+ VERTEX_ATTRIBUTE_LOCATION_UV,
+};
+
+typedef struct
+{
+ enum VertexAttributeLocation location;
+ const char * name;
+} ovrVertexAttribute;
+
+static ovrVertexAttribute ProgramVertexAttributes[] =
+{
+ { VERTEX_ATTRIBUTE_LOCATION_POSITION, "vertexPosition" },
+ { VERTEX_ATTRIBUTE_LOCATION_COLOR, "vertexColor" },
+ { VERTEX_ATTRIBUTE_LOCATION_UV, "vertexUv" },
+};
+
+static void ovrGeometry_Clear( ovrGeometry * geometry )
+{
+ geometry->VertexBuffer = 0;
+ geometry->IndexBuffer = 0;
+ geometry->VertexArrayObject = 0;
+ geometry->VertexCount = 0;
+ geometry->IndexCount = 0;
+ for ( int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++ )
+ {
+ memset( &geometry->VertexAttribs[i], 0, sizeof( geometry->VertexAttribs[i] ) );
+ geometry->VertexAttribs[i].Index = -1;
+ }
+}
+
+static void ovrGeometry_CreateGroundPlane( ovrGeometry * geometry )
+{
+ typedef struct
+ {
+ float positions[4][4];
+ unsigned char colors[4][4];
+ } ovrCubeVertices;
+
+ static const ovrCubeVertices cubeVertices =
+ {
+ // positions
+ {
+ { 4.5f, -1.2f, 4.5f, 1.0f },
+ { 4.5f, -1.2f, -4.5f, 1.0f },
+ { -4.5f, -1.2f, -4.5f, 1.0f },
+ { -4.5f, -1.2f, 4.5f, 1.0f }
+ },
+ // colors
+ {
+ { 255, 0, 0, 255 },
+ { 0, 255, 0, 255 },
+ { 0, 0, 255, 255 },
+ { 255, 255, 0, 255 },
+ },
+ };
+
+ static const unsigned short cubeIndices[6] =
+ {
+ 0, 1, 2,
+ 0, 2, 3,
+ };
+
+ geometry->VertexCount = 4;
+ geometry->IndexCount = 6;
+
+ geometry->VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION;
+ geometry->VertexAttribs[0].Size = 4;
+ geometry->VertexAttribs[0].Type = GL_FLOAT;
+ geometry->VertexAttribs[0].Normalized = false;
+ geometry->VertexAttribs[0].Stride = sizeof( cubeVertices.positions[0] );
+ geometry->VertexAttribs[0].Pointer = (const GLvoid *)offsetof( ovrCubeVertices, positions );
+
+ geometry->VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR;
+ geometry->VertexAttribs[1].Size = 4;
+ geometry->VertexAttribs[1].Type = GL_UNSIGNED_BYTE;
+ geometry->VertexAttribs[1].Normalized = true;
+ geometry->VertexAttribs[1].Stride = sizeof( cubeVertices.colors[0] );
+ geometry->VertexAttribs[1].Pointer = (const GLvoid *)offsetof( ovrCubeVertices, colors );
+
+ renderState state;
+ getCurrentRenderState(&state);
+
+ GL( glGenBuffers( 1, &geometry->VertexBuffer ) );
+ GL( glBindBuffer( GL_ARRAY_BUFFER, geometry->VertexBuffer ) );
+ GL( glBufferData( GL_ARRAY_BUFFER, sizeof( cubeVertices ), &cubeVertices, GL_STATIC_DRAW ) );
+
+ GL( glGenBuffers( 1, &geometry->IndexBuffer ) );
+ GL( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer ) );
+ GL( glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( cubeIndices ), cubeIndices, GL_STATIC_DRAW ) );
+
+ restoreRenderState(&state);
+}
+
+static void ovrGeometry_Destroy( ovrGeometry * geometry )
+{
+ GL( glDeleteBuffers( 1, &geometry->IndexBuffer ) );
+ GL( glDeleteBuffers( 1, &geometry->VertexBuffer ) );
+
+ ovrGeometry_Clear( geometry );
+}
+
+static void ovrGeometry_CreateVAO( ovrGeometry * geometry )
+{
+ renderState state;
+ getCurrentRenderState(&state);
+
+ GL( glGenVertexArrays( 1, &geometry->VertexArrayObject ) );
+ GL( glBindVertexArray( geometry->VertexArrayObject ) );
+
+ GL( glBindBuffer( GL_ARRAY_BUFFER, geometry->VertexBuffer ) );
+
+ for ( int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++ )
+ {
+ if ( geometry->VertexAttribs[i].Index != -1 )
+ {
+ GL( glEnableVertexAttribArray( geometry->VertexAttribs[i].Index ) );
+ GL( glVertexAttribPointer( geometry->VertexAttribs[i].Index, geometry->VertexAttribs[i].Size,
+ geometry->VertexAttribs[i].Type, geometry->VertexAttribs[i].Normalized,
+ geometry->VertexAttribs[i].Stride, geometry->VertexAttribs[i].Pointer ) );
+ }
+ }
+
+ GL( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer ) );
+
+ restoreRenderState(&state);
+}
+
+static void ovrGeometry_DestroyVAO( ovrGeometry * geometry )
+{
+ GL( glDeleteVertexArrays( 1, &geometry->VertexArrayObject ) );
+}
+
+/*
+================================================================================
+
+ovrProgram
+
+================================================================================
+*/
+
+
+typedef struct
+{
+ enum
+ {
+ UNIFORM_VIEW_PROJ_MATRIX,
+ } index;
+ enum
+ {
+ UNIFORM_TYPE_VECTOR4,
+ UNIFORM_TYPE_MATRIX4X4,
+ UNIFORM_TYPE_INT,
+ UNIFORM_TYPE_BUFFER,
+ } type;
+ const char * name;
+} ovrUniform;
+
+static ovrUniform ProgramUniforms[] =
+{
+ { UNIFORM_VIEW_PROJ_MATRIX, UNIFORM_TYPE_MATRIX4X4, "viewProjectionMatrix" },
+};
+
+static void ovrProgram_Clear( ovrProgram * program )
+{
+ program->Program = 0;
+ program->VertexShader = 0;
+ program->FragmentShader = 0;
+ memset( program->UniformLocation, 0, sizeof( program->UniformLocation ) );
+ memset( program->UniformBinding, 0, sizeof( program->UniformBinding ) );
+ memset( program->Textures, 0, sizeof( program->Textures ) );
+}
+
+static bool ovrProgram_Create( ovrProgram * program, const char * vertexSource, const char * fragmentSource )
+{
+ GLint r;
+
+ GL( program->VertexShader = glCreateShader( GL_VERTEX_SHADER ) );
+
+ GL( glShaderSource( program->VertexShader, 1, &vertexSource, 0 ) );
+ GL( glCompileShader( program->VertexShader ) );
+ GL( glGetShaderiv( program->VertexShader, GL_COMPILE_STATUS, &r ) );
+ if ( r == GL_FALSE )
+ {
+ GLchar msg[4096];
+ GL( glGetShaderInfoLog( program->VertexShader, sizeof( msg ), 0, msg ) );
+ ALOGE( "%s\n%s\n", vertexSource, msg );
+ return false;
+ }
+
+ GL( program->FragmentShader = glCreateShader( GL_FRAGMENT_SHADER ) );
+ GL( glShaderSource( program->FragmentShader, 1, &fragmentSource, 0 ) );
+ GL( glCompileShader( program->FragmentShader ) );
+ GL( glGetShaderiv( program->FragmentShader, GL_COMPILE_STATUS, &r ) );
+ if ( r == GL_FALSE )
+ {
+ GLchar msg[4096];
+ GL( glGetShaderInfoLog( program->FragmentShader, sizeof( msg ), 0, msg ) );
+ ALOGE( "%s\n%s\n", fragmentSource, msg );
+ return false;
+ }
+
+ GL( program->Program = glCreateProgram() );
+ GL( glAttachShader( program->Program, program->VertexShader ) );
+ GL( glAttachShader( program->Program, program->FragmentShader ) );
+
+ // Bind the vertex attribute locations.
+ for ( int i = 0; i < sizeof( ProgramVertexAttributes ) / sizeof( ProgramVertexAttributes[0] ); i++ )
+ {
+ GL( glBindAttribLocation( program->Program, ProgramVertexAttributes[i].location, ProgramVertexAttributes[i].name ) );
+ }
+
+ GL( glLinkProgram( program->Program ) );
+ GL( glGetProgramiv( program->Program, GL_LINK_STATUS, &r ) );
+ if ( r == GL_FALSE )
+ {
+ GLchar msg[4096];
+ GL( glGetProgramInfoLog( program->Program, sizeof( msg ), 0, msg ) );
+ ALOGE( "Linking program failed: %s\n", msg );
+ return false;
+ }
+
+ int numBufferBindings = 0;
+
+ // Get the uniform locations.
+ memset( program->UniformLocation, -1, sizeof( program->UniformLocation ) );
+ for ( int i = 0; i < sizeof( ProgramUniforms ) / sizeof( ProgramUniforms[0] ); i++ )
+ {
+ const int uniformIndex = ProgramUniforms[i].index;
+ if ( ProgramUniforms[i].type == UNIFORM_TYPE_BUFFER )
+ {
+ GL( program->UniformLocation[uniformIndex] = glGetUniformBlockIndex( program->Program, ProgramUniforms[i].name ) );
+ program->UniformBinding[uniformIndex] = numBufferBindings++;
+ GL( glUniformBlockBinding( program->Program, program->UniformLocation[uniformIndex], program->UniformBinding[uniformIndex] ) );
+ }
+ else
+ {
+ GL( program->UniformLocation[uniformIndex] = glGetUniformLocation( program->Program, ProgramUniforms[i].name ) );
+ program->UniformBinding[uniformIndex] = program->UniformLocation[uniformIndex];
+ }
+ }
+
+ renderState state;
+ getCurrentRenderState(&state);
+
+ GL( glUseProgram( program->Program ) );
+
+ // Get the texture locations.
+ for ( int i = 0; i < MAX_PROGRAM_TEXTURES; i++ )
+ {
+ char name[32];
+ sprintf( name, "Texture%i", i );
+ program->Textures[i] = glGetUniformLocation( program->Program, name );
+ if ( program->Textures[i] != -1 )
+ {
+ GL( glUniform1i( program->Textures[i], i ) );
+ }
+ }
+
+ restoreRenderState(&state);
+
+ return true;
+}
+
+static void ovrProgram_Destroy( ovrProgram * program )
+{
+ if ( program->Program != 0 )
+ {
+ GL( glDeleteProgram( program->Program ) );
+ program->Program = 0;
+ }
+ if ( program->VertexShader != 0 )
+ {
+ GL( glDeleteShader( program->VertexShader ) );
+ program->VertexShader = 0;
+ }
+ if ( program->FragmentShader != 0 )
+ {
+ GL( glDeleteShader( program->FragmentShader ) );
+ program->FragmentShader = 0;
+ }
+}
+
+static const char VERTEX_SHADER[] =
+ "#version 300 es\n"
+ "in vec3 vertexPosition;\n"
+ "in vec4 vertexColor;\n"
+ "uniform mat4 viewProjectionMatrix;\n"
+ "out vec4 fragmentColor;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = viewProjectionMatrix * vec4( vertexPosition, 1.0 );\n"
+ " fragmentColor = vertexColor;\n"
+ "}\n";
+
+static const char FRAGMENT_SHADER[] =
+ "#version 300 es\n"
+ "in lowp vec4 fragmentColor;\n"
+ "out lowp vec4 outColor;\n"
+ "void main()\n"
+ "{\n"
+ " outColor = fragmentColor;\n"
+ "}\n";
+
+/*
+================================================================================
+
+ovrScene
+
+================================================================================
+*/
+
+void ovrScene_Clear( ovrScene * scene )
+{
+ scene->CreatedScene = false;
+ scene->CreatedVAOs = false;
+ ovrProgram_Clear( &scene->Program );
+ ovrGeometry_Clear( &scene->GroundPlane );
+ ovrRenderer_Clear( &scene->CylinderRenderer );
+
+ scene->CylinderWidth = 0;
+ scene->CylinderHeight = 0;
+}
+
+bool ovrScene_IsCreated( ovrScene * scene )
+{
+ return scene->CreatedScene;
+}
+
+void ovrScene_CreateVAOs( ovrScene * scene )
+{
+ if ( !scene->CreatedVAOs )
+ {
+ ovrGeometry_CreateVAO( &scene->GroundPlane );
+ scene->CreatedVAOs = true;
+ }
+}
+
+void ovrScene_DestroyVAOs( ovrScene * scene )
+{
+ if ( scene->CreatedVAOs )
+ {
+ ovrGeometry_DestroyVAO( &scene->GroundPlane );
+ scene->CreatedVAOs = false;
+ }
+}
+
+void ovrScene_Create( int width, int height, ovrScene * scene, const ovrJava * java )
+{
+ // Simple ground plane geometry.
+ {
+ ovrProgram_Create( &scene->Program, VERTEX_SHADER, FRAGMENT_SHADER );
+ ovrGeometry_CreateGroundPlane( &scene->GroundPlane );
+ ovrScene_CreateVAOs( scene );
+ }
+
+ // Create Cylinder renderer
+ {
+ scene->CylinderWidth = width;
+ scene->CylinderHeight = height;
+
+ //Create cylinder renderer
+ ovrRenderer_Create( width, height, &scene->CylinderRenderer, java );
+ }
+
+ scene->CreatedScene = true;
+}
+
+void ovrScene_Destroy( ovrScene * scene )
+{
+ ovrScene_DestroyVAOs( scene );
+ ovrProgram_Destroy( &scene->Program );
+ ovrGeometry_Destroy( &scene->GroundPlane );
+ ovrRenderer_Destroy( &scene->CylinderRenderer );
+
+ scene->CreatedScene = false;
+}
+
+/*
+================================================================================
+
+ovrRenderer
+
+================================================================================
+*/
+
+ovrLayerProjection2 ovrRenderer_RenderGroundPlaneToEyeBuffer( ovrRenderer * renderer, const ovrJava * java,
+ const ovrScene * scene, const ovrTracking2 * tracking )
+{
+ ovrLayerProjection2 layer = vrapi_DefaultLayerProjection2();
+ layer.HeadPose = tracking->HeadPose;
+ for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )
+ {
+ ovrFramebuffer * frameBuffer = &renderer->FrameBuffer[eye];
+ layer.Textures[eye].ColorSwapChain = frameBuffer->ColorTextureSwapChain;
+ layer.Textures[eye].SwapChainIndex = frameBuffer->TextureSwapChainIndex;
+ layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection( &tracking->Eye[eye].ProjectionMatrix );
+ }
+ layer.Header.Flags |= VRAPI_FRAME_LAYER_FLAG_CHROMATIC_ABERRATION_CORRECTION;
+
+ for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )
+ {
+ ovrFramebuffer * frameBuffer = &renderer->FrameBuffer[eye];
+ ovrFramebuffer_SetCurrent( frameBuffer );
+
+ renderState state;
+ getCurrentRenderState(&state);
+
+ GL( glUseProgram( scene->Program.Program ) );
+
+ ovrMatrix4f viewProjMatrix = ovrMatrix4f_Multiply( &tracking->Eye[eye].ProjectionMatrix, &tracking->Eye[eye].ViewMatrix );
+ glUniformMatrix4fv( scene->Program.UniformLocation[UNIFORM_VIEW_PROJ_MATRIX], 1, GL_TRUE, &viewProjMatrix.M[0][0] );
+
+ GL( glEnable( GL_SCISSOR_TEST ) );
+ GL( glDepthMask( GL_TRUE ) );
+ GL( glEnable( GL_DEPTH_TEST ) );
+ GL( glDepthFunc( GL_LEQUAL ) );
+ GL( glEnable( GL_CULL_FACE ) );
+ GL( glCullFace( GL_BACK ) );
+ GL( glViewport( 0, 0, frameBuffer->Width, frameBuffer->Height ) );
+ GL( glScissor( 0, 0, frameBuffer->Width, frameBuffer->Height ) );
+ GL( glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ) );
+ GL( glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) );
+
+ //bind buffers
+ GL( glBindBuffer( GL_ARRAY_BUFFER, scene->GroundPlane.VertexBuffer ) );
+ GL( glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, scene->GroundPlane.IndexBuffer ) );
+ GL( glBindVertexArray( scene->GroundPlane.VertexArrayObject ) );
+
+ GL( glDrawElements( GL_TRIANGLES, scene->GroundPlane.IndexCount, GL_UNSIGNED_SHORT, NULL ) );
+
+ restoreRenderState(&state);
+
+ // Explicitly clear the border texels to black when GL_CLAMP_TO_BORDER is not available.
+ ovrFramebuffer_ClearEdgeTexels( frameBuffer );
+
+ ovrFramebuffer_Resolve( frameBuffer );
+ ovrFramebuffer_Advance( frameBuffer );
+ }
+
+ ovrFramebuffer_SetNone();
+
+ return layer;
+}
+
+// Assumes landscape cylinder shape.
+static ovrMatrix4f CylinderModelMatrix( const int texWidth, const int texHeight,
+ const ovrVector3f translation,
+ const float rotateYaw,
+ const float rotatePitch,
+ const float radius,
+ const float density )
+{
+ const ovrMatrix4f scaleMatrix = ovrMatrix4f_CreateScale( radius, radius * (float)texHeight * VRAPI_PI / density, radius );
+ const ovrMatrix4f transMatrix = ovrMatrix4f_CreateTranslation( translation.x, translation.y, translation.z );
+ const ovrMatrix4f rotXMatrix = ovrMatrix4f_CreateRotation( rotateYaw, 0.0f, 0.0f );
+ const ovrMatrix4f rotYMatrix = ovrMatrix4f_CreateRotation( 0.0f, rotatePitch, 0.0f );
+
+ const ovrMatrix4f m0 = ovrMatrix4f_Multiply( &transMatrix, &scaleMatrix );
+ const ovrMatrix4f m1 = ovrMatrix4f_Multiply( &rotXMatrix, &m0 );
+ const ovrMatrix4f m2 = ovrMatrix4f_Multiply( &rotYMatrix, &m1 );
+
+ return m2;
+}
+
+ovrLayerCylinder2 BuildCylinderLayer( ovrRenderer * cylinderRenderer,
+ const int textureWidth, const int textureHeight,
+ const ovrTracking2 * tracking, float rotatePitch )
+{
+ ovrLayerCylinder2 layer = vrapi_DefaultLayerCylinder2();
+
+ const float fadeLevel = 1.0f;
+ layer.Header.ColorScale.x =
+ layer.Header.ColorScale.y =
+ layer.Header.ColorScale.z =
+ layer.Header.ColorScale.w = fadeLevel;
+ layer.Header.SrcBlend = VRAPI_FRAME_LAYER_BLEND_SRC_ALPHA;
+ layer.Header.DstBlend = VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_SRC_ALPHA;
+
+ //layer.Header.Flags = VRAPI_FRAME_LAYER_FLAG_CLIP_TO_TEXTURE_RECT;
+
+ layer.HeadPose = tracking->HeadPose;
+
+ const float density = 4500.0f;
+ const float rotateYaw = 0.0f;
+ const float radius = 2.0f;
+ const ovrVector3f translation = { 0.0f, playerHeight, -0.5f };
+
+ ovrMatrix4f cylinderTransform =
+ CylinderModelMatrix( textureWidth, textureHeight, translation,
+ rotateYaw, rotatePitch, radius, density );
+
+ const float circScale = density * 0.5f / textureWidth;
+ const float circBias = -circScale * ( 0.5f * ( 1.0f - 1.0f / circScale ) );
+
+ for ( int eye = 0; eye < VRAPI_FRAME_LAYER_EYE_MAX; eye++ )
+ {
+ ovrFramebuffer * cylinderFrameBuffer = &cylinderRenderer->FrameBuffer[eye];
+
+ ovrMatrix4f modelViewMatrix = ovrMatrix4f_Multiply( &tracking->Eye[eye].ViewMatrix, &cylinderTransform );
+ layer.Textures[eye].TexCoordsFromTanAngles = ovrMatrix4f_Inverse( &modelViewMatrix );
+ layer.Textures[eye].ColorSwapChain = cylinderFrameBuffer->ColorTextureSwapChain;
+ layer.Textures[eye].SwapChainIndex = cylinderFrameBuffer->TextureSwapChainIndex;
+
+ // Texcoord scale and bias is just a representation of the aspect ratio. The positioning
+ // of the cylinder is handled entirely by the TexCoordsFromTanAngles matrix.
+
+ const float texScaleX = circScale;
+ const float texBiasX = circBias;
+ const float texScaleY = -0.5f;
+ const float texBiasY = texScaleY * ( 0.5f * ( 1.0f - ( 1.0f / texScaleY ) ) );
+
+ layer.Textures[eye].TextureMatrix.M[0][0] = texScaleX;
+ layer.Textures[eye].TextureMatrix.M[0][2] = texBiasX;
+ layer.Textures[eye].TextureMatrix.M[1][1] = texScaleY;
+ layer.Textures[eye].TextureMatrix.M[1][2] = -texBiasY;
+
+ layer.Textures[eye].TextureRect.width = 1.0f;
+ layer.Textures[eye].TextureRect.height = 1.0f;
+ }
+
+ return layer;
+}
diff --git a/Projects/Android/jni/RTCWVR/VrCompositor.h b/Projects/Android/jni/RTCWVR/VrCompositor.h
new file mode 100644
index 0000000..9c02f4d
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/VrCompositor.h
@@ -0,0 +1,217 @@
+/************************************************************************************
+
+Filename : VrCompositor.h
+
+*************************************************************************************/
+
+#include "VrInput.h"
+
+#define CHECK_GL_ERRORS
+#ifdef CHECK_GL_ERRORS
+
+static const char * GlErrorString( GLenum error )
+{
+ switch ( error )
+ {
+ case GL_NO_ERROR: return "GL_NO_ERROR";
+ case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
+ case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
+ case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
+ case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
+ case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
+ default: return "unknown";
+ }
+}
+
+static void GLCheckErrors( int line )
+{
+ for ( int i = 0; i < 10; i++ )
+ {
+ const GLenum error = glGetError();
+ if ( error == GL_NO_ERROR )
+ {
+ break;
+ }
+ ALOGE( "GL error on line %d: %s", line, GlErrorString( error ) );
+ }
+}
+
+#define GL( func ) func; GLCheckErrors( __LINE__ );
+
+#else // CHECK_GL_ERRORS
+
+#define GL( func ) func;
+
+#endif // CHECK_GL_ERRORS
+
+
+/*
+================================================================================
+
+ovrFramebuffer
+
+================================================================================
+*/
+
+typedef struct
+{
+ int Width;
+ int Height;
+ int Multisamples;
+ int TextureSwapChainLength;
+ int TextureSwapChainIndex;
+ ovrTextureSwapChain * ColorTextureSwapChain;
+ GLuint * DepthBuffers;
+ GLuint * FrameBuffers;
+} ovrFramebuffer;
+
+void ovrFramebuffer_SetCurrent( ovrFramebuffer * frameBuffer );
+void ovrFramebuffer_Destroy( ovrFramebuffer * frameBuffer );
+void ovrFramebuffer_SetNone();
+void ovrFramebuffer_Resolve( ovrFramebuffer * frameBuffer );
+void ovrFramebuffer_Advance( ovrFramebuffer * frameBuffer );
+void ovrFramebuffer_ClearEdgeTexels( ovrFramebuffer * frameBuffer );
+
+/*
+================================================================================
+
+ovrRenderer
+
+================================================================================
+*/
+
+typedef struct
+{
+ ovrFramebuffer FrameBuffer[VRAPI_FRAME_LAYER_EYE_MAX];
+ ovrMatrix4f ProjectionMatrix;
+ int NumBuffers;
+} ovrRenderer;
+
+
+void ovrRenderer_Clear( ovrRenderer * renderer );
+void ovrRenderer_Create( int width, int height, ovrRenderer * renderer, const ovrJava * java );
+void ovrRenderer_Destroy( ovrRenderer * renderer );
+
+
+/*
+================================================================================
+
+renderState
+
+================================================================================
+*/
+
+typedef struct
+{
+ GLuint VertexBuffer;
+ GLuint IndexBuffer;
+ GLuint VertexArrayObject;
+ GLuint Program;
+ GLuint VertexShader;
+ GLuint FragmentShader;
+} renderState;
+
+void getCurrentRenderState( renderState * state);
+void restoreRenderState( renderState * state );
+
+/*
+================================================================================
+
+ovrGeometry
+
+================================================================================
+*/
+
+typedef struct
+{
+ GLuint Index;
+ GLint Size;
+ GLenum Type;
+ GLboolean Normalized;
+ GLsizei Stride;
+ const GLvoid * Pointer;
+} ovrVertexAttribPointer;
+
+#define MAX_VERTEX_ATTRIB_POINTERS 3
+
+typedef struct
+{
+ GLuint VertexBuffer;
+ GLuint IndexBuffer;
+ GLuint VertexArrayObject;
+ int VertexCount;
+ int IndexCount;
+ ovrVertexAttribPointer VertexAttribs[MAX_VERTEX_ATTRIB_POINTERS];
+} ovrGeometry;
+
+/*
+================================================================================
+
+ovrProgram
+
+================================================================================
+*/
+
+#define MAX_PROGRAM_UNIFORMS 8
+#define MAX_PROGRAM_TEXTURES 8
+
+typedef struct
+{
+ GLuint Program;
+ GLuint VertexShader;
+ GLuint FragmentShader;
+ // These will be -1 if not used by the program.
+ GLint UniformLocation[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name
+ GLint UniformBinding[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name
+ GLint Textures[MAX_PROGRAM_TEXTURES]; // Texture%i
+} ovrProgram;
+
+/*
+================================================================================
+
+ovrScene
+
+================================================================================
+*/
+
+typedef struct
+{
+ bool CreatedScene;
+ bool CreatedVAOs;
+ ovrProgram Program;
+ ovrGeometry GroundPlane;
+
+ //Proper renderer for stereo rendering to the cylinder layer
+ ovrRenderer CylinderRenderer;
+
+ int CylinderWidth;
+ int CylinderHeight;
+} ovrScene;
+
+bool ovrScene_IsCreated( ovrScene * scene );
+void ovrScene_Clear( ovrScene * scene );
+void ovrScene_Create( int width, int height, ovrScene * scene, const ovrJava * java );
+void ovrScene_CreateVAOs( ovrScene * scene );
+void ovrScene_DestroyVAOs( ovrScene * scene );
+void ovrScene_Destroy( ovrScene * scene );
+
+/*
+================================================================================
+
+ovrRenderer
+
+================================================================================
+*/
+
+ovrLayerProjection2 ovrRenderer_RenderGroundPlaneToEyeBuffer( ovrRenderer * renderer, const ovrJava * java,
+ const ovrScene * scene, const ovrTracking2 * tracking );
+
+ovrLayerProjection2 ovrRenderer_RenderToEyeBuffer( ovrRenderer * renderer, const ovrJava * java,
+ const ovrTracking2 * tracking );
+
+ovrLayerCylinder2 BuildCylinderLayer( ovrRenderer * cylinderRenderer,
+ const int textureWidth, const int textureHeight,
+ const ovrTracking2 * tracking, float rotateYaw );
+;
+
+
diff --git a/Projects/Android/jni/RTCWVR/VrCvars.h b/Projects/Android/jni/RTCWVR/VrCvars.h
new file mode 100644
index 0000000..6d6e774
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/VrCvars.h
@@ -0,0 +1,11 @@
+cvar_t *vr_snapturn_angle;
+cvar_t *vr_positional_factor;
+cvar_t *vr_walkdirection;
+cvar_t *vr_weapon_pitchadjust;
+cvar_t *vr_lasersight;
+cvar_t *vr_control_scheme;
+cvar_t *vr_height_adjust;
+cvar_t *vr_worldscale;
+cvar_t *vr_weaponscale;
+cvar_t *vr_weapon_stabilised;
+cvar_t *vr_comfort_mask;
diff --git a/Projects/Android/jni/RTCWVR/VrInput.h b/Projects/Android/jni/RTCWVR/VrInput.h
new file mode 100644
index 0000000..7c2e955
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/VrInput.h
@@ -0,0 +1,37 @@
+
+#if !defined(vrinput_h)
+#define vrinput_h
+
+#include "VrCommon.h"
+
+//New control scheme definitions to be defined L1VR_SurfaceView.c enumeration
+enum control_scheme;
+
+ovrInputStateTrackedRemote leftTrackedRemoteState_old;
+ovrInputStateTrackedRemote leftTrackedRemoteState_new;
+ovrTracking leftRemoteTracking_new;
+
+ovrInputStateTrackedRemote rightTrackedRemoteState_old;
+ovrInputStateTrackedRemote rightTrackedRemoteState_new;
+ovrTracking rightRemoteTracking_new;
+
+ovrDeviceID controllerIDs[2];
+
+float remote_movementSideways;
+float remote_movementForward;
+float remote_movementUp;
+float positional_movementSideways;
+float positional_movementForward;
+float snapTurn;
+
+void sendButtonAction(const char* action, long buttonDown);
+void sendButtonActionSimple(const char* action);
+
+void acquireTrackedRemotesData(const ovrMobile *Ovr, double displayTime);
+
+void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, ovrInputStateTrackedRemote *pDominantTrackedRemoteOld, ovrTracking* pDominantTracking,
+ ovrInputStateTrackedRemote *pOffTrackedRemoteNew, ovrInputStateTrackedRemote *pOffTrackedRemoteOld, ovrTracking* pOffTracking,
+ int domButton1, int domButton2, int offButton1, int offButton2 );
+
+
+#endif //vrinput_h
\ No newline at end of file
diff --git a/Projects/Android/jni/RTCWVR/VrInputCommon.c b/Projects/Android/jni/RTCWVR/VrInputCommon.c
new file mode 100644
index 0000000..e73302b
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/VrInputCommon.c
@@ -0,0 +1,142 @@
+/************************************************************************************
+
+Filename : VrInputRight.c
+Content : Handles common controller input functionality
+Created : September 2019
+Authors : Simon Brown
+
+*************************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+
+#include "VrInput.h"
+
+//keys.h
+void Key_Event (int key, qboolean down, unsigned time);
+
+void handleTrackedControllerButton(ovrInputStateTrackedRemote * trackedRemoteState, ovrInputStateTrackedRemote * prevTrackedRemoteState, uint32_t button, int key)
+{
+ if ((trackedRemoteState->Buttons & button) != (prevTrackedRemoteState->Buttons & button))
+ {
+ Key_Event(key, (trackedRemoteState->Buttons & button) != 0, global_time);
+ }
+}
+
+
+static void Matrix4x4_Transform (const matrix4x4 *in, const float v[3], float out[3])
+{
+ out[0] = v[0] * (*in)[0][0] + v[1] * (*in)[0][1] + v[2] * (*in)[0][2] + (*in)[0][3];
+ out[1] = v[0] * (*in)[1][0] + v[1] * (*in)[1][1] + v[2] * (*in)[1][2] + (*in)[1][3];
+ out[2] = v[0] * (*in)[2][0] + v[1] * (*in)[2][1] + v[2] * (*in)[2][2] + (*in)[2][3];
+}
+
+void Matrix4x4_CreateFromEntity( matrix4x4 out, const vec3_t angles, const vec3_t origin, float scale );
+
+void rotateAboutOrigin(float v1, float v2, float rotation, vec2_t out)
+{
+ vec3_t temp = {0.0f, 0.0f, 0.0f};
+ temp[0] = v1;
+ temp[1] = v2;
+
+ vec3_t v = {0.0f, 0.0f, 0.0f};
+ matrix4x4 matrix;
+ vec3_t angles = {0.0f, rotation, 0.0f};
+ vec3_t origin = {0.0f, 0.0f, 0.0f};
+ Matrix4x4_CreateFromEntity(matrix, angles, origin, 1.0f);
+ Matrix4x4_Transform(&matrix, temp, v);
+
+ out[0] = v[0];
+ out[1] = v[1];
+}
+
+float length(float x, float y)
+{
+ return sqrtf(powf(x, 2.0f) + powf(y, 2.0f));
+}
+
+#define NLF_DEADZONE 0.1
+#define NLF_POWER 2.2
+
+float nonLinearFilter(float in)
+{
+ float val = 0.0f;
+ if (in > NLF_DEADZONE)
+ {
+ val = in;
+ val -= NLF_DEADZONE;
+ val /= (1.0f - NLF_DEADZONE);
+ val = powf(val, NLF_POWER);
+ }
+ else if (in < -NLF_DEADZONE)
+ {
+ val = in;
+ val += NLF_DEADZONE;
+ val /= (1.0f - NLF_DEADZONE);
+ val = -powf(fabsf(val), NLF_POWER);
+ }
+
+ return val;
+}
+
+void sendButtonActionSimple(const char* action)
+{
+ char command[256];
+ snprintf( command, sizeof( command ), "%s\n", action );
+ Cbuf_AddText( command );
+}
+
+bool between(float min, float val, float max)
+{
+ return (min < val) && (val < max);
+}
+
+void sendButtonAction(const char* action, long buttonDown)
+{
+ char command[256];
+ snprintf( command, sizeof( command ), "%s\n", action );
+ if (!buttonDown)
+ {
+ command[0] = '-';
+ }
+ Cbuf_AddText( command );
+}
+
+void acquireTrackedRemotesData(const ovrMobile *Ovr, double displayTime) {//The amount of yaw changed by controller
+ for ( int i = 0; ; i++ ) {
+ ovrInputCapabilityHeader cap;
+ ovrResult result = vrapi_EnumerateInputDevices(Ovr, i, &cap);
+ if (result < 0) {
+ break;
+ }
+
+ if (cap.Type == ovrControllerType_TrackedRemote) {
+ ovrTracking remoteTracking;
+ ovrInputStateTrackedRemote trackedRemoteState;
+ trackedRemoteState.Header.ControllerType = ovrControllerType_TrackedRemote;
+ result = vrapi_GetCurrentInputState(Ovr, cap.DeviceID, &trackedRemoteState.Header);
+
+ if (result == ovrSuccess) {
+ ovrInputTrackedRemoteCapabilities remoteCapabilities;
+ remoteCapabilities.Header = cap;
+ result = vrapi_GetInputDeviceCapabilities(Ovr, &remoteCapabilities.Header);
+
+ result = vrapi_GetInputTrackingState(Ovr, cap.DeviceID, displayTime,
+ &remoteTracking);
+
+ if (remoteCapabilities.ControllerCapabilities & ovrControllerCaps_RightHand) {
+ rightTrackedRemoteState_new = trackedRemoteState;
+ rightRemoteTracking_new = remoteTracking;
+ controllerIDs[1] = cap.DeviceID;
+ } else{
+ leftTrackedRemoteState_new = trackedRemoteState;
+ leftRemoteTracking_new = remoteTracking;
+ controllerIDs[0] = cap.DeviceID;
+ }
+ }
+ }
+ }
+}
diff --git a/Projects/Android/jni/RTCWVR/VrInputDefault.c b/Projects/Android/jni/RTCWVR/VrInputDefault.c
new file mode 100644
index 0000000..abff774
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/VrInputDefault.c
@@ -0,0 +1,376 @@
+/************************************************************************************
+
+Filename : VrInputDefault.c
+Content : Handles default controller input
+Created : August 2019
+Authors : Simon Brown
+
+*************************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+
+#include "VrInput.h"
+#include "VrCvars.h"
+
+#include "../quake2/src/client/header/client.h"
+
+extern cvar_t *cl_forwardspeed;
+cvar_t *sv_cheats;
+extern cvar_t *vr_weapon_stabilised;
+
+
+void HandleInput_Default( ovrInputStateTrackedRemote *pDominantTrackedRemoteNew, ovrInputStateTrackedRemote *pDominantTrackedRemoteOld, ovrTracking* pDominantTracking,
+ ovrInputStateTrackedRemote *pOffTrackedRemoteNew, ovrInputStateTrackedRemote *pOffTrackedRemoteOld, ovrTracking* pOffTracking,
+ int domButton1, int domButton2, int offButton1, int offButton2 )
+
+{
+ //Ensure handedness is set correctly
+ Cvar_Set("hand", vr_control_scheme->value < 10 ? "0" : "1");
+
+ //Get the cvar
+ sv_cheats = Cvar_Get("cheats", "0", CVAR_ARCHIVE);
+
+ static qboolean dominantGripPushed = false;
+ static float dominantGripPushTime = 0.0f;
+ static qboolean inventoryManagementMode = false;
+
+ //Show screen view (if in multiplayer toggle scoreboard)
+ if (((pOffTrackedRemoteNew->Buttons & offButton2) !=
+ (pOffTrackedRemoteOld->Buttons & offButton2)) &&
+ (pOffTrackedRemoteNew->Buttons & offButton2)) {
+
+ showingScreenLayer = !showingScreenLayer;
+
+ //Check we are in multiplayer
+ if (isMultiplayer()) {
+ sendButtonActionSimple("score");
+ }
+ }
+
+ //Menu button
+ handleTrackedControllerButton(&leftTrackedRemoteState_new, &leftTrackedRemoteState_old, ovrButton_Enter, K_ESCAPE);
+
+ if (cls.key_dest == key_menu)
+ {
+ int leftJoyState = (pOffTrackedRemoteNew->Joystick.x > 0.7f ? 1 : 0);
+ if (leftJoyState != (pOffTrackedRemoteOld->Joystick.x > 0.7f ? 1 : 0)) {
+ Key_Event(K_RIGHTARROW, leftJoyState, global_time);
+ }
+ leftJoyState = (pOffTrackedRemoteNew->Joystick.x < -0.7f ? 1 : 0);
+ if (leftJoyState != (pOffTrackedRemoteOld->Joystick.x < -0.7f ? 1 : 0)) {
+ Key_Event(K_LEFTARROW, leftJoyState, global_time);
+ }
+ leftJoyState = (pOffTrackedRemoteNew->Joystick.y < -0.7f ? 1 : 0);
+ if (leftJoyState != (pOffTrackedRemoteOld->Joystick.y < -0.7f ? 1 : 0)) {
+ Key_Event(K_DOWNARROW, leftJoyState, global_time);
+ }
+ leftJoyState = (pOffTrackedRemoteNew->Joystick.y > 0.7f ? 1 : 0);
+ if (leftJoyState != (pOffTrackedRemoteOld->Joystick.y > 0.7f ? 1 : 0)) {
+ Key_Event(K_UPARROW, leftJoyState, global_time);
+ }
+
+ handleTrackedControllerButton(pDominantTrackedRemoteNew, pDominantTrackedRemoteOld, domButton1, K_ENTER);
+ handleTrackedControllerButton(pDominantTrackedRemoteNew, pDominantTrackedRemoteOld, ovrButton_Trigger, K_ENTER);
+ handleTrackedControllerButton(pDominantTrackedRemoteNew, pDominantTrackedRemoteOld, domButton2, K_ESCAPE);
+ }
+ else
+ {
+ float distance = sqrtf(powf(pOffTracking->HeadPose.Pose.Position.x - pDominantTracking->HeadPose.Pose.Position.x, 2) +
+ powf(pOffTracking->HeadPose.Pose.Position.y - pDominantTracking->HeadPose.Pose.Position.y, 2) +
+ powf(pOffTracking->HeadPose.Pose.Position.z - pDominantTracking->HeadPose.Pose.Position.z, 2));
+
+ //Turn on weapon stabilisation?
+ if ((pOffTrackedRemoteNew->Buttons & ovrButton_GripTrigger) !=
+ (pOffTrackedRemoteOld->Buttons & ovrButton_GripTrigger)) {
+
+ if (pOffTrackedRemoteNew->Buttons & ovrButton_GripTrigger)
+ {
+ if (distance < 0.50f)
+ {
+ Cvar_ForceSet("vr_weapon_stabilised", "1.0");
+ }
+ }
+ else
+ {
+ Cvar_ForceSet("vr_weapon_stabilised", "0.0");
+ }
+ }
+
+ //dominant hand stuff first
+ {
+ ///Weapon location relative to view
+ weaponoffset[0] = pDominantTracking->HeadPose.Pose.Position.x - hmdPosition[0];
+ weaponoffset[1] = pDominantTracking->HeadPose.Pose.Position.y - hmdPosition[1];
+ weaponoffset[2] = pDominantTracking->HeadPose.Pose.Position.z - hmdPosition[2];
+
+ {
+ vec2_t v;
+ rotateAboutOrigin(-weaponoffset[0], weaponoffset[2], (cl.refdef.viewangles[YAW] - hmdorientation[YAW]), v);
+ weaponoffset[0] = v[0];
+ weaponoffset[2] = v[1];
+ }
+
+ //Set gun angles - We need to calculate all those we might need (including adjustments) for the client to then take its pick
+ const ovrQuatf quatRemote = pDominantTracking->HeadPose.Pose.Orientation;
+ QuatToYawPitchRoll(quatRemote, vr_weapon_pitchadjust->value, weaponangles);
+ weaponangles[YAW] += (cl.refdef.viewangles[YAW] - hmdorientation[YAW]);
+ weaponangles[ROLL] *= -1.0f;
+
+
+ if (vr_weapon_stabilised->value == 1.0f)
+ {
+ float z = pOffTracking->HeadPose.Pose.Position.z - pDominantTracking->HeadPose.Pose.Position.z;
+ float x = pOffTracking->HeadPose.Pose.Position.x - pDominantTracking->HeadPose.Pose.Position.x;
+ float y = pOffTracking->HeadPose.Pose.Position.y - pDominantTracking->HeadPose.Pose.Position.y;
+ float zxDist = length(x, z);
+
+ if (zxDist != 0.0f && z != 0.0f) {
+ VectorSet(weaponangles, -degrees(atanf(y / zxDist)), (cl.refdef.viewangles[YAW] - hmdorientation[YAW]) - degrees(atan2f(x, -z)), weaponangles[ROLL]);
+ }
+ }
+
+ if ((pDominantTrackedRemoteNew->Buttons & ovrButton_GripTrigger) !=
+ (pDominantTrackedRemoteOld->Buttons & ovrButton_GripTrigger)) {
+
+ sendButtonActionSimple("inven");
+ inventoryManagementMode = (pDominantTrackedRemoteNew->Buttons & ovrButton_GripTrigger) > 0;
+ }
+ }
+
+ float controllerYawHeading = 0.0f;
+ //off-hand stuff
+ {
+ flashlightoffset[0] = pOffTracking->HeadPose.Pose.Position.x - hmdPosition[0];
+ flashlightoffset[1] = pOffTracking->HeadPose.Pose.Position.y - hmdPosition[1];
+ flashlightoffset[2] = pOffTracking->HeadPose.Pose.Position.z - hmdPosition[2];
+
+ vec2_t v;
+ rotateAboutOrigin(-flashlightoffset[0], flashlightoffset[2], (cl.refdef.viewangles[YAW] - hmdorientation[YAW]), v);
+ flashlightoffset[0] = v[0];
+ flashlightoffset[2] = v[1];
+
+ QuatToYawPitchRoll(pOffTracking->HeadPose.Pose.Orientation, 15.0f, flashlightangles);
+
+ flashlightangles[YAW] += (cl.refdef.viewangles[YAW] - hmdorientation[YAW]);
+
+ if (vr_walkdirection->value == 0) {
+ controllerYawHeading = -cl.refdef.viewangles[YAW] + flashlightangles[YAW];
+ }
+ else
+ {
+ controllerYawHeading = 0.0f;//-cl.viewangles[YAW];
+ }
+ }
+
+ //Right-hand specific stuff
+ {
+ ALOGV(" Right-Controller-Position: %f, %f, %f",
+ pDominantTracking->HeadPose.Pose.Position.x,
+ pDominantTracking->HeadPose.Pose.Position.y,
+ pDominantTracking->HeadPose.Pose.Position.z);
+
+ //This section corrects for the fact that the controller actually controls direction of movement, but we want to move relative to the direction the
+ //player is facing for positional tracking
+ float multiplier = (vr_positional_factor->value) / (cl_forwardspeed->value *
+ ((pOffTrackedRemoteNew->Buttons & ovrButton_Trigger) ? 1.5f : 1.0f));
+
+ vec2_t v;
+ rotateAboutOrigin(-positionDeltaThisFrame[0] * multiplier,
+ positionDeltaThisFrame[2] * multiplier, /*cl.refdef.viewangles[YAW]*/ - hmdorientation[YAW], v);
+ positional_movementSideways = v[0];
+ positional_movementForward = v[1];
+
+ ALOGV(" positional_movementSideways: %f, positional_movementForward: %f",
+ positional_movementSideways,
+ positional_movementForward);
+
+ //Jump (B Button)
+ handleTrackedControllerButton(pDominantTrackedRemoteNew,
+ pDominantTrackedRemoteOld, domButton2, K_SPACE);
+
+ //We need to record if we have started firing primary so that releasing trigger will stop firing, if user has pushed grip
+ //in meantime, then it wouldn't stop the gun firing and it would get stuck
+ static bool firingPrimary = false;
+
+ {
+ //Fire Primary
+ if ((pDominantTrackedRemoteNew->Buttons & ovrButton_Trigger) !=
+ (pDominantTrackedRemoteOld->Buttons & ovrButton_Trigger)) {
+
+ firingPrimary = (pDominantTrackedRemoteNew->Buttons & ovrButton_Trigger);
+
+ if (inventoryManagementMode)
+ {
+ if (firingPrimary)
+ sendButtonActionSimple("invuse");
+ }
+ else
+ {
+ sendButtonAction("+attack", firingPrimary);
+ }
+ }
+ }
+
+ //Duck with A
+ if ((pDominantTrackedRemoteNew->Buttons & domButton1) !=
+ (pDominantTrackedRemoteOld->Buttons & domButton1) &&
+ ducked != DUCK_CROUCHED) {
+ ducked = (pDominantTrackedRemoteNew->Buttons & domButton1) ? DUCK_BUTTON : DUCK_NOTDUCKED;
+ sendButtonAction("+movedown", (pDominantTrackedRemoteNew->Buttons & domButton1));
+ }
+
+ //Weapon/Inventory Chooser
+ static qboolean itemSwitched = false;
+ if (between(-0.2f, pDominantTrackedRemoteNew->Joystick.x, 0.2f) &&
+ (between(0.8f, pDominantTrackedRemoteNew->Joystick.y, 1.0f) ||
+ between(-1.0f, pDominantTrackedRemoteNew->Joystick.y, -0.8f)))
+ {
+ if (!itemSwitched) {
+ if (between(0.8f, pDominantTrackedRemoteNew->Joystick.y, 1.0f))
+ {
+ if (inventoryManagementMode)
+ {
+ sendButtonActionSimple("invprev");
+ }
+ else
+ {
+ sendButtonActionSimple("weapprev");
+ }
+
+ }
+ else
+ {
+ if (inventoryManagementMode)
+ {
+ sendButtonActionSimple("invnext");
+ }
+ else
+ {
+ sendButtonActionSimple("weapnext");
+ }
+ }
+ itemSwitched = true;
+ }
+ } else {
+ itemSwitched = false;
+ }
+ }
+
+ //Left-hand specific stuff
+ {
+ ALOGV(" Left-Controller-Position: %f, %f, %f",
+ pOffTracking->HeadPose.Pose.Position.x,
+ pOffTracking->HeadPose.Pose.Position.y,
+ pOffTracking->HeadPose.Pose.Position.z);
+
+ //Laser-sight
+ if ((pDominantTrackedRemoteNew->Buttons & ovrButton_Joystick) !=
+ (pDominantTrackedRemoteOld->Buttons & ovrButton_Joystick)
+ && (pDominantTrackedRemoteNew->Buttons & ovrButton_Joystick)) {
+
+ if (vr_lasersight->value != 0.0)
+ {
+ Cvar_ForceSet("vr_lasersight", "0.0");
+ } else {
+ Cvar_ForceSet("vr_lasersight", "1.0");
+ }
+ }
+
+ //Apply a filter and quadratic scaler so small movements are easier to make
+ float dist = length(pOffTrackedRemoteNew->Joystick.x, pOffTrackedRemoteNew->Joystick.y);
+ float nlf = nonLinearFilter(dist);
+ float x = nlf * pOffTrackedRemoteNew->Joystick.x;
+ float y = nlf * pOffTrackedRemoteNew->Joystick.y;
+
+ player_moving = (fabs(x) + fabs(y)) > 0.05f;
+
+ //Adjust to be off-hand controller oriented
+ vec2_t v;
+ rotateAboutOrigin(x, y, controllerYawHeading, v);
+
+ remote_movementSideways = v[0];
+ remote_movementForward = v[1];
+ ALOGV(" remote_movementSideways: %f, remote_movementForward: %f",
+ remote_movementSideways,
+ remote_movementForward);
+
+
+ //show help computer while X/A pressed
+ if ((pOffTrackedRemoteNew->Buttons & offButton1) !=
+ (pOffTrackedRemoteOld->Buttons & offButton1)) {
+ sendButtonActionSimple("cmd help");
+ }
+
+
+ //Use (Action)
+ if ((pOffTrackedRemoteNew->Buttons & ovrButton_Joystick) !=
+ (pOffTrackedRemoteOld->Buttons & ovrButton_Joystick)
+ && (pOffTrackedRemoteNew->Buttons & ovrButton_Joystick)) {
+
+ //If cheats enabled, give all weapons/pickups to player
+ if (sv_cheats->value == 1.0f) {
+ Cbuf_AddText("give all\n");
+ }
+
+ }
+
+ //We need to record if we have started firing primary so that releasing trigger will stop definitely firing, if user has pushed grip
+ //in meantime, then it wouldn't stop the gun firing and it would get stuck
+ static bool firingPrimary = false;
+
+ //Run
+ handleTrackedControllerButton(pOffTrackedRemoteNew,
+ pOffTrackedRemoteOld,
+ ovrButton_Trigger, K_SHIFT);
+
+ static int increaseSnap = true;
+ if (pDominantTrackedRemoteNew->Joystick.x > 0.6f)
+ {
+ if (increaseSnap)
+ {
+ snapTurn -= vr_snapturn_angle->value;
+ if (vr_snapturn_angle->value > 10.0f) {
+ increaseSnap = false;
+ }
+
+ if (snapTurn < -180.0f)
+ {
+ snapTurn += 360.f;
+ }
+ }
+ } else if (pDominantTrackedRemoteNew->Joystick.x < 0.4f) {
+ increaseSnap = true;
+ }
+
+ static int decreaseSnap = true;
+ if (pDominantTrackedRemoteNew->Joystick.x < -0.6f)
+ {
+ if (decreaseSnap)
+ {
+ snapTurn += vr_snapturn_angle->value;
+
+ //If snap turn configured for less than 10 degrees
+ if (vr_snapturn_angle->value > 10.0f) {
+ decreaseSnap = false;
+ }
+
+ if (snapTurn > 180.0f)
+ {
+ snapTurn -= 360.f;
+ }
+ }
+ } else if (pDominantTrackedRemoteNew->Joystick.x > -0.4f)
+ {
+ decreaseSnap = true;
+ }
+ }
+ }
+
+ //Save state
+ rightTrackedRemoteState_old = rightTrackedRemoteState_new;
+ leftTrackedRemoteState_old = leftTrackedRemoteState_new;
+}
\ No newline at end of file
diff --git a/Projects/Android/jni/RTCWVR/argtable3.c b/Projects/Android/jni/RTCWVR/argtable3.c
new file mode 100644
index 0000000..d031bd2
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/argtable3.c
@@ -0,0 +1,5019 @@
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include "argtable3.h"
+
+// On Windows isspace crashes app in case of using Unicode character set and string to be above ASCII
+// so you have to use _istspace instead of space
+#ifdef UNICODE
+#include
+ #define ISSPACE _istspace
+#else
+ #define ISSPACE isspace
+#endif
+
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 2013 Tom G. Huang
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#ifndef ARG_UTILS_H
+#define ARG_UTILS_H
+
+#define ARG_ENABLE_TRACE 0
+#define ARG_ENABLE_LOG 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum
+{
+ EMINCOUNT = 1,
+ EMAXCOUNT,
+ EBADINT,
+ // The same name define EOVERFLOW in errno.h on windows platform
+#ifdef __STDC_WANT_SECURE_LIB__
+ EOVERFLOW_,
+#else
+ EOVERFLOW,
+#endif
+ EBADDOUBLE,
+ EBADDATE,
+ EREGNOMATCH
+};
+
+
+#if defined(_MSC_VER)
+#define ARG_TRACE(x) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { if (ARG_ENABLE_TRACE) dbg_printf x; } while (0) \
+ __pragma(warning(pop))
+
+#define ARG_LOG(x) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { if (ARG_ENABLE_LOG) dbg_printf x; } while (0) \
+ __pragma(warning(pop))
+#else
+#define ARG_TRACE(x) \
+ do { if (ARG_ENABLE_TRACE) dbg_printf x; } while (0)
+
+#define ARG_LOG(x) \
+ do { if (ARG_ENABLE_LOG) dbg_printf x; } while (0)
+#endif
+
+extern void dbg_printf(const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+#include
+
+
+void dbg_printf(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+}
+
+/* $Id: getopt.h,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $ */
+/* $OpenBSD: getopt.h,v 1.1 2002/12/03 20:24:29 millert Exp $ */
+/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _GETOPT_H_
+#define _GETOPT_H_
+
+#if 0
+#include
+#endif
+
+/*
+ * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions
+ */
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+struct option {
+ /* name of long option */
+ const char *name;
+ /*
+ * one of no_argument, required_argument, and optional_argument:
+ * whether option takes an argument
+ */
+ int has_arg;
+ /* if not NULL, set *flag to val when option found */
+ int *flag;
+ /* if flag not NULL, value to set *flag to; else return value */
+ int val;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int getopt_long(int, char * const *, const char *,
+ const struct option *, int *);
+int getopt_long_only(int, char * const *, const char *,
+ const struct option *, int *);
+#ifndef _GETOPT_DEFINED
+#define _GETOPT_DEFINED
+int getopt(int, char * const *, const char *);
+int getsubopt(char **, char * const *, char **);
+
+extern char *optarg; /* getopt(3) external variables */
+extern int opterr;
+extern int optind;
+extern int optopt;
+extern int optreset;
+extern char *suboptarg; /* getsubopt(3) external variable */
+#endif /* _GETOPT_DEFINED */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !_GETOPT_H_ */
+/* $Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $ */
+/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */
+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
+
+/*
+ * Copyright (c) 2002 Todd C. Miller
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+#ifndef lint
+static const char rcsid[]="$Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $";
+#endif /* lint */
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if 0
+#include
+#endif
+#include
+#include
+#include
+
+
+#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
+
+#ifdef REPLACE_GETOPT
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = '?'; /* character checked for validity */
+int optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+#endif
+
+#define PRINT_ERROR ((opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
+#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
+#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
+
+/* return values */
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define EMSG ""
+
+static int getopt_internal(int, char * const *, const char *,
+ const struct option *, int *, int);
+static int parse_long_options(char * const *, const char *,
+ const struct option *, int *, int);
+static int gcd(int, int);
+static void permute_args(int, int, int, char * const *);
+
+static char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1; /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptchar[] = "unknown option -- %c";
+static const char illoptstring[] = "unknown option -- %s";
+
+
+
+#ifdef _WIN32
+
+/* Windows needs warnx(). We change the definition though:
+ * 1. (another) global is defined, opterrmsg, which holds the error message
+ * 2. errors are always printed out on stderr w/o the program name
+ * Note that opterrmsg always gets set no matter what opterr is set to. The
+ * error message will not be printed if opterr is 0 as usual.
+ */
+
+#include
+#include
+
+#define MAX_OPTER_MSG_SIZE 128
+
+extern char opterrmsg[MAX_OPTER_MSG_SIZE];
+char opterrmsg[MAX_OPTER_MSG_SIZE]; /* buffer for the last error message */
+
+static void warnx(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ /*
+ Make sure opterrmsg is always zero-terminated despite the _vsnprintf()
+ implementation specifics and manually suppress the warning.
+ */
+ memset(opterrmsg, 0, sizeof opterrmsg);
+ if (fmt != NULL)
+#ifdef __STDC_WANT_SECURE_LIB__
+ _vsnprintf_s(opterrmsg, MAX_OPTER_MSG_SIZE, sizeof(opterrmsg) - 1, fmt, ap);
+#else
+ _vsnprintf(opterrmsg, sizeof(opterrmsg) - 1, fmt, ap);
+#endif
+ va_end(ap);
+
+#pragma warning(suppress: 6053)
+ fprintf(stderr, "%s\n", opterrmsg);
+}
+
+#else
+#include
+#endif /*_WIN32*/
+
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(int a, int b)
+{
+ int c;
+
+ c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+
+ return (b);
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(int panonopt_start, int panonopt_end, int opt_end,
+ char * const *nargv)
+{
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+ char *swap;
+
+ /*
+ * compute lengths of blocks and number and size of cycles
+ */
+ nnonopts = panonopt_end - panonopt_start;
+ nopts = opt_end - panonopt_end;
+ ncycle = gcd(nnonopts, nopts);
+ cyclelen = (opt_end - panonopt_start) / ncycle;
+
+ for (i = 0; i < ncycle; i++) {
+ cstart = panonopt_end+i;
+ pos = cstart;
+ for (j = 0; j < cyclelen; j++) {
+ if (pos >= panonopt_end)
+ pos -= nnonopts;
+ else
+ pos += nopts;
+ swap = nargv[pos];
+ /* LINTED const cast */
+ ((char **) nargv)[pos] = nargv[cstart];
+ /* LINTED const cast */
+ ((char **)nargv)[cstart] = swap;
+ }
+ }
+}
+
+/*
+ * parse_long_options --
+ * Parse long options in argc/argv argument vector.
+ * Returns -1 if short_too is set and the option does not match long_options.
+ */
+static int
+parse_long_options(char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int short_too)
+{
+ char *current_argv, *has_equal;
+ size_t current_argv_len;
+ int i, match;
+
+ current_argv = place;
+ match = -1;
+
+ optind++;
+
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ /* argument found (--option=arg) */
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ /* find matching long option */
+ if (strncmp(current_argv, long_options[i].name,
+ current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ /* exact match */
+ match = i;
+ break;
+ }
+ /*
+ * If this is a known short option, don't allow
+ * a partial match of a single character.
+ */
+ if (short_too && current_argv_len == 1)
+ continue;
+
+ if (match == -1) /* partial match */
+ match = i;
+ else {
+ /* ambiguous abbreviation */
+ if (PRINT_ERROR)
+ warnx(ambig, (int)current_argv_len,
+ current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ }
+ if (match != -1) { /* option found */
+ if (long_options[match].has_arg == no_argument
+ && has_equal) {
+ if (PRINT_ERROR)
+ warnx(noarg, (int)current_argv_len,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ return (BADARG);
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else if (long_options[match].has_arg ==
+ required_argument) {
+ /*
+ * optional argument doesn't use next nargv
+ */
+ optarg = nargv[optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (optarg == NULL)) {
+ /*
+ * Missing argument; leading ':' indicates no error
+ * should be generated.
+ */
+ if (PRINT_ERROR)
+ warnx(recargstring,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ --optind;
+ return (BADARG);
+ }
+ } else { /* unknown option */
+ if (short_too) {
+ --optind;
+ return (-1);
+ }
+ if (PRINT_ERROR)
+ warnx(illoptstring, current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ if (idx)
+ *idx = match;
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ return (0);
+ } else
+ return (long_options[match].val);
+}
+
+/*
+ * getopt_internal --
+ * Parse argc/argv argument vector. Called by user level routines.
+ */
+static int
+getopt_internal(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int flags)
+{
+ char *oli; /* option letter list index */
+ int optchar, short_too;
+ static int posixly_correct = -1;
+#ifdef __STDC_WANT_SECURE_LIB__
+ char* buffer = NULL;
+ size_t buffer_size = 0;
+ errno_t err = 0;
+#endif
+
+ if (options == NULL)
+ return (-1);
+
+ /*
+ * Disable GNU extensions if POSIXLY_CORRECT is set or options
+ * string begins with a '+'.
+ */
+
+#ifdef __STDC_WANT_SECURE_LIB__
+ if (posixly_correct == -1) {
+ err = _dupenv_s(&buffer, &buffer_size, "POSIXLY_CORRECT") == 0;
+ posixly_correct = buffer != NULL;
+ if(buffer != NULL && err == 0) {
+ free(buffer);
+ }
+ }
+#else
+ if (posixly_correct == -1)
+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+#endif
+ if (posixly_correct || *options == '+')
+ flags &= ~FLAG_PERMUTE;
+ else if (*options == '-')
+ flags |= FLAG_ALLARGS;
+ if (*options == '+' || *options == '-')
+ options++;
+
+ /*
+ * XXX Some GNU programs (like cvs) set optind to 0 instead of
+ * XXX using optreset. Work around this braindamage.
+ */
+ if (optind == 0)
+ optind = optreset = 1;
+
+ optarg = NULL;
+ if (optreset)
+ nonopt_start = nonopt_end = -1;
+start:
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc) { /* end of argument vector */
+ place = EMSG;
+ if (nonopt_end != -1) {
+ /* do permutation, if we have to */
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ else if (nonopt_start != -1) {
+ /*
+ * If we skipped non-options, set optind
+ * to the first of them.
+ */
+ optind = nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ if (*(place = nargv[optind]) != '-' ||
+ (place[1] == '\0' && strchr(options, '-') == NULL)) {
+ place = EMSG; /* found non-option */
+ if (flags & FLAG_ALLARGS) {
+ /*
+ * GNU extension:
+ * return non-option as argument to option 1
+ */
+ optarg = nargv[optind++];
+ return (INORDER);
+ }
+ if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If no permutation wanted, stop parsing
+ * at first non-option.
+ */
+ return (-1);
+ }
+ /* do permutation */
+ if (nonopt_start == -1)
+ nonopt_start = optind;
+ else if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ nonopt_start = optind -
+ (nonopt_end - nonopt_start);
+ nonopt_end = -1;
+ }
+ optind++;
+ /* process next argument */
+ goto start;
+ }
+ if (nonopt_start != -1 && nonopt_end == -1)
+ nonopt_end = optind;
+
+ /*
+ * If we have "-" do nothing, if "--" we are done.
+ */
+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+ optind++;
+ place = EMSG;
+ /*
+ * We found an option (--), so if we skipped
+ * non-options, we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ }
+
+ /*
+ * Check long options if:
+ * 1) we were passed some
+ * 2) the arg is not just "-"
+ * 3) either the arg starts with -- we are getopt_long_only()
+ */
+ if (long_options != NULL && place != nargv[optind] &&
+ (*place == '-' || (flags & FLAG_LONGONLY))) {
+ short_too = 0;
+ if (*place == '-')
+ place++; /* --foo long option */
+ else if (*place != ':' && strchr(options, *place) != NULL)
+ short_too = 1; /* could be short option too */
+
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, short_too);
+ if (optchar != -1) {
+ place = EMSG;
+ return (optchar);
+ }
+ }
+
+ if ((optchar = (int)*place++) == (int)':' ||
+ (optchar == (int)'-' && *place != '\0') ||
+ (oli = strchr(options, optchar)) == NULL) {
+ /*
+ * If the user specified "-" and '-' isn't listed in
+ * options, return -1 (non-option) as per POSIX.
+ * Otherwise, it is an unknown option character (or ':').
+ */
+ if (optchar == (int)'-' && *place == '\0')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (PRINT_ERROR)
+ warnx(illoptchar, optchar);
+ optopt = optchar;
+ return (BADCH);
+ }
+ if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
+ /* -W long-option */
+ if (*place) /* no space */
+ /* NOTHING */;
+ else if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else /* white space */
+ place = nargv[optind];
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, 0);
+ place = EMSG;
+ return (optchar);
+ }
+ if (*++oli != ':') { /* doesn't take argument */
+ if (!*place)
+ ++optind;
+ } else { /* takes (optional) argument */
+ optarg = NULL;
+ if (*place) /* no white space */
+ optarg = place;
+ else if (oli[1] != ':') { /* arg not optional */
+ if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else
+ optarg = nargv[optind];
+ }
+ place = EMSG;
+ ++optind;
+ }
+ /* dump back option letter */
+ return (optchar);
+}
+
+#ifdef REPLACE_GETOPT
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the BSD getopt]
+ */
+int
+getopt(int nargc, char * const *nargv, const char *options)
+{
+
+ /*
+ * We don't pass FLAG_PERMUTE to getopt_internal() since
+ * the BSD getopt(3) (unlike GNU) has never done this.
+ *
+ * Furthermore, since many privileged programs call getopt()
+ * before dropping privileges it makes sense to keep things
+ * as simple (and bug-free) as possible.
+ */
+ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
+}
+#endif /* REPLACE_GETOPT */
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE));
+}
+
+/*
+ * getopt_long_only --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long_only(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE|FLAG_LONGONLY));
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+#include
+
+#include "argtable3.h"
+
+
+char * arg_strptime(const char *buf, const char *fmt, struct tm *tm);
+
+
+static void arg_date_resetfn(struct arg_date *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+static int arg_date_scanfn(struct arg_date *parent, const char *argval)
+{
+ int errorcode = 0;
+
+ if (parent->count == parent->hdr.maxcount)
+ {
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* no argument value was given, leave parent->tmval[] unaltered but still count it */
+ parent->count++;
+ }
+ else
+ {
+ const char *pend;
+ struct tm tm = parent->tmval[parent->count];
+
+ /* parse the given argument value, store result in parent->tmval[] */
+ pend = arg_strptime(argval, parent->format, &tm);
+ if (pend && pend[0] == '\0')
+ parent->tmval[parent->count++] = tm;
+ else
+ errorcode = EBADDATE;
+ }
+
+ ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static int arg_date_checkfn(struct arg_date *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+
+ ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static void arg_date_errorfn(
+ struct arg_date *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ case EBADDATE:
+ {
+ struct tm tm;
+ char buff[200];
+
+ fprintf(fp, "illegal timestamp format \"%s\"\n", argval);
+ memset(&tm, 0, sizeof(tm));
+ arg_strptime("1999-12-31 23:59:59", "%F %H:%M:%S", &tm);
+ strftime(buff, sizeof(buff), parent->format, &tm);
+ printf("correct format is \"%s\"\n", buff);
+ break;
+ }
+ }
+}
+
+
+struct arg_date * arg_date0(
+ const char * shortopts,
+ const char * longopts,
+ const char * format,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_daten(shortopts, longopts, format, datatype, 0, 1, glossary);
+}
+
+
+struct arg_date * arg_date1(
+ const char * shortopts,
+ const char * longopts,
+ const char * format,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_daten(shortopts, longopts, format, datatype, 1, 1, glossary);
+}
+
+
+struct arg_date * arg_daten(
+ const char * shortopts,
+ const char * longopts,
+ const char * format,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_date *result;
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ /* default time format is the national date format for the locale */
+ if (!format)
+ format = "%x";
+
+ nbytes = sizeof(struct arg_date) /* storage for struct arg_date */
+ + maxcount * sizeof(struct tm); /* storage for tmval[maxcount] array */
+
+ /* allocate storage for the arg_date struct + tmval[] array. */
+ /* we use calloc because we want the tmval[] array zero filled. */
+ result = (struct arg_date *)calloc(1, nbytes);
+ if (result)
+ {
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = datatype ? datatype : format;
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_date_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_date_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_date_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_date_errorfn;
+
+ /* store the tmval[maxcount] array immediately after the arg_date struct */
+ result->tmval = (struct tm *)(result + 1);
+
+ /* init the remaining arg_date member variables */
+ result->count = 0;
+ result->format = format;
+ }
+
+ ARG_TRACE(("arg_daten() returns %p\n", result));
+ return result;
+}
+
+
+/*-
+ * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code was contributed to The NetBSD Foundation by Klaus Klein.
+ * Heavily optimised by David Laight
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+#include
+#include
+
+/*
+ * We do not implement alternate representations. However, we always
+ * check whether a given modifier is allowed for a certain conversion.
+ */
+#define ALT_E 0x01
+#define ALT_O 0x02
+#define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
+#define TM_YEAR_BASE (1900)
+
+static int conv_num(const char * *, int *, int, int);
+
+static const char *day[7] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday"
+};
+
+static const char *abday[7] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static const char *mon[12] = {
+ "January", "February", "March", "April", "May", "June", "July",
+ "August", "September", "October", "November", "December"
+};
+
+static const char *abmon[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static const char *am_pm[2] = {
+ "AM", "PM"
+};
+
+
+static int arg_strcasecmp(const char *s1, const char *s2)
+{
+ const unsigned char *us1 = (const unsigned char *)s1;
+ const unsigned char *us2 = (const unsigned char *)s2;
+ while (tolower(*us1) == tolower(*us2++))
+ if (*us1++ == '\0')
+ return 0;
+
+ return tolower(*us1) - tolower(*--us2);
+}
+
+
+static int arg_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+ if (n != 0)
+ {
+ const unsigned char *us1 = (const unsigned char *)s1;
+ const unsigned char *us2 = (const unsigned char *)s2;
+ do
+ {
+ if (tolower(*us1) != tolower(*us2++))
+ return tolower(*us1) - tolower(*--us2);
+
+ if (*us1++ == '\0')
+ break;
+ } while (--n != 0);
+ }
+
+ return 0;
+}
+
+
+char * arg_strptime(const char *buf, const char *fmt, struct tm *tm)
+{
+ char c;
+ const char *bp;
+ size_t len = 0;
+ int alt_format, i, split_year = 0;
+
+ bp = buf;
+
+ while ((c = *fmt) != '\0') {
+ /* Clear `alternate' modifier prior to new conversion. */
+ alt_format = 0;
+
+ /* Eat up white-space. */
+ if (ISSPACE(c)) {
+ while (ISSPACE(*bp))
+ bp++;
+
+ fmt++;
+ continue;
+ }
+
+ if ((c = *fmt++) != '%')
+ goto literal;
+
+
+again:
+ switch (c = *fmt++)
+ {
+ case '%': /* "%%" is converted to "%". */
+literal:
+ if (c != *bp++)
+ return (0);
+ break;
+
+ /*
+ * "Alternative" modifiers. Just set the appropriate flag
+ * and start over again.
+ */
+ case 'E': /* "%E?" alternative conversion modifier. */
+ LEGAL_ALT(0);
+ alt_format |= ALT_E;
+ goto again;
+
+ case 'O': /* "%O?" alternative conversion modifier. */
+ LEGAL_ALT(0);
+ alt_format |= ALT_O;
+ goto again;
+
+ /*
+ * "Complex" conversion rules, implemented through recursion.
+ */
+ case 'c': /* Date and time, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ bp = arg_strptime(bp, "%x %X", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'D': /* The date as "%m/%d/%y". */
+ LEGAL_ALT(0);
+ bp = arg_strptime(bp, "%m/%d/%y", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'R': /* The time as "%H:%M". */
+ LEGAL_ALT(0);
+ bp = arg_strptime(bp, "%H:%M", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'r': /* The time in 12-hour clock representation. */
+ LEGAL_ALT(0);
+ bp = arg_strptime(bp, "%I:%M:%S %p", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'T': /* The time as "%H:%M:%S". */
+ LEGAL_ALT(0);
+ bp = arg_strptime(bp, "%H:%M:%S", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'X': /* The time, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ bp = arg_strptime(bp, "%H:%M:%S", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ case 'x': /* The date, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ bp = arg_strptime(bp, "%m/%d/%y", tm);
+ if (!bp)
+ return (0);
+ break;
+
+ /*
+ * "Elementary" conversion rules.
+ */
+ case 'A': /* The day of week, using the locale's form. */
+ case 'a':
+ LEGAL_ALT(0);
+ for (i = 0; i < 7; i++) {
+ /* Full name. */
+ len = strlen(day[i]);
+ if (arg_strncasecmp(day[i], bp, len) == 0)
+ break;
+
+ /* Abbreviated name. */
+ len = strlen(abday[i]);
+ if (arg_strncasecmp(abday[i], bp, len) == 0)
+ break;
+ }
+
+ /* Nothing matched. */
+ if (i == 7)
+ return (0);
+
+ tm->tm_wday = i;
+ bp += len;
+ break;
+
+ case 'B': /* The month, using the locale's form. */
+ case 'b':
+ case 'h':
+ LEGAL_ALT(0);
+ for (i = 0; i < 12; i++) {
+ /* Full name. */
+ len = strlen(mon[i]);
+ if (arg_strncasecmp(mon[i], bp, len) == 0)
+ break;
+
+ /* Abbreviated name. */
+ len = strlen(abmon[i]);
+ if (arg_strncasecmp(abmon[i], bp, len) == 0)
+ break;
+ }
+
+ /* Nothing matched. */
+ if (i == 12)
+ return (0);
+
+ tm->tm_mon = i;
+ bp += len;
+ break;
+
+ case 'C': /* The century number. */
+ LEGAL_ALT(ALT_E);
+ if (!(conv_num(&bp, &i, 0, 99)))
+ return (0);
+
+ if (split_year) {
+ tm->tm_year = (tm->tm_year % 100) + (i * 100);
+ } else {
+ tm->tm_year = i * 100;
+ split_year = 1;
+ }
+ break;
+
+ case 'd': /* The day of month. */
+ case 'e':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_mday, 1, 31)))
+ return (0);
+ break;
+
+ case 'k': /* The hour (24-hour clock representation). */
+ LEGAL_ALT(0);
+ /* FALLTHROUGH */
+ case 'H':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_hour, 0, 23)))
+ return (0);
+ break;
+
+ case 'l': /* The hour (12-hour clock representation). */
+ LEGAL_ALT(0);
+ /* FALLTHROUGH */
+ case 'I':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_hour, 1, 12)))
+ return (0);
+ if (tm->tm_hour == 12)
+ tm->tm_hour = 0;
+ break;
+
+ case 'j': /* The day of year. */
+ LEGAL_ALT(0);
+ if (!(conv_num(&bp, &i, 1, 366)))
+ return (0);
+ tm->tm_yday = i - 1;
+ break;
+
+ case 'M': /* The minute. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_min, 0, 59)))
+ return (0);
+ break;
+
+ case 'm': /* The month. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &i, 1, 12)))
+ return (0);
+ tm->tm_mon = i - 1;
+ break;
+
+ case 'p': /* The locale's equivalent of AM/PM. */
+ LEGAL_ALT(0);
+ /* AM? */
+ if (arg_strcasecmp(am_pm[0], bp) == 0) {
+ if (tm->tm_hour > 11)
+ return (0);
+
+ bp += strlen(am_pm[0]);
+ break;
+ }
+ /* PM? */
+ else if (arg_strcasecmp(am_pm[1], bp) == 0) {
+ if (tm->tm_hour > 11)
+ return (0);
+
+ tm->tm_hour += 12;
+ bp += strlen(am_pm[1]);
+ break;
+ }
+
+ /* Nothing matched. */
+ return (0);
+
+ case 'S': /* The seconds. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_sec, 0, 61)))
+ return (0);
+ break;
+
+ case 'U': /* The week of year, beginning on sunday. */
+ case 'W': /* The week of year, beginning on monday. */
+ LEGAL_ALT(ALT_O);
+ /*
+ * XXX This is bogus, as we can not assume any valid
+ * information present in the tm structure at this
+ * point to calculate a real value, so just check the
+ * range for now.
+ */
+ if (!(conv_num(&bp, &i, 0, 53)))
+ return (0);
+ break;
+
+ case 'w': /* The day of week, beginning on sunday. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_wday, 0, 6)))
+ return (0);
+ break;
+
+ case 'Y': /* The year. */
+ LEGAL_ALT(ALT_E);
+ if (!(conv_num(&bp, &i, 0, 9999)))
+ return (0);
+
+ tm->tm_year = i - TM_YEAR_BASE;
+ break;
+
+ case 'y': /* The year within 100 years of the epoch. */
+ LEGAL_ALT(ALT_E | ALT_O);
+ if (!(conv_num(&bp, &i, 0, 99)))
+ return (0);
+
+ if (split_year) {
+ tm->tm_year = ((tm->tm_year / 100) * 100) + i;
+ break;
+ }
+ split_year = 1;
+ if (i <= 68)
+ tm->tm_year = i + 2000 - TM_YEAR_BASE;
+ else
+ tm->tm_year = i + 1900 - TM_YEAR_BASE;
+ break;
+
+ /*
+ * Miscellaneous conversions.
+ */
+ case 'n': /* Any kind of white-space. */
+ case 't':
+ LEGAL_ALT(0);
+ while (ISSPACE(*bp))
+ bp++;
+ break;
+
+
+ default: /* Unknown/unsupported conversion. */
+ return (0);
+ }
+
+
+ }
+
+ /* LINTED functional specification */
+ return ((char *)bp);
+}
+
+
+static int conv_num(const char * *buf, int *dest, int llim, int ulim)
+{
+ int result = 0;
+
+ /* The limit also determines the number of valid digits. */
+ int rulim = ulim;
+
+ if (**buf < '0' || **buf > '9')
+ return (0);
+
+ do {
+ result *= 10;
+ result += *(*buf)++ - '0';
+ rulim /= 10;
+ } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
+
+ if (result < llim || result > ulim)
+ return (0);
+
+ *dest = result;
+ return (1);
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+
+#include "argtable3.h"
+
+
+static void arg_dbl_resetfn(struct arg_dbl *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+static int arg_dbl_scanfn(struct arg_dbl *parent, const char *argval)
+{
+ int errorcode = 0;
+
+ if (parent->count == parent->hdr.maxcount)
+ {
+ /* maximum number of arguments exceeded */
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* a valid argument with no argument value was given. */
+ /* This happens when an optional argument value was invoked. */
+ /* leave parent argument value unaltered but still count the argument. */
+ parent->count++;
+ }
+ else
+ {
+ double val;
+ char *end;
+
+ /* extract double from argval into val */
+ val = strtod(argval, &end);
+
+ /* if success then store result in parent->dval[] array otherwise return error*/
+ if (*end == 0)
+ parent->dval[parent->count++] = val;
+ else
+ errorcode = EBADDOUBLE;
+ }
+
+ ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static int arg_dbl_checkfn(struct arg_dbl *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+
+ ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static void arg_dbl_errorfn(
+ struct arg_dbl *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ case EBADDOUBLE:
+ fprintf(fp, "invalid argument \"%s\" to option ", argval);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+ }
+}
+
+
+struct arg_dbl * arg_dbl0(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_dbln(shortopts, longopts, datatype, 0, 1, glossary);
+}
+
+
+struct arg_dbl * arg_dbl1(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_dbln(shortopts, longopts, datatype, 1, 1, glossary);
+}
+
+
+struct arg_dbl * arg_dbln(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_dbl *result;
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ nbytes = sizeof(struct arg_dbl) /* storage for struct arg_dbl */
+ + (maxcount + 1) * sizeof(double); /* storage for dval[maxcount] array plus one extra for padding to memory boundary */
+
+ result = (struct arg_dbl *)malloc(nbytes);
+ if (result)
+ {
+ size_t addr;
+ size_t rem;
+
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = datatype ? datatype : "";
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_dbl_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_dbl_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_dbl_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_dbl_errorfn;
+
+ /* Store the dval[maxcount] array on the first double boundary that
+ * immediately follows the arg_dbl struct. We do the memory alignment
+ * purely for SPARC and Motorola systems. They require floats and
+ * doubles to be aligned on natural boundaries.
+ */
+ addr = (size_t)(result + 1);
+ rem = addr % sizeof(double);
+ result->dval = (double *)(addr + sizeof(double) - rem);
+ ARG_TRACE(("addr=%p, dval=%p, sizeof(double)=%d rem=%d\n", addr, result->dval, (int)sizeof(double), (int)rem));
+
+ result->count = 0;
+ }
+
+ ARG_TRACE(("arg_dbln() returns %p\n", result));
+ return result;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+
+#include "argtable3.h"
+
+
+static void arg_end_resetfn(struct arg_end *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+static void arg_end_errorfn(
+ void *parent,
+ FILE *fp,
+ int error,
+ const char *argval,
+ const char *progname)
+{
+ /* suppress unreferenced formal parameter warning */
+ (void)parent;
+
+ progname = progname ? progname : "";
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(error)
+ {
+ case ARG_ELIMIT:
+ fputs("too many errors to display", fp);
+ break;
+ case ARG_EMALLOC:
+ fputs("insufficent memory", fp);
+ break;
+ case ARG_ENOMATCH:
+ fprintf(fp, "unexpected argument \"%s\"", argval);
+ break;
+ case ARG_EMISSARG:
+ fprintf(fp, "option \"%s\" requires an argument", argval);
+ break;
+ case ARG_ELONGOPT:
+ fprintf(fp, "invalid option \"%s\"", argval);
+ break;
+ default:
+ fprintf(fp, "invalid option \"-%c\"", error);
+ break;
+ }
+
+ fputc('\n', fp);
+}
+
+
+struct arg_end * arg_end(int maxcount)
+{
+ size_t nbytes;
+ struct arg_end *result;
+
+ nbytes = sizeof(struct arg_end)
+ + maxcount * sizeof(int) /* storage for int error[maxcount] array*/
+ + maxcount * sizeof(void *) /* storage for void* parent[maxcount] array */
+ + maxcount * sizeof(char *); /* storage for char* argval[maxcount] array */
+
+ result = (struct arg_end *)malloc(nbytes);
+ if (result)
+ {
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_TERMINATOR;
+ result->hdr.shortopts = NULL;
+ result->hdr.longopts = NULL;
+ result->hdr.datatype = NULL;
+ result->hdr.glossary = NULL;
+ result->hdr.mincount = 1;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_end_resetfn;
+ result->hdr.scanfn = NULL;
+ result->hdr.checkfn = NULL;
+ result->hdr.errorfn = (arg_errorfn *)arg_end_errorfn;
+
+ /* store error[maxcount] array immediately after struct arg_end */
+ result->error = (int *)(result + 1);
+
+ /* store parent[maxcount] array immediately after error[] array */
+ result->parent = (void * *)(result->error + maxcount );
+
+ /* store argval[maxcount] array immediately after parent[] array */
+ result->argval = (const char * *)(result->parent + maxcount );
+ }
+
+ ARG_TRACE(("arg_end(%d) returns %p\n", maxcount, result));
+ return result;
+}
+
+
+void arg_print_errors(FILE * fp, struct arg_end * end, const char * progname)
+{
+ int i;
+ ARG_TRACE(("arg_errors()\n"));
+ for (i = 0; i < end->count; i++)
+ {
+ struct arg_hdr *errorparent = (struct arg_hdr *)(end->parent[i]);
+ if (errorparent->errorfn)
+ errorparent->errorfn(end->parent[i],
+ fp,
+ end->error[i],
+ end->argval[i],
+ progname);
+ }
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+#include
+
+#include "argtable3.h"
+
+#ifdef WIN32
+# define FILESEPARATOR1 '\\'
+# define FILESEPARATOR2 '/'
+#else
+# define FILESEPARATOR1 '/'
+# define FILESEPARATOR2 '/'
+#endif
+
+
+static void arg_file_resetfn(struct arg_file *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+/* Returns ptr to the base filename within *filename */
+static const char * arg_basename(const char *filename)
+{
+ const char *result = NULL, *result1, *result2;
+
+ /* Find the last occurrence of eother file separator character. */
+ /* Two alternative file separator chars are supported as legal */
+ /* file separators but not both together in the same filename. */
+ result1 = (filename ? strrchr(filename, FILESEPARATOR1) : NULL);
+ result2 = (filename ? strrchr(filename, FILESEPARATOR2) : NULL);
+
+ if (result2)
+ result = result2 + 1; /* using FILESEPARATOR2 (the alternative file separator) */
+
+ if (result1)
+ result = result1 + 1; /* using FILESEPARATOR1 (the preferred file separator) */
+
+ if (!result)
+ result = filename; /* neither file separator was found so basename is the whole filename */
+
+ /* special cases of "." and ".." are not considered basenames */
+ if (result && ( strcmp(".", result) == 0 || strcmp("..", result) == 0 ))
+ result = filename + strlen(filename);
+
+ return result;
+}
+
+
+/* Returns ptr to the file extension within *basename */
+static const char * arg_extension(const char *basename)
+{
+ /* find the last occurrence of '.' in basename */
+ const char *result = (basename ? strrchr(basename, '.') : NULL);
+
+ /* if no '.' was found then return pointer to end of basename */
+ if (basename && !result)
+ result = basename + strlen(basename);
+
+ /* special case: basenames with a single leading dot (eg ".foo") are not considered as true extensions */
+ if (basename && result == basename)
+ result = basename + strlen(basename);
+
+ /* special case: empty extensions (eg "foo.","foo..") are not considered as true extensions */
+ if (basename && result && result[1] == '\0')
+ result = basename + strlen(basename);
+
+ return result;
+}
+
+
+static int arg_file_scanfn(struct arg_file *parent, const char *argval)
+{
+ int errorcode = 0;
+
+ if (parent->count == parent->hdr.maxcount)
+ {
+ /* maximum number of arguments exceeded */
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* a valid argument with no argument value was given. */
+ /* This happens when an optional argument value was invoked. */
+ /* leave parent arguiment value unaltered but still count the argument. */
+ parent->count++;
+ }
+ else
+ {
+ parent->filename[parent->count] = argval;
+ parent->basename[parent->count] = arg_basename(argval);
+ parent->extension[parent->count] =
+ arg_extension(parent->basename[parent->count]); /* only seek extensions within the basename (not the file path)*/
+ parent->count++;
+ }
+
+ ARG_TRACE(("%s4:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static int arg_file_checkfn(struct arg_file *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+
+ ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static void arg_file_errorfn(
+ struct arg_file *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ default:
+ fprintf(fp, "unknown error at \"%s\"\n", argval);
+ }
+}
+
+
+struct arg_file * arg_file0(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_filen(shortopts, longopts, datatype, 0, 1, glossary);
+}
+
+
+struct arg_file * arg_file1(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_filen(shortopts, longopts, datatype, 1, 1, glossary);
+}
+
+
+struct arg_file * arg_filen(
+ const char * shortopts,
+ const char * longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_file *result;
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ nbytes = sizeof(struct arg_file) /* storage for struct arg_file */
+ + sizeof(char *) * maxcount /* storage for filename[maxcount] array */
+ + sizeof(char *) * maxcount /* storage for basename[maxcount] array */
+ + sizeof(char *) * maxcount; /* storage for extension[maxcount] array */
+
+ result = (struct arg_file *)malloc(nbytes);
+ if (result)
+ {
+ int i;
+
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.glossary = glossary;
+ result->hdr.datatype = datatype ? datatype : "";
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_file_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_file_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_file_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_file_errorfn;
+
+ /* store the filename,basename,extension arrays immediately after the arg_file struct */
+ result->filename = (const char * *)(result + 1);
+ result->basename = result->filename + maxcount;
+ result->extension = result->basename + maxcount;
+ result->count = 0;
+
+ /* foolproof the string pointers by initialising them with empty strings */
+ for (i = 0; i < maxcount; i++)
+ {
+ result->filename[i] = "";
+ result->basename[i] = "";
+ result->extension[i] = "";
+ }
+ }
+
+ ARG_TRACE(("arg_filen() returns %p\n", result));
+ return result;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+#include
+#include
+
+#include "argtable3.h"
+
+
+static void arg_int_resetfn(struct arg_int *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+/* strtol0x() is like strtol() except that the numeric string is */
+/* expected to be prefixed by "0X" where X is a user supplied char. */
+/* The string may optionally be prefixed by white space and + or - */
+/* as in +0X123 or -0X123. */
+/* Once the prefix has been scanned, the remainder of the numeric */
+/* string is converted using strtol() with the given base. */
+/* eg: to parse hex str="-0X12324", specify X='X' and base=16. */
+/* eg: to parse oct str="+0o12324", specify X='O' and base=8. */
+/* eg: to parse bin str="-0B01010", specify X='B' and base=2. */
+/* Failure of conversion is indicated by result where *endptr==str. */
+static long int strtol0X(const char * str,
+ const char * *endptr,
+ char X,
+ int base)
+{
+ long int val; /* stores result */
+ int s = 1; /* sign is +1 or -1 */
+ const char *ptr = str; /* ptr to current position in str */
+
+ /* skip leading whitespace */
+ while (ISSPACE(*ptr))
+ ptr++;
+ /* printf("1) %s\n",ptr); */
+
+ /* scan optional sign character */
+ switch (*ptr)
+ {
+ case '+':
+ ptr++;
+ s = 1;
+ break;
+ case '-':
+ ptr++;
+ s = -1;
+ break;
+ default:
+ s = 1;
+ break;
+ }
+ /* printf("2) %s\n",ptr); */
+
+ /* '0X' prefix */
+ if ((*ptr++) != '0')
+ {
+ /* printf("failed to detect '0'\n"); */
+ *endptr = str;
+ return 0;
+ }
+ /* printf("3) %s\n",ptr); */
+ if (toupper(*ptr++) != toupper(X))
+ {
+ /* printf("failed to detect '%c'\n",X); */
+ *endptr = str;
+ return 0;
+ }
+ /* printf("4) %s\n",ptr); */
+
+ /* attempt conversion on remainder of string using strtol() */
+ val = strtol(ptr, (char * *)endptr, base);
+ if (*endptr == ptr)
+ {
+ /* conversion failed */
+ *endptr = str;
+ return 0;
+ }
+
+ /* success */
+ return s * val;
+}
+
+
+/* Returns 1 if str matches suffix (case insensitive). */
+/* Str may contain trailing whitespace, but nothing else. */
+static int detectsuffix(const char *str, const char *suffix)
+{
+ /* scan pairwise through strings until mismatch detected */
+ while( toupper(*str) == toupper(*suffix) )
+ {
+ /* printf("'%c' '%c'\n", *str, *suffix); */
+
+ /* return 1 (success) if match persists until the string terminator */
+ if (*str == '\0')
+ return 1;
+
+ /* next chars */
+ str++;
+ suffix++;
+ }
+ /* printf("'%c' '%c' mismatch\n", *str, *suffix); */
+
+ /* return 0 (fail) if the matching did not consume the entire suffix */
+ if (*suffix != 0)
+ return 0; /* failed to consume entire suffix */
+
+ /* skip any remaining whitespace in str */
+ while (ISSPACE(*str))
+ str++;
+
+ /* return 1 (success) if we have reached end of str else return 0 (fail) */
+ return (*str == '\0') ? 1 : 0;
+}
+
+
+static int arg_int_scanfn(struct arg_int *parent, const char *argval)
+{
+ int errorcode = 0;
+
+ if (parent->count == parent->hdr.maxcount)
+ {
+ /* maximum number of arguments exceeded */
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* a valid argument with no argument value was given. */
+ /* This happens when an optional argument value was invoked. */
+ /* leave parent arguiment value unaltered but still count the argument. */
+ parent->count++;
+ }
+ else
+ {
+ long int val;
+ const char *end;
+
+ /* attempt to extract hex integer (eg: +0x123) from argval into val conversion */
+ val = strtol0X(argval, &end, 'X', 16);
+ if (end == argval)
+ {
+ /* hex failed, attempt octal conversion (eg +0o123) */
+ val = strtol0X(argval, &end, 'O', 8);
+ if (end == argval)
+ {
+ /* octal failed, attempt binary conversion (eg +0B101) */
+ val = strtol0X(argval, &end, 'B', 2);
+ if (end == argval)
+ {
+ /* binary failed, attempt decimal conversion with no prefix (eg 1234) */
+ val = strtol(argval, (char * *)&end, 10);
+ if (end == argval)
+ {
+ /* all supported number formats failed */
+ return EBADINT;
+ }
+ }
+ }
+ }
+
+ /* Safety check for integer overflow. WARNING: this check */
+ /* achieves nothing on machines where size(int)==size(long). */
+ if ( val > INT_MAX || val < INT_MIN )
+#ifdef __STDC_WANT_SECURE_LIB__
+ errorcode = EOVERFLOW_;
+#else
+ errorcode = EOVERFLOW;
+#endif
+
+ /* Detect any suffixes (KB,MB,GB) and multiply argument value appropriately. */
+ /* We need to be mindful of integer overflows when using such big numbers. */
+ if (detectsuffix(end, "KB")) /* kilobytes */
+ {
+ if ( val > (INT_MAX / 1024) || val < (INT_MIN / 1024) )
+#ifdef __STDC_WANT_SECURE_LIB__
+ errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */
+#else
+ errorcode = EOVERFLOW; /* Overflow would occur if we proceed */
+#endif
+ else
+ val *= 1024; /* 1KB = 1024 */
+ }
+ else if (detectsuffix(end, "MB")) /* megabytes */
+ {
+ if ( val > (INT_MAX / 1048576) || val < (INT_MIN / 1048576) )
+#ifdef __STDC_WANT_SECURE_LIB__
+ errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */
+#else
+ errorcode = EOVERFLOW; /* Overflow would occur if we proceed */
+#endif
+ else
+ val *= 1048576; /* 1MB = 1024*1024 */
+ }
+ else if (detectsuffix(end, "GB")) /* gigabytes */
+ {
+ if ( val > (INT_MAX / 1073741824) || val < (INT_MIN / 1073741824) )
+#ifdef __STDC_WANT_SECURE_LIB__
+ errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */
+#else
+ errorcode = EOVERFLOW; /* Overflow would occur if we proceed */
+#endif
+ else
+ val *= 1073741824; /* 1GB = 1024*1024*1024 */
+ }
+ else if (!detectsuffix(end, ""))
+ errorcode = EBADINT; /* invalid suffix detected */
+
+ /* if success then store result in parent->ival[] array */
+ if (errorcode == 0)
+ parent->ival[parent->count++] = val;
+ }
+
+ /* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */
+ return errorcode;
+}
+
+
+static int arg_int_checkfn(struct arg_int *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+ /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/
+ return errorcode;
+}
+
+
+static void arg_int_errorfn(
+ struct arg_int *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ case EBADINT:
+ fprintf(fp, "invalid argument \"%s\" to option ", argval);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+#ifdef __STDC_WANT_SECURE_LIB__
+ case EOVERFLOW_:
+#else
+ case EOVERFLOW:
+#endif
+ fputs("integer overflow at option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, " ");
+ fprintf(fp, "(%s is too large)\n", argval);
+ break;
+ }
+}
+
+
+struct arg_int * arg_int0(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_intn(shortopts, longopts, datatype, 0, 1, glossary);
+}
+
+
+struct arg_int * arg_int1(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_intn(shortopts, longopts, datatype, 1, 1, glossary);
+}
+
+
+struct arg_int * arg_intn(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_int *result;
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ nbytes = sizeof(struct arg_int) /* storage for struct arg_int */
+ + maxcount * sizeof(int); /* storage for ival[maxcount] array */
+
+ result = (struct arg_int *)malloc(nbytes);
+ if (result)
+ {
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = datatype ? datatype : "";
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_int_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_int_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_int_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_int_errorfn;
+
+ /* store the ival[maxcount] array immediately after the arg_int struct */
+ result->ival = (int *)(result + 1);
+ result->count = 0;
+ }
+
+ ARG_TRACE(("arg_intn() returns %p\n", result));
+ return result;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+
+#include "argtable3.h"
+
+
+static void arg_lit_resetfn(struct arg_lit *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+static int arg_lit_scanfn(struct arg_lit *parent, const char *argval)
+{
+ int errorcode = 0;
+ if (parent->count < parent->hdr.maxcount )
+ parent->count++;
+ else
+ errorcode = EMAXCOUNT;
+
+ ARG_TRACE(("%s:scanfn(%p,%s) returns %d\n", __FILE__, parent, argval,
+ errorcode));
+ return errorcode;
+}
+
+
+static int arg_lit_checkfn(struct arg_lit *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+ ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static void arg_lit_errorfn(
+ struct arg_lit *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fprintf(fp, "%s: missing option ", progname);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ fprintf(fp, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fprintf(fp, "%s: extraneous option ", progname);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+ }
+
+ ARG_TRACE(("%s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, fp,
+ errorcode, argval, progname));
+}
+
+
+struct arg_lit * arg_lit0(
+ const char * shortopts,
+ const char * longopts,
+ const char * glossary)
+{
+ return arg_litn(shortopts, longopts, 0, 1, glossary);
+}
+
+
+struct arg_lit * arg_lit1(
+ const char *shortopts,
+ const char *longopts,
+ const char *glossary)
+{
+ return arg_litn(shortopts, longopts, 1, 1, glossary);
+}
+
+
+struct arg_lit * arg_litn(
+ const char *shortopts,
+ const char *longopts,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ struct arg_lit *result;
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ result = (struct arg_lit *)malloc(sizeof(struct arg_lit));
+ if (result)
+ {
+ /* init the arg_hdr struct */
+ result->hdr.flag = 0;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = NULL;
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_lit_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_lit_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_lit_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_lit_errorfn;
+
+ /* init local variables */
+ result->count = 0;
+ }
+
+ ARG_TRACE(("arg_litn() returns %p\n", result));
+ return result;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+
+#include "argtable3.h"
+
+struct arg_rem *arg_rem(const char *datatype, const char *glossary)
+{
+ struct arg_rem *result = (struct arg_rem *)malloc(sizeof(struct arg_rem));
+ if (result)
+ {
+ result->hdr.flag = 0;
+ result->hdr.shortopts = NULL;
+ result->hdr.longopts = NULL;
+ result->hdr.datatype = datatype;
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = 1;
+ result->hdr.maxcount = 1;
+ result->hdr.parent = result;
+ result->hdr.resetfn = NULL;
+ result->hdr.scanfn = NULL;
+ result->hdr.checkfn = NULL;
+ result->hdr.errorfn = NULL;
+ }
+
+ ARG_TRACE(("arg_rem() returns %p\n", result));
+ return result;
+}
+
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+#include
+
+#include "argtable3.h"
+
+
+#ifndef _TREX_H_
+#define _TREX_H_
+/***************************************************************
+ T-Rex a tiny regular expression library
+
+ Copyright (C) 2003-2006 Alberto Demichelis
+
+ This software is provided 'as-is', without any express
+ or implied warranty. In no event will the authors be held
+ liable for any damages arising from the use of this software.
+
+ Permission is granted to anyone to use this software for
+ any purpose, including commercial applications, and to alter
+ it and redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented;
+ you must not claim that you wrote the original software.
+ If you use this software in a product, an acknowledgment
+ in the product documentation would be appreciated but
+ is not required.
+
+ 2. Altered source versions must be plainly marked as such,
+ and must not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any
+ source distribution.
+
+****************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _UNICODE
+#define TRexChar unsigned short
+#define MAX_CHAR 0xFFFF
+#define _TREXC(c) L##c
+#define trex_strlen wcslen
+#define trex_printf wprintf
+#else
+#define TRexChar char
+#define MAX_CHAR 0xFF
+#define _TREXC(c) (c)
+#define trex_strlen strlen
+#define trex_printf printf
+#endif
+
+#ifndef TREX_API
+#define TREX_API extern
+#endif
+
+#define TRex_True 1
+#define TRex_False 0
+
+#define TREX_ICASE ARG_REX_ICASE
+
+typedef unsigned int TRexBool;
+typedef struct TRex TRex;
+
+typedef struct {
+ const TRexChar *begin;
+ int len;
+} TRexMatch;
+
+TREX_API TRex *trex_compile(const TRexChar *pattern, const TRexChar **error, int flags);
+TREX_API void trex_free(TRex *exp);
+TREX_API TRexBool trex_match(TRex* exp, const TRexChar* text);
+TREX_API TRexBool trex_search(TRex* exp, const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
+TREX_API TRexBool trex_searchrange(TRex* exp, const TRexChar* text_begin, const TRexChar* text_end, const TRexChar** out_begin, const TRexChar** out_end);
+TREX_API int trex_getsubexpcount(TRex* exp);
+TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
+
+struct privhdr
+{
+ const char *pattern;
+ int flags;
+};
+
+
+static void arg_rex_resetfn(struct arg_rex *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+static int arg_rex_scanfn(struct arg_rex *parent, const char *argval)
+{
+ int errorcode = 0;
+ const TRexChar *error = NULL;
+ TRex *rex = NULL;
+ TRexBool is_match = TRex_False;
+
+ if (parent->count == parent->hdr.maxcount )
+ {
+ /* maximum number of arguments exceeded */
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* a valid argument with no argument value was given. */
+ /* This happens when an optional argument value was invoked. */
+ /* leave parent argument value unaltered but still count the argument. */
+ parent->count++;
+ }
+ else
+ {
+ struct privhdr *priv = (struct privhdr *)parent->hdr.priv;
+
+ /* test the current argument value for a match with the regular expression */
+ /* if a match is detected, record the argument value in the arg_rex struct */
+
+ rex = trex_compile(priv->pattern, &error, priv->flags);
+ is_match = trex_match(rex, argval);
+ if (!is_match)
+ errorcode = EREGNOMATCH;
+ else
+ parent->sval[parent->count++] = argval;
+
+ trex_free(rex);
+ }
+
+ ARG_TRACE(("%s:scanfn(%p) returns %d\n",__FILE__,parent,errorcode));
+ return errorcode;
+}
+
+static int arg_rex_checkfn(struct arg_rex *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+ //struct privhdr *priv = (struct privhdr*)parent->hdr.priv;
+
+ /* free the regex "program" we constructed in resetfn */
+ //regfree(&(priv->regex));
+
+ /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/
+ return errorcode;
+}
+
+static void arg_rex_errorfn(struct arg_rex *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ case EREGNOMATCH:
+ fputs("illegal value ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+
+ default:
+ {
+ //char errbuff[256];
+ //regerror(errorcode, NULL, errbuff, sizeof(errbuff));
+ //printf("%s\n", errbuff);
+ }
+ break;
+ }
+}
+
+
+struct arg_rex * arg_rex0(const char * shortopts,
+ const char * longopts,
+ const char * pattern,
+ const char *datatype,
+ int flags,
+ const char *glossary)
+{
+ return arg_rexn(shortopts,
+ longopts,
+ pattern,
+ datatype,
+ 0,
+ 1,
+ flags,
+ glossary);
+}
+
+struct arg_rex * arg_rex1(const char * shortopts,
+ const char * longopts,
+ const char * pattern,
+ const char *datatype,
+ int flags,
+ const char *glossary)
+{
+ return arg_rexn(shortopts,
+ longopts,
+ pattern,
+ datatype,
+ 1,
+ 1,
+ flags,
+ glossary);
+}
+
+
+struct arg_rex * arg_rexn(const char * shortopts,
+ const char * longopts,
+ const char * pattern,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ int flags,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_rex *result;
+ struct privhdr *priv;
+ int i;
+ const TRexChar *error = NULL;
+ TRex *rex = NULL;
+
+ if (!pattern)
+ {
+ printf(
+ "argtable: ERROR - illegal regular expression pattern \"(NULL)\"\n");
+ printf("argtable: Bad argument table.\n");
+ return NULL;
+ }
+
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ nbytes = sizeof(struct arg_rex) /* storage for struct arg_rex */
+ + sizeof(struct privhdr) /* storage for private arg_rex data */
+ + maxcount * sizeof(char *); /* storage for sval[maxcount] array */
+
+ result = (struct arg_rex *)malloc(nbytes);
+ if (result == NULL)
+ return result;
+
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = datatype ? datatype : pattern;
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_rex_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_rex_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_rex_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_rex_errorfn;
+
+ /* store the arg_rex_priv struct immediately after the arg_rex struct */
+ result->hdr.priv = result + 1;
+ priv = (struct privhdr *)(result->hdr.priv);
+ priv->pattern = pattern;
+ priv->flags = flags;
+
+ /* store the sval[maxcount] array immediately after the arg_rex_priv struct */
+ result->sval = (const char * *)(priv + 1);
+ result->count = 0;
+
+ /* foolproof the string pointers by initializing them to reference empty strings */
+ for (i = 0; i < maxcount; i++)
+ result->sval[i] = "";
+
+ /* here we construct and destroy a regex representation of the regular
+ * expression for no other reason than to force any regex errors to be
+ * trapped now rather than later. If we don't, then errors may go undetected
+ * until an argument is actually parsed.
+ */
+
+ rex = trex_compile(priv->pattern, &error, priv->flags);
+ if (rex == NULL)
+ {
+ ARG_LOG(("argtable: %s \"%s\"\n", error ? error : _TREXC("undefined"), priv->pattern));
+ ARG_LOG(("argtable: Bad argument table.\n"));
+ }
+
+ trex_free(rex);
+
+ ARG_TRACE(("arg_rexn() returns %p\n", result));
+ return result;
+}
+
+
+
+/* see copyright notice in trex.h */
+#include
+#include
+#include
+#include
+
+#ifdef _UINCODE
+#define scisprint iswprint
+#define scstrlen wcslen
+#define scprintf wprintf
+#define _SC(x) L(x)
+#else
+#define scisprint isprint
+#define scstrlen strlen
+#define scprintf printf
+#define _SC(x) (x)
+#endif
+
+#ifdef _DEBUG
+#include
+
+static const TRexChar *g_nnames[] =
+{
+ _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"),
+ _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"),
+ _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
+ _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
+};
+
+#endif
+#define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
+#define OP_OR (MAX_CHAR+2)
+#define OP_EXPR (MAX_CHAR+3) //parentesis ()
+#define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
+#define OP_DOT (MAX_CHAR+5)
+#define OP_CLASS (MAX_CHAR+6)
+#define OP_CCLASS (MAX_CHAR+7)
+#define OP_NCLASS (MAX_CHAR+8) //negates class the [^
+#define OP_RANGE (MAX_CHAR+9)
+#define OP_CHAR (MAX_CHAR+10)
+#define OP_EOL (MAX_CHAR+11)
+#define OP_BOL (MAX_CHAR+12)
+#define OP_WB (MAX_CHAR+13)
+
+#define TREX_SYMBOL_ANY_CHAR ('.')
+#define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
+#define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
+#define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
+#define TREX_SYMBOL_BRANCH ('|')
+#define TREX_SYMBOL_END_OF_STRING ('$')
+#define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
+#define TREX_SYMBOL_ESCAPE_CHAR ('\\')
+
+
+typedef int TRexNodeType;
+
+typedef struct tagTRexNode{
+ TRexNodeType type;
+ int left;
+ int right;
+ int next;
+}TRexNode;
+
+struct TRex{
+ const TRexChar *_eol;
+ const TRexChar *_bol;
+ const TRexChar *_p;
+ int _first;
+ int _op;
+ TRexNode *_nodes;
+ int _nallocated;
+ int _nsize;
+ int _nsubexpr;
+ TRexMatch *_matches;
+ int _currsubexp;
+ void *_jmpbuf;
+ const TRexChar **_error;
+ int _flags;
+};
+
+static int trex_list(TRex *exp);
+
+static int trex_newnode(TRex *exp, TRexNodeType type)
+{
+ TRexNode n;
+ int newid;
+ n.type = type;
+ n.next = n.right = n.left = -1;
+ if(type == OP_EXPR)
+ n.right = exp->_nsubexpr++;
+ if(exp->_nallocated < (exp->_nsize + 1)) {
+ exp->_nallocated *= 2;
+ exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
+ }
+ exp->_nodes[exp->_nsize++] = n;
+ newid = exp->_nsize - 1;
+ return (int)newid;
+}
+
+static void trex_error(TRex *exp,const TRexChar *error)
+{
+ if(exp->_error) *exp->_error = error;
+ longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
+}
+
+static void trex_expect(TRex *exp, int n){
+ if((*exp->_p) != n)
+ trex_error(exp, _SC("expected paren"));
+ exp->_p++;
+}
+
+static TRexChar trex_escapechar(TRex *exp)
+{
+ if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
+ exp->_p++;
+ switch(*exp->_p) {
+ case 'v': exp->_p++; return '\v';
+ case 'n': exp->_p++; return '\n';
+ case 't': exp->_p++; return '\t';
+ case 'r': exp->_p++; return '\r';
+ case 'f': exp->_p++; return '\f';
+ default: return (*exp->_p++);
+ }
+ } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
+ return (*exp->_p++);
+}
+
+static int trex_charclass(TRex *exp,int classid)
+{
+ int n = trex_newnode(exp,OP_CCLASS);
+ exp->_nodes[n].left = classid;
+ return n;
+}
+
+static int trex_charnode(TRex *exp,TRexBool isclass)
+{
+ TRexChar t;
+ if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
+ exp->_p++;
+ switch(*exp->_p) {
+ case 'n': exp->_p++; return trex_newnode(exp,'\n');
+ case 't': exp->_p++; return trex_newnode(exp,'\t');
+ case 'r': exp->_p++; return trex_newnode(exp,'\r');
+ case 'f': exp->_p++; return trex_newnode(exp,'\f');
+ case 'v': exp->_p++; return trex_newnode(exp,'\v');
+ case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
+ case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
+ case 'p': case 'P': case 'l': case 'u':
+ {
+ t = *exp->_p; exp->_p++;
+ return trex_charclass(exp,t);
+ }
+ case 'b':
+ case 'B':
+ if(!isclass) {
+ int node = trex_newnode(exp,OP_WB);
+ exp->_nodes[node].left = *exp->_p;
+ exp->_p++;
+ return node;
+ } //else default
+ default:
+ t = *exp->_p; exp->_p++;
+ return trex_newnode(exp,t);
+ }
+ }
+ else if(!scisprint(*exp->_p)) {
+
+ trex_error(exp,_SC("letter expected"));
+ }
+ t = *exp->_p; exp->_p++;
+ return trex_newnode(exp,t);
+}
+static int trex_class(TRex *exp)
+{
+ int ret = -1;
+ int first = -1,chain;
+ if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
+ ret = trex_newnode(exp,OP_NCLASS);
+ exp->_p++;
+ }else ret = trex_newnode(exp,OP_CLASS);
+
+ if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
+ chain = ret;
+ while(*exp->_p != ']' && exp->_p != exp->_eol) {
+ if(*exp->_p == '-' && first != -1){
+ int r,t;
+ if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
+ r = trex_newnode(exp,OP_RANGE);
+ if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
+ if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
+ exp->_nodes[r].left = exp->_nodes[first].type;
+ t = trex_escapechar(exp);
+ exp->_nodes[r].right = t;
+ exp->_nodes[chain].next = r;
+ chain = r;
+ first = -1;
+ }
+ else{
+ if(first!=-1){
+ int c = first;
+ exp->_nodes[chain].next = c;
+ chain = c;
+ first = trex_charnode(exp,TRex_True);
+ }
+ else{
+ first = trex_charnode(exp,TRex_True);
+ }
+ }
+ }
+ if(first!=-1){
+ int c = first;
+ exp->_nodes[chain].next = c;
+ chain = c;
+ first = -1;
+ }
+ /* hack? */
+ exp->_nodes[ret].left = exp->_nodes[ret].next;
+ exp->_nodes[ret].next = -1;
+ return ret;
+}
+
+static int trex_parsenumber(TRex *exp)
+{
+ int ret = *exp->_p-'0';
+ int positions = 10;
+ exp->_p++;
+ while(isdigit(*exp->_p)) {
+ ret = ret*10+(*exp->_p++-'0');
+ if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
+ positions *= 10;
+ };
+ return ret;
+}
+
+static int trex_element(TRex *exp)
+{
+ int ret = -1;
+ switch(*exp->_p)
+ {
+ case '(': {
+ int expr,newn;
+ exp->_p++;
+
+
+ if(*exp->_p =='?') {
+ exp->_p++;
+ trex_expect(exp,':');
+ expr = trex_newnode(exp,OP_NOCAPEXPR);
+ }
+ else
+ expr = trex_newnode(exp,OP_EXPR);
+ newn = trex_list(exp);
+ exp->_nodes[expr].left = newn;
+ ret = expr;
+ trex_expect(exp,')');
+ }
+ break;
+ case '[':
+ exp->_p++;
+ ret = trex_class(exp);
+ trex_expect(exp,']');
+ break;
+ case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
+ case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
+ default:
+ ret = trex_charnode(exp,TRex_False);
+ break;
+ }
+
+ {
+ TRexBool isgreedy = TRex_False;
+ unsigned short p0 = 0, p1 = 0;
+ switch(*exp->_p){
+ case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
+ case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
+ case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
+ case '{':
+ exp->_p++;
+ if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
+ p0 = (unsigned short)trex_parsenumber(exp);
+ /*******************************/
+ switch(*exp->_p) {
+ case '}':
+ p1 = p0; exp->_p++;
+ break;
+ case ',':
+ exp->_p++;
+ p1 = 0xFFFF;
+ if(isdigit(*exp->_p)){
+ p1 = (unsigned short)trex_parsenumber(exp);
+ }
+ trex_expect(exp,'}');
+ break;
+ default:
+ trex_error(exp,_SC(", or } expected"));
+ }
+ /*******************************/
+ isgreedy = TRex_True;
+ break;
+
+ }
+ if(isgreedy) {
+ int nnode = trex_newnode(exp,OP_GREEDY);
+ exp->_nodes[nnode].left = ret;
+ exp->_nodes[nnode].right = ((p0)<<16)|p1;
+ ret = nnode;
+ }
+ }
+ if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
+ int nnode = trex_element(exp);
+ exp->_nodes[ret].next = nnode;
+ }
+
+ return ret;
+}
+
+static int trex_list(TRex *exp)
+{
+ int ret=-1,e;
+ if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
+ exp->_p++;
+ ret = trex_newnode(exp,OP_BOL);
+ }
+ e = trex_element(exp);
+ if(ret != -1) {
+ exp->_nodes[ret].next = e;
+ }
+ else ret = e;
+
+ if(*exp->_p == TREX_SYMBOL_BRANCH) {
+ int temp,tright;
+ exp->_p++;
+ temp = trex_newnode(exp,OP_OR);
+ exp->_nodes[temp].left = ret;
+ tright = trex_list(exp);
+ exp->_nodes[temp].right = tright;
+ ret = temp;
+ }
+ return ret;
+}
+
+static TRexBool trex_matchcclass(int cclass,TRexChar c)
+{
+ switch(cclass) {
+ case 'a': return isalpha(c)?TRex_True:TRex_False;
+ case 'A': return !isalpha(c)?TRex_True:TRex_False;
+ case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
+ case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
+ case 's': return ISSPACE(c)?TRex_True:TRex_False;
+ case 'S': return !ISSPACE(c)?TRex_True:TRex_False;
+ case 'd': return isdigit(c)?TRex_True:TRex_False;
+ case 'D': return !isdigit(c)?TRex_True:TRex_False;
+ case 'x': return isxdigit(c)?TRex_True:TRex_False;
+ case 'X': return !isxdigit(c)?TRex_True:TRex_False;
+ case 'c': return iscntrl(c)?TRex_True:TRex_False;
+ case 'C': return !iscntrl(c)?TRex_True:TRex_False;
+ case 'p': return ispunct(c)?TRex_True:TRex_False;
+ case 'P': return !ispunct(c)?TRex_True:TRex_False;
+ case 'l': return islower(c)?TRex_True:TRex_False;
+ case 'u': return isupper(c)?TRex_True:TRex_False;
+ }
+ return TRex_False; /*cannot happen*/
+}
+
+static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
+{
+ do {
+ switch(node->type) {
+ case OP_RANGE:
+ if (exp->_flags & TREX_ICASE)
+ {
+ if(c >= toupper(node->left) && c <= toupper(node->right)) return TRex_True;
+ if(c >= tolower(node->left) && c <= tolower(node->right)) return TRex_True;
+ }
+ else
+ {
+ if(c >= node->left && c <= node->right) return TRex_True;
+ }
+ break;
+ case OP_CCLASS:
+ if(trex_matchcclass(node->left,c)) return TRex_True;
+ break;
+ default:
+ if (exp->_flags & TREX_ICASE)
+ {
+ if (c == tolower(node->type) || c == toupper(node->type)) return TRex_True;
+ }
+ else
+ {
+ if(c == node->type)return TRex_True;
+ }
+
+ }
+ } while((node->next != -1) && (node = &exp->_nodes[node->next]));
+ return TRex_False;
+}
+
+static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
+{
+
+ TRexNodeType type = node->type;
+ switch(type) {
+ case OP_GREEDY: {
+ //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
+ TRexNode *greedystop = NULL;
+ int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
+ const TRexChar *s=str, *good = str;
+
+ if(node->next != -1) {
+ greedystop = &exp->_nodes[node->next];
+ }
+ else {
+ greedystop = next;
+ }
+
+ while((nmaches == 0xFFFF || nmaches < p1)) {
+
+ const TRexChar *stop;
+ if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
+ break;
+ nmaches++;
+ good=s;
+ if(greedystop) {
+ //checks that 0 matches satisfy the expression(if so skips)
+ //if not would always stop(for instance if is a '?')
+ if(greedystop->type != OP_GREEDY ||
+ (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
+ {
+ TRexNode *gnext = NULL;
+ if(greedystop->next != -1) {
+ gnext = &exp->_nodes[greedystop->next];
+ }else if(next && next->next != -1){
+ gnext = &exp->_nodes[next->next];
+ }
+ stop = trex_matchnode(exp,greedystop,s,gnext);
+ if(stop) {
+ //if satisfied stop it
+ if(p0 == p1 && p0 == nmaches) break;
+ else if(nmaches >= p0 && p1 == 0xFFFF) break;
+ else if(nmaches >= p0 && nmaches <= p1) break;
+ }
+ }
+ }
+
+ if(s >= exp->_eol)
+ break;
+ }
+ if(p0 == p1 && p0 == nmaches) return good;
+ else if(nmaches >= p0 && p1 == 0xFFFF) return good;
+ else if(nmaches >= p0 && nmaches <= p1) return good;
+ return NULL;
+ }
+ case OP_OR: {
+ const TRexChar *asd = str;
+ TRexNode *temp=&exp->_nodes[node->left];
+ while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
+ if(temp->next != -1)
+ temp = &exp->_nodes[temp->next];
+ else
+ return asd;
+ }
+ asd = str;
+ temp = &exp->_nodes[node->right];
+ while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
+ if(temp->next != -1)
+ temp = &exp->_nodes[temp->next];
+ else
+ return asd;
+ }
+ return NULL;
+ break;
+ }
+ case OP_EXPR:
+ case OP_NOCAPEXPR:{
+ TRexNode *n = &exp->_nodes[node->left];
+ const TRexChar *cur = str;
+ int capture = -1;
+ if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
+ capture = exp->_currsubexp;
+ exp->_matches[capture].begin = cur;
+ exp->_currsubexp++;
+ }
+
+ do {
+ TRexNode *subnext = NULL;
+ if(n->next != -1) {
+ subnext = &exp->_nodes[n->next];
+ }else {
+ subnext = next;
+ }
+ if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
+ if(capture != -1){
+ exp->_matches[capture].begin = 0;
+ exp->_matches[capture].len = 0;
+ }
+ return NULL;
+ }
+ } while((n->next != -1) && (n = &exp->_nodes[n->next]));
+
+ if(capture != -1)
+ exp->_matches[capture].len = (int)(cur - exp->_matches[capture].begin);
+ return cur;
+ }
+ case OP_WB:
+ if((str == exp->_bol && !ISSPACE(*str))
+ || ((str == exp->_eol && !ISSPACE(*(str-1))))
+ || ((!ISSPACE(*str) && ISSPACE(*(str+1))))
+ || ((ISSPACE(*str) && !ISSPACE(*(str+1)))) ) {
+ return (node->left == 'b')?str:NULL;
+ }
+ return (node->left == 'b')?NULL:str;
+ case OP_BOL:
+ if(str == exp->_bol) return str;
+ return NULL;
+ case OP_EOL:
+ if(str == exp->_eol) return str;
+ return NULL;
+ case OP_DOT:
+ str++;
+ return str;
+ case OP_NCLASS:
+ case OP_CLASS:
+ if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
+ str++;
+ return str;
+ }
+ return NULL;
+ case OP_CCLASS:
+ if(trex_matchcclass(node->left,*str)) {
+ str++;
+ return str;
+ }
+ return NULL;
+ default: /* char */
+ if (exp->_flags & TREX_ICASE)
+ {
+ if(*str != tolower(node->type) && *str != toupper(node->type)) return NULL;
+ }
+ else
+ {
+ if (*str != node->type) return NULL;
+ }
+ str++;
+ return str;
+ }
+ return NULL;
+}
+
+/* public api */
+TRex *trex_compile(const TRexChar *pattern,const TRexChar **error,int flags)
+{
+ TRex *exp = (TRex *)malloc(sizeof(TRex));
+ exp->_eol = exp->_bol = NULL;
+ exp->_p = pattern;
+ exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
+ exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
+ exp->_nsize = 0;
+ exp->_matches = 0;
+ exp->_nsubexpr = 0;
+ exp->_first = trex_newnode(exp,OP_EXPR);
+ exp->_error = error;
+ exp->_jmpbuf = malloc(sizeof(jmp_buf));
+ exp->_flags = flags;
+ if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
+ int res = trex_list(exp);
+ exp->_nodes[exp->_first].left = res;
+ if(*exp->_p!='\0')
+ trex_error(exp,_SC("unexpected character"));
+#ifdef _DEBUG
+ {
+ int nsize,i;
+ TRexNode *t;
+ nsize = exp->_nsize;
+ t = &exp->_nodes[0];
+ scprintf(_SC("\n"));
+ for(i = 0;i < nsize; i++) {
+ if(exp->_nodes[i].type>MAX_CHAR)
+ scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
+ else
+ scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
+ scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
+ }
+ scprintf(_SC("\n"));
+ }
+#endif
+ exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
+ memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
+ }
+ else{
+ trex_free(exp);
+ return NULL;
+ }
+ return exp;
+}
+
+void trex_free(TRex *exp)
+{
+ if(exp) {
+ if(exp->_nodes) free(exp->_nodes);
+ if(exp->_jmpbuf) free(exp->_jmpbuf);
+ if(exp->_matches) free(exp->_matches);
+ free(exp);
+ }
+}
+
+TRexBool trex_match(TRex* exp,const TRexChar* text)
+{
+ const TRexChar* res = NULL;
+ exp->_bol = text;
+ exp->_eol = text + scstrlen(text);
+ exp->_currsubexp = 0;
+ res = trex_matchnode(exp,exp->_nodes,text,NULL);
+ if(res == NULL || res != exp->_eol)
+ return TRex_False;
+ return TRex_True;
+}
+
+TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
+{
+ const TRexChar *cur = NULL;
+ int node = exp->_first;
+ if(text_begin >= text_end) return TRex_False;
+ exp->_bol = text_begin;
+ exp->_eol = text_end;
+ do {
+ cur = text_begin;
+ while(node != -1) {
+ exp->_currsubexp = 0;
+ cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
+ if(!cur)
+ break;
+ node = exp->_nodes[node].next;
+ }
+ text_begin++;
+ } while(cur == NULL && text_begin != text_end);
+
+ if(cur == NULL)
+ return TRex_False;
+
+ --text_begin;
+
+ if(out_begin) *out_begin = text_begin;
+ if(out_end) *out_end = cur;
+ return TRex_True;
+}
+
+TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
+{
+ return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
+}
+
+int trex_getsubexpcount(TRex* exp)
+{
+ return exp->_nsubexpr;
+}
+
+TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
+{
+ if( n<0 || n >= exp->_nsubexpr) return TRex_False;
+ *subexp = exp->_matches[n];
+ return TRex_True;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+
+#include "argtable3.h"
+
+
+static void arg_str_resetfn(struct arg_str *parent)
+{
+ ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent));
+ parent->count = 0;
+}
+
+
+static int arg_str_scanfn(struct arg_str *parent, const char *argval)
+{
+ int errorcode = 0;
+
+ if (parent->count == parent->hdr.maxcount)
+ {
+ /* maximum number of arguments exceeded */
+ errorcode = EMAXCOUNT;
+ }
+ else if (!argval)
+ {
+ /* a valid argument with no argument value was given. */
+ /* This happens when an optional argument value was invoked. */
+ /* leave parent arguiment value unaltered but still count the argument. */
+ parent->count++;
+ }
+ else
+ {
+ parent->sval[parent->count++] = argval;
+ }
+
+ ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static int arg_str_checkfn(struct arg_str *parent)
+{
+ int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0;
+
+ ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode));
+ return errorcode;
+}
+
+
+static void arg_str_errorfn(
+ struct arg_str *parent,
+ FILE *fp,
+ int errorcode,
+ const char *argval,
+ const char *progname)
+{
+ const char *shortopts = parent->hdr.shortopts;
+ const char *longopts = parent->hdr.longopts;
+ const char *datatype = parent->hdr.datatype;
+
+ /* make argval NULL safe */
+ argval = argval ? argval : "";
+
+ fprintf(fp, "%s: ", progname);
+ switch(errorcode)
+ {
+ case EMINCOUNT:
+ fputs("missing option ", fp);
+ arg_print_option(fp, shortopts, longopts, datatype, "\n");
+ break;
+
+ case EMAXCOUNT:
+ fputs("excess option ", fp);
+ arg_print_option(fp, shortopts, longopts, argval, "\n");
+ break;
+ }
+}
+
+
+struct arg_str * arg_str0(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_strn(shortopts, longopts, datatype, 0, 1, glossary);
+}
+
+
+struct arg_str * arg_str1(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ const char *glossary)
+{
+ return arg_strn(shortopts, longopts, datatype, 1, 1, glossary);
+}
+
+
+struct arg_str * arg_strn(
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary)
+{
+ size_t nbytes;
+ struct arg_str *result;
+
+ /* should not allow this stupid error */
+ /* we should return an error code warning this logic error */
+ /* foolproof things by ensuring maxcount is not less than mincount */
+ maxcount = (maxcount < mincount) ? mincount : maxcount;
+
+ nbytes = sizeof(struct arg_str) /* storage for struct arg_str */
+ + maxcount * sizeof(char *); /* storage for sval[maxcount] array */
+
+ result = (struct arg_str *)malloc(nbytes);
+ if (result)
+ {
+ int i;
+
+ /* init the arg_hdr struct */
+ result->hdr.flag = ARG_HASVALUE;
+ result->hdr.shortopts = shortopts;
+ result->hdr.longopts = longopts;
+ result->hdr.datatype = datatype ? datatype : "";
+ result->hdr.glossary = glossary;
+ result->hdr.mincount = mincount;
+ result->hdr.maxcount = maxcount;
+ result->hdr.parent = result;
+ result->hdr.resetfn = (arg_resetfn *)arg_str_resetfn;
+ result->hdr.scanfn = (arg_scanfn *)arg_str_scanfn;
+ result->hdr.checkfn = (arg_checkfn *)arg_str_checkfn;
+ result->hdr.errorfn = (arg_errorfn *)arg_str_errorfn;
+
+ /* store the sval[maxcount] array immediately after the arg_str struct */
+ result->sval = (const char * *)(result + 1);
+ result->count = 0;
+
+ /* foolproof the string pointers by initialising them to reference empty strings */
+ for (i = 0; i < maxcount; i++)
+ result->sval[i] = "";
+ }
+
+ ARG_TRACE(("arg_strn() returns %p\n", result));
+ return result;
+}
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#include
+#include
+#include
+#include
+
+#include "argtable3.h"
+
+static
+void arg_register_error(struct arg_end *end,
+ void *parent,
+ int error,
+ const char *argval)
+{
+ /* printf("arg_register_error(%p,%p,%d,%s)\n",end,parent,error,argval); */
+ if (end->count < end->hdr.maxcount)
+ {
+ end->error[end->count] = error;
+ end->parent[end->count] = parent;
+ end->argval[end->count] = argval;
+ end->count++;
+ }
+ else
+ {
+ end->error[end->hdr.maxcount - 1] = ARG_ELIMIT;
+ end->parent[end->hdr.maxcount - 1] = end;
+ end->argval[end->hdr.maxcount - 1] = NULL;
+ }
+}
+
+
+/*
+ * Return index of first table entry with a matching short option
+ * or -1 if no match was found.
+ */
+static
+int find_shortoption(struct arg_hdr * *table, char shortopt)
+{
+ int tabindex;
+ for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ if (table[tabindex]->shortopts &&
+ strchr(table[tabindex]->shortopts, shortopt))
+ return tabindex;
+ }
+ return -1;
+}
+
+
+struct longoptions
+{
+ int getoptval;
+ int noptions;
+ struct option *options;
+};
+
+#if 0
+static
+void dump_longoptions(struct longoptions * longoptions)
+{
+ int i;
+ printf("getoptval = %d\n", longoptions->getoptval);
+ printf("noptions = %d\n", longoptions->noptions);
+ for (i = 0; i < longoptions->noptions; i++)
+ {
+ printf("options[%d].name = \"%s\"\n",
+ i,
+ longoptions->options[i].name);
+ printf("options[%d].has_arg = %d\n", i, longoptions->options[i].has_arg);
+ printf("options[%d].flag = %p\n", i, longoptions->options[i].flag);
+ printf("options[%d].val = %d\n", i, longoptions->options[i].val);
+ }
+}
+#endif
+
+static
+struct longoptions * alloc_longoptions(struct arg_hdr * *table)
+{
+ struct longoptions *result;
+ size_t nbytes;
+ int noptions = 1;
+ size_t longoptlen = 0;
+ int tabindex;
+
+ /*
+ * Determine the total number of option structs required
+ * by counting the number of comma separated long options
+ * in all table entries and return the count in noptions.
+ * note: noptions starts at 1 not 0 because we getoptlong
+ * requires a NULL option entry to terminate the option array.
+ * While we are at it, count the number of chars required
+ * to store private copies of all the longoption strings
+ * and return that count in logoptlen.
+ */
+ tabindex = 0;
+ do
+ {
+ const char *longopts = table[tabindex]->longopts;
+ longoptlen += (longopts ? strlen(longopts) : 0) + 1;
+ while (longopts)
+ {
+ noptions++;
+ longopts = strchr(longopts + 1, ',');
+ }
+ } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
+ /*printf("%d long options consuming %d chars in total\n",noptions,longoptlen);*/
+
+
+ /* allocate storage for return data structure as: */
+ /* (struct longoptions) + (struct options)[noptions] + char[longoptlen] */
+ nbytes = sizeof(struct longoptions)
+ + sizeof(struct option) * noptions
+ + longoptlen;
+ result = (struct longoptions *)malloc(nbytes);
+ if (result)
+ {
+ int option_index = 0;
+ char *store;
+
+ result->getoptval = 0;
+ result->noptions = noptions;
+ result->options = (struct option *)(result + 1);
+ store = (char *)(result->options + noptions);
+
+ for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ const char *longopts = table[tabindex]->longopts;
+
+ while(longopts && *longopts)
+ {
+ char *storestart = store;
+
+ /* copy progressive longopt strings into the store */
+ while (*longopts != 0 && *longopts != ',')
+ *store++ = *longopts++;
+ *store++ = 0;
+ if (*longopts == ',')
+ longopts++;
+ /*fprintf(stderr,"storestart=\"%s\"\n",storestart);*/
+
+ result->options[option_index].name = storestart;
+ result->options[option_index].flag = &(result->getoptval);
+ result->options[option_index].val = tabindex;
+ if (table[tabindex]->flag & ARG_HASOPTVALUE)
+ result->options[option_index].has_arg = 2;
+ else if (table[tabindex]->flag & ARG_HASVALUE)
+ result->options[option_index].has_arg = 1;
+ else
+ result->options[option_index].has_arg = 0;
+
+ option_index++;
+ }
+ }
+ /* terminate the options array with a zero-filled entry */
+ result->options[option_index].name = 0;
+ result->options[option_index].has_arg = 0;
+ result->options[option_index].flag = 0;
+ result->options[option_index].val = 0;
+ }
+
+ /*dump_longoptions(result);*/
+ return result;
+}
+
+static
+char * alloc_shortoptions(struct arg_hdr * *table)
+{
+ char *result;
+ size_t len = 2;
+ int tabindex;
+
+ /* determine the total number of option chars required */
+ for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ struct arg_hdr *hdr = table[tabindex];
+ len += 3 * (hdr->shortopts ? strlen(hdr->shortopts) : 0);
+ }
+
+ result = malloc(len);
+ if (result)
+ {
+ char *res = result;
+
+ /* add a leading ':' so getopt return codes distinguish */
+ /* unrecognised option and options missing argument values */
+ *res++ = ':';
+
+ for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ struct arg_hdr *hdr = table[tabindex];
+ const char *shortopts = hdr->shortopts;
+ while(shortopts && *shortopts)
+ {
+ *res++ = *shortopts++;
+ if (hdr->flag & ARG_HASVALUE)
+ *res++ = ':';
+ if (hdr->flag & ARG_HASOPTVALUE)
+ *res++ = ':';
+ }
+ }
+ /* null terminate the string */
+ *res = 0;
+ }
+
+ /*printf("alloc_shortoptions() returns \"%s\"\n",(result?result:"NULL"));*/
+ return result;
+}
+
+
+/* return index of the table terminator entry */
+static
+int arg_endindex(struct arg_hdr * *table)
+{
+ int tabindex = 0;
+ while (!(table[tabindex]->flag & ARG_TERMINATOR))
+ tabindex++;
+ return tabindex;
+}
+
+
+static
+void arg_parse_tagged(int argc,
+ char * *argv,
+ struct arg_hdr * *table,
+ struct arg_end *endtable)
+{
+ struct longoptions *longoptions;
+ char *shortoptions;
+ int copt;
+
+ /*printf("arg_parse_tagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/
+
+ /* allocate short and long option arrays for the given opttable[]. */
+ /* if the allocs fail then put an error msg in the last table entry. */
+ longoptions = alloc_longoptions(table);
+ shortoptions = alloc_shortoptions(table);
+ if (!longoptions || !shortoptions)
+ {
+ /* one or both memory allocs failed */
+ arg_register_error(endtable, endtable, ARG_EMALLOC, NULL);
+ /* free anything that was allocated (this is null safe) */
+ free(shortoptions);
+ free(longoptions);
+ return;
+ }
+
+ /*dump_longoptions(longoptions);*/
+
+ /* reset getopts internal option-index to zero, and disable error reporting */
+ optind = 0;
+ opterr = 0;
+
+ /* fetch and process args using getopt_long */
+ while( (copt =
+ getopt_long(argc, argv, shortoptions, longoptions->options,
+ NULL)) != -1)
+ {
+ /*
+ printf("optarg='%s'\n",optarg);
+ printf("optind=%d\n",optind);
+ printf("copt=%c\n",(char)copt);
+ printf("optopt=%c (%d)\n",optopt, (int)(optopt));
+ */
+ switch(copt)
+ {
+ case 0:
+ {
+ int tabindex = longoptions->getoptval;
+ void *parent = table[tabindex]->parent;
+ /*printf("long option detected from argtable[%d]\n", tabindex);*/
+ if (optarg && optarg[0] == 0 &&
+ (table[tabindex]->flag & ARG_HASVALUE))
+ {
+ /* printf(": long option %s requires an argument\n",argv[optind-1]); */
+ arg_register_error(endtable, endtable, ARG_EMISSARG,
+ argv[optind - 1]);
+ /* continue to scan the (empty) argument value to enforce argument count checking */
+ }
+ if (table[tabindex]->scanfn)
+ {
+ int errorcode = table[tabindex]->scanfn(parent, optarg);
+ if (errorcode != 0)
+ arg_register_error(endtable, parent, errorcode, optarg);
+ }
+ }
+ break;
+
+ case '?':
+ /*
+ * getopt_long() found an unrecognised short option.
+ * if it was a short option its value is in optopt
+ * if it was a long option then optopt=0
+ */
+ switch (optopt)
+ {
+ case 0:
+ /*printf("?0 unrecognised long option %s\n",argv[optind-1]);*/
+ arg_register_error(endtable, endtable, ARG_ELONGOPT,
+ argv[optind - 1]);
+ break;
+ default:
+ /*printf("?* unrecognised short option '%c'\n",optopt);*/
+ arg_register_error(endtable, endtable, optopt, NULL);
+ break;
+ }
+ break;
+
+ case ':':
+ /*
+ * getopt_long() found an option with its argument missing.
+ */
+ /*printf(": option %s requires an argument\n",argv[optind-1]); */
+ arg_register_error(endtable, endtable, ARG_EMISSARG,
+ argv[optind - 1]);
+ break;
+
+ default:
+ {
+ /* getopt_long() found a valid short option */
+ int tabindex = find_shortoption(table, (char)copt);
+ /*printf("short option detected from argtable[%d]\n", tabindex);*/
+ if (tabindex == -1)
+ {
+ /* should never get here - but handle it just in case */
+ /*printf("unrecognised short option %d\n",copt);*/
+ arg_register_error(endtable, endtable, copt, NULL);
+ }
+ else
+ {
+ if (table[tabindex]->scanfn)
+ {
+ void *parent = table[tabindex]->parent;
+ int errorcode = table[tabindex]->scanfn(parent, optarg);
+ if (errorcode != 0)
+ arg_register_error(endtable, parent, errorcode, optarg);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ free(shortoptions);
+ free(longoptions);
+}
+
+
+static
+void arg_parse_untagged(int argc,
+ char * *argv,
+ struct arg_hdr * *table,
+ struct arg_end *endtable)
+{
+ int tabindex = 0;
+ int errorlast = 0;
+ const char *optarglast = NULL;
+ void *parentlast = NULL;
+
+ /*printf("arg_parse_untagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/
+ while (!(table[tabindex]->flag & ARG_TERMINATOR))
+ {
+ void *parent;
+ int errorcode;
+
+ /* if we have exhausted our argv[optind] entries then we have finished */
+ if (optind >= argc)
+ {
+ /*printf("arg_parse_untagged(): argv[] exhausted\n");*/
+ return;
+ }
+
+ /* skip table entries with non-null long or short options (they are not untagged entries) */
+ if (table[tabindex]->longopts || table[tabindex]->shortopts)
+ {
+ /*printf("arg_parse_untagged(): skipping argtable[%d] (tagged argument)\n",tabindex);*/
+ tabindex++;
+ continue;
+ }
+
+ /* skip table entries with NULL scanfn */
+ if (!(table[tabindex]->scanfn))
+ {
+ /*printf("arg_parse_untagged(): skipping argtable[%d] (NULL scanfn)\n",tabindex);*/
+ tabindex++;
+ continue;
+ }
+
+ /* attempt to scan the current argv[optind] with the current */
+ /* table[tabindex] entry. If it succeeds then keep it, otherwise */
+ /* try again with the next table[] entry. */
+ parent = table[tabindex]->parent;
+ errorcode = table[tabindex]->scanfn(parent, argv[optind]);
+ if (errorcode == 0)
+ {
+ /* success, move onto next argv[optind] but stay with same table[tabindex] */
+ /*printf("arg_parse_untagged(): argtable[%d] successfully matched\n",tabindex);*/
+ optind++;
+
+ /* clear the last tentative error */
+ errorlast = 0;
+ }
+ else
+ {
+ /* failure, try same argv[optind] with next table[tabindex] entry */
+ /*printf("arg_parse_untagged(): argtable[%d] failed match\n",tabindex);*/
+ tabindex++;
+
+ /* remember this as a tentative error we may wish to reinstate later */
+ errorlast = errorcode;
+ optarglast = argv[optind];
+ parentlast = parent;
+ }
+
+ }
+
+ /* if a tenative error still remains at this point then register it as a proper error */
+ if (errorlast)
+ {
+ arg_register_error(endtable, parentlast, errorlast, optarglast);
+ optind++;
+ }
+
+ /* only get here when not all argv[] entries were consumed */
+ /* register an error for each unused argv[] entry */
+ while (optind < argc)
+ {
+ /*printf("arg_parse_untagged(): argv[%d]=\"%s\" not consumed\n",optind,argv[optind]);*/
+ arg_register_error(endtable, endtable, ARG_ENOMATCH, argv[optind++]);
+ }
+
+ return;
+}
+
+
+static
+void arg_parse_check(struct arg_hdr * *table, struct arg_end *endtable)
+{
+ int tabindex = 0;
+ /* printf("arg_parse_check()\n"); */
+ do
+ {
+ if (table[tabindex]->checkfn)
+ {
+ void *parent = table[tabindex]->parent;
+ int errorcode = table[tabindex]->checkfn(parent);
+ if (errorcode != 0)
+ arg_register_error(endtable, parent, errorcode, NULL);
+ }
+ } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
+}
+
+
+static
+void arg_reset(void * *argtable)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int tabindex = 0;
+ /*printf("arg_reset(%p)\n",argtable);*/
+ do
+ {
+ if (table[tabindex]->resetfn)
+ table[tabindex]->resetfn(table[tabindex]->parent);
+ } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
+}
+
+
+int arg_parse(int argc, char * *argv, void * *argtable)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ struct arg_end *endtable;
+ int endindex;
+ char * *argvcopy = NULL;
+
+ /*printf("arg_parse(%d,%p,%p)\n",argc,argv,argtable);*/
+
+ /* reset any argtable data from previous invocations */
+ arg_reset(argtable);
+
+ /* locate the first end-of-table marker within the array */
+ endindex = arg_endindex(table);
+ endtable = (struct arg_end *)table[endindex];
+
+ /* Special case of argc==0. This can occur on Texas Instruments DSP. */
+ /* Failure to trap this case results in an unwanted NULL result from */
+ /* the malloc for argvcopy (next code block). */
+ if (argc == 0)
+ {
+ /* We must still perform post-parse checks despite the absence of command line arguments */
+ arg_parse_check(table, endtable);
+
+ /* Now we are finished */
+ return endtable->count;
+ }
+
+ argvcopy = (char **)malloc(sizeof(char *) * (argc + 1));
+ if (argvcopy)
+ {
+ int i;
+
+ /*
+ Fill in the local copy of argv[]. We need a local copy
+ because getopt rearranges argv[] which adversely affects
+ susbsequent parsing attempts.
+ */
+ for (i = 0; i < argc; i++)
+ argvcopy[i] = argv[i];
+
+ argvcopy[argc] = NULL;
+
+ /* parse the command line (local copy) for tagged options */
+ arg_parse_tagged(argc, argvcopy, table, endtable);
+
+ /* parse the command line (local copy) for untagged options */
+ arg_parse_untagged(argc, argvcopy, table, endtable);
+
+ /* if no errors so far then perform post-parse checks otherwise dont bother */
+ if (endtable->count == 0)
+ arg_parse_check(table, endtable);
+
+ /* release the local copt of argv[] */
+ free(argvcopy);
+ }
+ else
+ {
+ /* memory alloc failed */
+ arg_register_error(endtable, endtable, ARG_EMALLOC, NULL);
+ }
+
+ return endtable->count;
+}
+
+
+/*
+ * Concatenate contents of src[] string onto *pdest[] string.
+ * The *pdest pointer is altered to point to the end of the
+ * target string and *pndest is decremented by the same number
+ * of chars.
+ * Does not append more than *pndest chars into *pdest[]
+ * so as to prevent buffer overruns.
+ * Its something like strncat() but more efficient for repeated
+ * calls on the same destination string.
+ * Example of use:
+ * char dest[30] = "good"
+ * size_t ndest = sizeof(dest);
+ * char *pdest = dest;
+ * arg_char(&pdest,"bye ",&ndest);
+ * arg_char(&pdest,"cruel ",&ndest);
+ * arg_char(&pdest,"world!",&ndest);
+ * Results in:
+ * dest[] == "goodbye cruel world!"
+ * ndest == 10
+ */
+static
+void arg_cat(char * *pdest, const char *src, size_t *pndest)
+{
+ char *dest = *pdest;
+ char *end = dest + *pndest;
+
+ /*locate null terminator of dest string */
+ while(dest < end && *dest != 0)
+ dest++;
+
+ /* concat src string to dest string */
+ while(dest < end && *src != 0)
+ *dest++ = *src++;
+
+ /* null terminate dest string */
+ *dest = 0;
+
+ /* update *pdest and *pndest */
+ *pndest = end - dest;
+ *pdest = dest;
+}
+
+
+static
+void arg_cat_option(char *dest,
+ size_t ndest,
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ int optvalue)
+{
+ if (shortopts)
+ {
+ char option[3];
+
+ /* note: option array[] is initialiazed dynamically here to satisfy */
+ /* a deficiency in the watcom compiler wrt static array initializers. */
+ option[0] = '-';
+ option[1] = shortopts[0];
+ option[2] = 0;
+
+ arg_cat(&dest, option, &ndest);
+ if (datatype)
+ {
+ arg_cat(&dest, " ", &ndest);
+ if (optvalue)
+ {
+ arg_cat(&dest, "[", &ndest);
+ arg_cat(&dest, datatype, &ndest);
+ arg_cat(&dest, "]", &ndest);
+ }
+ else
+ arg_cat(&dest, datatype, &ndest);
+ }
+ }
+ else if (longopts)
+ {
+ size_t ncspn;
+
+ /* add "--" tag prefix */
+ arg_cat(&dest, "--", &ndest);
+
+ /* add comma separated option tag */
+ ncspn = strcspn(longopts, ",");
+#ifdef __STDC_WANT_SECURE_LIB__
+ strncat_s(dest, ndest, longopts, (ncspn < ndest) ? ncspn : ndest);
+#else
+ strncat(dest, longopts, (ncspn < ndest) ? ncspn : ndest);
+#endif
+
+ if (datatype)
+ {
+ arg_cat(&dest, "=", &ndest);
+ if (optvalue)
+ {
+ arg_cat(&dest, "[", &ndest);
+ arg_cat(&dest, datatype, &ndest);
+ arg_cat(&dest, "]", &ndest);
+ }
+ else
+ arg_cat(&dest, datatype, &ndest);
+ }
+ }
+ else if (datatype)
+ {
+ if (optvalue)
+ {
+ arg_cat(&dest, "[", &ndest);
+ arg_cat(&dest, datatype, &ndest);
+ arg_cat(&dest, "]", &ndest);
+ }
+ else
+ arg_cat(&dest, datatype, &ndest);
+ }
+}
+
+static
+void arg_cat_optionv(char *dest,
+ size_t ndest,
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ int optvalue,
+ const char *separator)
+{
+ separator = separator ? separator : "";
+
+ if (shortopts)
+ {
+ const char *c = shortopts;
+ while(*c)
+ {
+ /* "-a|-b|-c" */
+ char shortopt[3];
+
+ /* note: shortopt array[] is initialiazed dynamically here to satisfy */
+ /* a deficiency in the watcom compiler wrt static array initializers. */
+ shortopt[0] = '-';
+ shortopt[1] = *c;
+ shortopt[2] = 0;
+
+ arg_cat(&dest, shortopt, &ndest);
+ if (*++c)
+ arg_cat(&dest, separator, &ndest);
+ }
+ }
+
+ /* put separator between long opts and short opts */
+ if (shortopts && longopts)
+ arg_cat(&dest, separator, &ndest);
+
+ if (longopts)
+ {
+ const char *c = longopts;
+ while(*c)
+ {
+ size_t ncspn;
+
+ /* add "--" tag prefix */
+ arg_cat(&dest, "--", &ndest);
+
+ /* add comma separated option tag */
+ ncspn = strcspn(c, ",");
+#ifdef __STDC_WANT_SECURE_LIB__
+ strncat_s(dest, ndest, c, (ncspn < ndest) ? ncspn : ndest);
+#else
+ strncat(dest, c, (ncspn < ndest) ? ncspn : ndest);
+#endif
+ c += ncspn;
+
+ /* add given separator in place of comma */
+ if (*c == ',')
+ {
+ arg_cat(&dest, separator, &ndest);
+ c++;
+ }
+ }
+ }
+
+ if (datatype)
+ {
+ if (longopts)
+ arg_cat(&dest, "=", &ndest);
+ else if (shortopts)
+ arg_cat(&dest, " ", &ndest);
+
+ if (optvalue)
+ {
+ arg_cat(&dest, "[", &ndest);
+ arg_cat(&dest, datatype, &ndest);
+ arg_cat(&dest, "]", &ndest);
+ }
+ else
+ arg_cat(&dest, datatype, &ndest);
+ }
+}
+
+
+/* this function should be deprecated because it doesnt consider optional argument values (ARG_HASOPTVALUE) */
+void arg_print_option(FILE *fp,
+ const char *shortopts,
+ const char *longopts,
+ const char *datatype,
+ const char *suffix)
+{
+ char syntax[200] = "";
+ suffix = suffix ? suffix : "";
+
+ /* there is no way of passing the proper optvalue for optional argument values here, so we must ignore it */
+ arg_cat_optionv(syntax,
+ sizeof(syntax),
+ shortopts,
+ longopts,
+ datatype,
+ 0,
+ "|");
+
+ fputs(syntax, fp);
+ fputs(suffix, fp);
+}
+
+
+/*
+ * Print a GNU style [OPTION] string in which all short options that
+ * do not take argument values are presented in abbreviated form, as
+ * in: -xvfsd, or -xvf[sd], or [-xvsfd]
+ */
+static
+void arg_print_gnuswitch(FILE *fp, struct arg_hdr * *table)
+{
+ int tabindex;
+ char *format1 = " -%c";
+ char *format2 = " [-%c";
+ char *suffix = "";
+
+ /* print all mandatory switches that are without argument values */
+ for(tabindex = 0;
+ table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
+ tabindex++)
+ {
+ /* skip optional options */
+ if (table[tabindex]->mincount < 1)
+ continue;
+
+ /* skip non-short options */
+ if (table[tabindex]->shortopts == NULL)
+ continue;
+
+ /* skip options that take argument values */
+ if (table[tabindex]->flag & ARG_HASVALUE)
+ continue;
+
+ /* print the short option (only the first short option char, ignore multiple choices)*/
+ fprintf(fp, format1, table[tabindex]->shortopts[0]);
+ format1 = "%c";
+ format2 = "[%c";
+ }
+
+ /* print all optional switches that are without argument values */
+ for(tabindex = 0;
+ table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
+ tabindex++)
+ {
+ /* skip mandatory args */
+ if (table[tabindex]->mincount > 0)
+ continue;
+
+ /* skip args without short options */
+ if (table[tabindex]->shortopts == NULL)
+ continue;
+
+ /* skip args with values */
+ if (table[tabindex]->flag & ARG_HASVALUE)
+ continue;
+
+ /* print first short option */
+ fprintf(fp, format2, table[tabindex]->shortopts[0]);
+ format2 = "%c";
+ suffix = "]";
+ }
+
+ fprintf(fp, "%s", suffix);
+}
+
+
+void arg_print_syntax(FILE *fp, void * *argtable, const char *suffix)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int i, tabindex;
+
+ /* print GNU style [OPTION] string */
+ arg_print_gnuswitch(fp, table);
+
+ /* print remaining options in abbreviated style */
+ for(tabindex = 0;
+ table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
+ tabindex++)
+ {
+ char syntax[200] = "";
+ const char *shortopts, *longopts, *datatype;
+
+ /* skip short options without arg values (they were printed by arg_print_gnu_switch) */
+ if (table[tabindex]->shortopts &&
+ !(table[tabindex]->flag & ARG_HASVALUE))
+ continue;
+
+ shortopts = table[tabindex]->shortopts;
+ longopts = table[tabindex]->longopts;
+ datatype = table[tabindex]->datatype;
+ arg_cat_option(syntax,
+ sizeof(syntax),
+ shortopts,
+ longopts,
+ datatype,
+ table[tabindex]->flag & ARG_HASOPTVALUE);
+
+ if (strlen(syntax) > 0)
+ {
+ /* print mandatory instances of this option */
+ for (i = 0; i < table[tabindex]->mincount; i++)
+ fprintf(fp, " %s", syntax);
+
+ /* print optional instances enclosed in "[..]" */
+ switch ( table[tabindex]->maxcount - table[tabindex]->mincount )
+ {
+ case 0:
+ break;
+ case 1:
+ fprintf(fp, " [%s]", syntax);
+ break;
+ case 2:
+ fprintf(fp, " [%s] [%s]", syntax, syntax);
+ break;
+ default:
+ fprintf(fp, " [%s]...", syntax);
+ break;
+ }
+ }
+ }
+
+ if (suffix)
+ fprintf(fp, "%s", suffix);
+}
+
+
+void arg_print_syntaxv(FILE *fp, void * *argtable, const char *suffix)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int i, tabindex;
+
+ /* print remaining options in abbreviated style */
+ for(tabindex = 0;
+ table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
+ tabindex++)
+ {
+ char syntax[200] = "";
+ const char *shortopts, *longopts, *datatype;
+
+ shortopts = table[tabindex]->shortopts;
+ longopts = table[tabindex]->longopts;
+ datatype = table[tabindex]->datatype;
+ arg_cat_optionv(syntax,
+ sizeof(syntax),
+ shortopts,
+ longopts,
+ datatype,
+ table[tabindex]->flag & ARG_HASOPTVALUE,
+ "|");
+
+ /* print mandatory options */
+ for (i = 0; i < table[tabindex]->mincount; i++)
+ fprintf(fp, " %s", syntax);
+
+ /* print optional args enclosed in "[..]" */
+ switch ( table[tabindex]->maxcount - table[tabindex]->mincount )
+ {
+ case 0:
+ break;
+ case 1:
+ fprintf(fp, " [%s]", syntax);
+ break;
+ case 2:
+ fprintf(fp, " [%s] [%s]", syntax, syntax);
+ break;
+ default:
+ fprintf(fp, " [%s]...", syntax);
+ break;
+ }
+ }
+
+ if (suffix)
+ fprintf(fp, "%s", suffix);
+}
+
+
+void arg_print_glossary(FILE *fp, void * *argtable, const char *format)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int tabindex;
+
+ format = format ? format : " %-20s %s\n";
+ for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ if (table[tabindex]->glossary)
+ {
+ char syntax[200] = "";
+ const char *shortopts = table[tabindex]->shortopts;
+ const char *longopts = table[tabindex]->longopts;
+ const char *datatype = table[tabindex]->datatype;
+ const char *glossary = table[tabindex]->glossary;
+ arg_cat_optionv(syntax,
+ sizeof(syntax),
+ shortopts,
+ longopts,
+ datatype,
+ table[tabindex]->flag & ARG_HASOPTVALUE,
+ ", ");
+ fprintf(fp, format, syntax, glossary);
+ }
+ }
+}
+
+
+/**
+ * Print a piece of text formatted, which means in a column with a
+ * left and a right margin. The lines are wrapped at whitspaces next
+ * to right margin. The function does not indent the first line, but
+ * only the following ones.
+ *
+ * Example:
+ * arg_print_formatted( fp, 0, 5, "Some text that doesn't fit." )
+ * will result in the following output:
+ *
+ * Some
+ * text
+ * that
+ * doesn'
+ * t fit.
+ *
+ * Too long lines will be wrapped in the middle of a word.
+ *
+ * arg_print_formatted( fp, 2, 7, "Some text that doesn't fit." )
+ * will result in the following output:
+ *
+ * Some
+ * text
+ * that
+ * doesn'
+ * t fit.
+ *
+ * As you see, the first line is not indented. This enables output of
+ * lines, which start in a line where output already happened.
+ *
+ * Author: Uli Fouquet
+ */
+static
+void arg_print_formatted( FILE *fp,
+ const unsigned lmargin,
+ const unsigned rmargin,
+ const char *text )
+{
+ const unsigned textlen = (unsigned)strlen( text );
+ unsigned line_start = 0;
+ unsigned line_end = textlen + 1;
+ const unsigned colwidth = (rmargin - lmargin) + 1;
+
+ /* Someone doesn't like us... */
+ if ( line_end < line_start )
+ { fprintf( fp, "%s\n", text ); }
+
+ while (line_end - 1 > line_start )
+ {
+ /* Eat leading whitespaces. This is essential because while
+ wrapping lines, there will often be a whitespace at beginning
+ of line */
+ while ( ISSPACE(*(text + line_start)) )
+ { line_start++; }
+
+ if ((line_end - line_start) > colwidth )
+ { line_end = line_start + colwidth; }
+
+ /* Find last whitespace, that fits into line */
+ while ( ( line_end > line_start )
+ && ( line_end - line_start > colwidth )
+ && !ISSPACE(*(text + line_end)))
+ { line_end--; }
+
+ /* Do not print trailing whitespace. If this text
+ has got only one line, line_end now points to the
+ last char due to initialization. */
+ line_end--;
+
+ /* Output line of text */
+ while ( line_start < line_end )
+ {
+ fputc(*(text + line_start), fp );
+ line_start++;
+ }
+ fputc( '\n', fp );
+
+ /* Initialize another line */
+ if ( line_end + 1 < textlen )
+ {
+ unsigned i;
+
+ for (i = 0; i < lmargin; i++ )
+ { fputc( ' ', fp ); }
+
+ line_end = textlen;
+ }
+
+ /* If we have to print another line, get also the last char. */
+ line_end++;
+
+ } /* lines of text */
+}
+
+/**
+ * Prints the glossary in strict GNU format.
+ * Differences to arg_print_glossary() are:
+ * - wraps lines after 80 chars
+ * - indents lines without shortops
+ * - does not accept formatstrings
+ *
+ * Contributed by Uli Fouquet
+ */
+void arg_print_glossary_gnu(FILE *fp, void * *argtable )
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int tabindex;
+
+ for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++)
+ {
+ if (table[tabindex]->glossary)
+ {
+ char syntax[200] = "";
+ const char *shortopts = table[tabindex]->shortopts;
+ const char *longopts = table[tabindex]->longopts;
+ const char *datatype = table[tabindex]->datatype;
+ const char *glossary = table[tabindex]->glossary;
+
+ if ( !shortopts && longopts )
+ {
+ /* Indent trailing line by 4 spaces... */
+ memset( syntax, ' ', 4 );
+ *(syntax + 4) = '\0';
+ }
+
+ arg_cat_optionv(syntax,
+ sizeof(syntax),
+ shortopts,
+ longopts,
+ datatype,
+ table[tabindex]->flag & ARG_HASOPTVALUE,
+ ", ");
+
+ /* If syntax fits not into column, print glossary in new line... */
+ if ( strlen(syntax) > 25 )
+ {
+ fprintf( fp, " %-25s %s\n", syntax, "" );
+ *syntax = '\0';
+ }
+
+ fprintf( fp, " %-25s ", syntax );
+ arg_print_formatted( fp, 28, 79, glossary );
+ }
+ } /* for each table entry */
+
+ fputc( '\n', fp );
+}
+
+
+/**
+ * Checks the argtable[] array for NULL entries and returns 1
+ * if any are found, zero otherwise.
+ */
+int arg_nullcheck(void * *argtable)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int tabindex;
+ /*printf("arg_nullcheck(%p)\n",argtable);*/
+
+ if (!table)
+ return 1;
+
+ tabindex = 0;
+ do
+ {
+ /*printf("argtable[%d]=%p\n",tabindex,argtable[tabindex]);*/
+ if (!table[tabindex])
+ return 1;
+ } while(!(table[tabindex++]->flag & ARG_TERMINATOR));
+
+ return 0;
+}
+
+
+/*
+ * arg_free() is deprecated in favour of arg_freetable() due to a flaw in its design.
+ * The flaw results in memory leak in the (very rare) case that an intermediate
+ * entry in the argtable array failed its memory allocation while others following
+ * that entry were still allocated ok. Those subsequent allocations will not be
+ * deallocated by arg_free().
+ * Despite the unlikeliness of the problem occurring, and the even unlikelier event
+ * that it has any deliterious effect, it is fixed regardless by replacing arg_free()
+ * with the newer arg_freetable() function.
+ * We still keep arg_free() for backwards compatibility.
+ */
+void arg_free(void * *argtable)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ int tabindex = 0;
+ int flag;
+ /*printf("arg_free(%p)\n",argtable);*/
+ do
+ {
+ /*
+ if we encounter a NULL entry then somewhat incorrectly we presume
+ we have come to the end of the array. It isnt strictly true because
+ an intermediate entry could be NULL with other non-NULL entries to follow.
+ The subsequent argtable entries would then not be freed as they should.
+ */
+ if (table[tabindex] == NULL)
+ break;
+
+ flag = table[tabindex]->flag;
+ free(table[tabindex]);
+ table[tabindex++] = NULL;
+
+ } while(!(flag & ARG_TERMINATOR));
+}
+
+/* frees each non-NULL element of argtable[], where n is the size of the number of entries in the array */
+void arg_freetable(void * *argtable, size_t n)
+{
+ struct arg_hdr * *table = (struct arg_hdr * *)argtable;
+ size_t tabindex = 0;
+ /*printf("arg_freetable(%p)\n",argtable);*/
+ for (tabindex = 0; tabindex < n; tabindex++)
+ {
+ if (table[tabindex] == NULL)
+ continue;
+
+ free(table[tabindex]);
+ table[tabindex] = NULL;
+ };
+}
+
diff --git a/Projects/Android/jni/RTCWVR/argtable3.h b/Projects/Android/jni/RTCWVR/argtable3.h
new file mode 100644
index 0000000..1107de2
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/argtable3.h
@@ -0,0 +1,305 @@
+/*******************************************************************************
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#ifndef ARGTABLE3
+#define ARGTABLE3
+
+#include /* FILE */
+#include /* struct tm */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ARG_REX_ICASE 1
+
+/* bit masks for arg_hdr.flag */
+enum
+{
+ ARG_TERMINATOR=0x1,
+ ARG_HASVALUE=0x2,
+ ARG_HASOPTVALUE=0x4
+};
+
+typedef void (arg_resetfn)(void *parent);
+typedef int (arg_scanfn)(void *parent, const char *argval);
+typedef int (arg_checkfn)(void *parent);
+typedef void (arg_errorfn)(void *parent, FILE *fp, int error, const char *argval, const char *progname);
+
+
+/*
+* The arg_hdr struct defines properties that are common to all arg_xxx structs.
+* The argtable library requires each arg_xxx struct to have an arg_hdr
+* struct as its first data member.
+* The argtable library functions then use this data to identify the
+* properties of the command line option, such as its option tags,
+* datatype string, and glossary strings, and so on.
+* Moreover, the arg_hdr struct contains pointers to custom functions that
+* are provided by each arg_xxx struct which perform the tasks of parsing
+* that particular arg_xxx arguments, performing post-parse checks, and
+* reporting errors.
+* These functions are private to the individual arg_xxx source code
+* and are the pointer to them are initiliased by that arg_xxx struct's
+* constructor function. The user could alter them after construction
+* if desired, but the original intention is for them to be set by the
+* constructor and left unaltered.
+*/
+struct arg_hdr
+{
+ char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */
+ const char *shortopts; /* String defining the short options */
+ const char *longopts; /* String defiing the long options */
+ const char *datatype; /* Description of the argument data type */
+ const char *glossary; /* Description of the option as shown by arg_print_glossary function */
+ int mincount; /* Minimum number of occurences of this option accepted */
+ int maxcount; /* Maximum number of occurences if this option accepted */
+ void *parent; /* Pointer to parent arg_xxx struct */
+ arg_resetfn *resetfn; /* Pointer to parent arg_xxx reset function */
+ arg_scanfn *scanfn; /* Pointer to parent arg_xxx scan function */
+ arg_checkfn *checkfn; /* Pointer to parent arg_xxx check function */
+ arg_errorfn *errorfn; /* Pointer to parent arg_xxx error function */
+ void *priv; /* Pointer to private header data for use by arg_xxx functions */
+};
+
+struct arg_rem
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+};
+
+struct arg_lit
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args */
+};
+
+struct arg_int
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args */
+ int *ival; /* Array of parsed argument values */
+};
+
+struct arg_dbl
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args */
+ double *dval; /* Array of parsed argument values */
+};
+
+struct arg_str
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args */
+ const char **sval; /* Array of parsed argument values */
+};
+
+struct arg_rex
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args */
+ const char **sval; /* Array of parsed argument values */
+};
+
+struct arg_file
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of matching command line args*/
+ const char **filename; /* Array of parsed filenames (eg: /home/foo.bar) */
+ const char **basename; /* Array of parsed basenames (eg: foo.bar) */
+ const char **extension; /* Array of parsed extensions (eg: .bar) */
+};
+
+struct arg_date
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ const char *format; /* strptime format string used to parse the date */
+ int count; /* Number of matching command line args */
+ struct tm *tmval; /* Array of parsed time values */
+};
+
+enum {ARG_ELIMIT=1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG};
+struct arg_end
+{
+ struct arg_hdr hdr; /* The mandatory argtable header struct */
+ int count; /* Number of errors encountered */
+ int *error; /* Array of error codes */
+ void **parent; /* Array of pointers to offending arg_xxx struct */
+ const char **argval; /* Array of pointers to offending argv[] string */
+};
+
+
+/**** arg_xxx constructor functions *********************************/
+
+struct arg_rem* arg_rem(const char* datatype, const char* glossary);
+
+struct arg_lit* arg_lit0(const char* shortopts,
+ const char* longopts,
+ const char* glossary);
+struct arg_lit* arg_lit1(const char* shortopts,
+ const char* longopts,
+ const char *glossary);
+struct arg_lit* arg_litn(const char* shortopts,
+ const char* longopts,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_key* arg_key0(const char* keyword,
+ int flags,
+ const char* glossary);
+struct arg_key* arg_key1(const char* keyword,
+ int flags,
+ const char* glossary);
+struct arg_key* arg_keyn(const char* keyword,
+ int flags,
+ int mincount,
+ int maxcount,
+ const char* glossary);
+
+struct arg_int* arg_int0(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char* glossary);
+struct arg_int* arg_int1(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char *glossary);
+struct arg_int* arg_intn(const char* shortopts,
+ const char* longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_dbl* arg_dbl0(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char* glossary);
+struct arg_dbl* arg_dbl1(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char *glossary);
+struct arg_dbl* arg_dbln(const char* shortopts,
+ const char* longopts,
+ const char *datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_str* arg_str0(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char* glossary);
+struct arg_str* arg_str1(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char *glossary);
+struct arg_str* arg_strn(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_rex* arg_rex0(const char* shortopts,
+ const char* longopts,
+ const char* pattern,
+ const char* datatype,
+ int flags,
+ const char* glossary);
+struct arg_rex* arg_rex1(const char* shortopts,
+ const char* longopts,
+ const char* pattern,
+ const char* datatype,
+ int flags,
+ const char *glossary);
+struct arg_rex* arg_rexn(const char* shortopts,
+ const char* longopts,
+ const char* pattern,
+ const char* datatype,
+ int mincount,
+ int maxcount,
+ int flags,
+ const char *glossary);
+
+struct arg_file* arg_file0(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char* glossary);
+struct arg_file* arg_file1(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ const char *glossary);
+struct arg_file* arg_filen(const char* shortopts,
+ const char* longopts,
+ const char* datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_date* arg_date0(const char* shortopts,
+ const char* longopts,
+ const char* format,
+ const char* datatype,
+ const char* glossary);
+struct arg_date* arg_date1(const char* shortopts,
+ const char* longopts,
+ const char* format,
+ const char* datatype,
+ const char *glossary);
+struct arg_date* arg_daten(const char* shortopts,
+ const char* longopts,
+ const char* format,
+ const char* datatype,
+ int mincount,
+ int maxcount,
+ const char *glossary);
+
+struct arg_end* arg_end(int maxerrors);
+
+
+/**** other functions *******************************************/
+int arg_nullcheck(void **argtable);
+int arg_parse(int argc, char **argv, void **argtable);
+void arg_print_option(FILE *fp, const char *shortopts, const char *longopts, const char *datatype, const char *suffix);
+void arg_print_syntax(FILE *fp, void **argtable, const char *suffix);
+void arg_print_syntaxv(FILE *fp, void **argtable, const char *suffix);
+void arg_print_glossary(FILE *fp, void **argtable, const char *format);
+void arg_print_glossary_gnu(FILE *fp, void **argtable);
+void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname);
+void arg_freetable(void **argtable, size_t n);
+
+/**** deprecated functions, for back-compatibility only ********/
+void arg_free(void **argtable);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/Projects/Android/jni/RTCWVR/mathlib.c b/Projects/Android/jni/RTCWVR/mathlib.c
new file mode 100644
index 0000000..6f6d1a7
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/mathlib.c
@@ -0,0 +1,525 @@
+/*
+mathlib.c - internal mathlib
+Copyright (C) 2010 Uncle Mike
+
+This program 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.
+*/
+
+#include "mathlib.h"
+
+unsigned short FloatToHalf( float v )
+{
+ unsigned int i = *((unsigned int *)&v);
+ unsigned int e = (i >> 23) & 0x00ff;
+ unsigned int m = i & 0x007fffff;
+ unsigned short h;
+
+ if( e <= 127 - 15 )
+ h = ((m | 0x00800000) >> (127 - 14 - e)) >> 13;
+ else h = (i >> 13) & 0x3fff;
+
+ h |= (i >> 16) & 0xc000;
+
+ return h;
+}
+
+float HalfToFloat( unsigned short h )
+{
+ unsigned int f = (h << 16) & 0x80000000;
+ unsigned int em = h & 0x7fff;
+
+ if( em > 0x03ff )
+ {
+ f |= (em << 13) + ((127 - 15) << 23);
+ }
+ else
+ {
+ unsigned int m = em & 0x03ff;
+
+ if( m != 0 )
+ {
+ unsigned int e = (em >> 10) & 0x1f;
+
+ while(( m & 0x0400 ) == 0 )
+ {
+ m <<= 1;
+ e--;
+ }
+
+ m &= 0x3ff;
+ f |= ((e + (127 - 14)) << 23) | (m << 13);
+ }
+ }
+
+ return *((float *)&f);
+}
+
+
+/*
+=================
+NearestPOW
+=================
+*/
+int NearestPOW( int value, qboolean roundDown )
+{
+ int n = 1;
+
+ if( value <= 0 ) return 1;
+ while( n < value ) n <<= 1;
+
+ if( roundDown )
+ {
+ if( n > value ) n >>= 1;
+ }
+ return n;
+}
+
+// remap a value in the range [A,B] to [C,D].
+float RemapVal( float val, float A, float B, float C, float D )
+{
+ return C + (D - C) * (val - A) / (B - A);
+}
+
+float ApproachVal( float target, float value, float speed )
+{
+ float delta = target - value;
+
+ if( delta > speed )
+ value += speed;
+ else if( delta < -speed )
+ value -= speed;
+ else value = target;
+
+ return value;
+}
+
+/*
+=================
+rsqrt
+=================
+*/
+float rsqrt( float number )
+{
+ int i;
+ float x, y;
+
+ if( number == 0.0f )
+ return 0.0f;
+
+ x = number * 0.5f;
+ i = *(int *)&number; // evil floating point bit level hacking
+ i = 0x5f3759df - (i >> 1); // what the fuck?
+ y = *(float *)&i;
+ y = y * (1.5f - (x * y * y)); // first iteration
+
+ return y;
+}
+
+/*
+=================
+SinCos
+=================
+*/
+void SinCos( float radians, float *sine, float *cosine )
+{
+#if _MSC_VER == 1200
+ _asm
+ {
+ fld dword ptr [radians]
+ fsincos
+
+ mov edx, dword ptr [cosine]
+ mov eax, dword ptr [sine]
+
+ fstp dword ptr [edx]
+ fstp dword ptr [eax]
+ }
+#else
+ // I think, better use math.h function, instead of ^
+#if defined (__linux__) && !defined (__ANDROID__)
+ sincosf(radians, sine, cosine);
+#else
+ *sine = sinf(radians);
+ *cosine = cosf(radians);
+#endif
+#endif
+}
+
+#ifdef XASH_VECTORIZE_SINCOS
+void SinCosFastVector4(float r1, float r2, float r3, float r4,
+ float *s0, float *s1, float *s2, float *s3,
+ float *c0, float *c1, float *c2, float *c3)
+{
+ v4sf rad_vector = {r1, r2, r3, r4};
+ v4sf sin_vector, cos_vector;
+
+ sincos_ps(rad_vector, &sin_vector, &cos_vector);
+
+ *s0 = s4f_x(sin_vector);
+ *s1 = s4f_y(sin_vector);
+ *s2 = s4f_z(sin_vector);
+ *s3 = s4f_w(sin_vector);
+
+ *c0 = s4f_x(cos_vector);
+ *c1 = s4f_y(cos_vector);
+ *c2 = s4f_z(cos_vector);
+ *c3 = s4f_w(cos_vector);
+}
+
+void SinCosFastVector3(float r1, float r2, float r3,
+ float *s0, float *s1, float *s2,
+ float *c0, float *c1, float *c2)
+{
+ v4sf rad_vector = {r1, r2, r3, 0};
+ v4sf sin_vector, cos_vector;
+
+ sincos_ps(rad_vector, &sin_vector, &cos_vector);
+
+ *s0 = s4f_x(sin_vector);
+ *s1 = s4f_y(sin_vector);
+ *s2 = s4f_z(sin_vector);
+
+ *c0 = s4f_x(cos_vector);
+ *c1 = s4f_y(cos_vector);
+ *c2 = s4f_z(cos_vector);
+}
+
+void SinCosFastVector2(float r1, float r2,
+ float *s0, float *s1,
+ float *c0, float *c1)
+{
+ v4sf rad_vector = {r1, r2, 0, 0};
+ v4sf sin_vector, cos_vector;
+
+ sincos_ps(rad_vector, &sin_vector, &cos_vector);
+
+ *s0 = s4f_x(sin_vector);
+ *s1 = s4f_y(sin_vector);
+
+ *c0 = s4f_x(cos_vector);
+ *c1 = s4f_y(cos_vector);
+}
+
+void SinFastVector3(float r1, float r2, float r3,
+ float *s0, float *s1, float *s2)
+{
+ v4sf rad_vector = {r1, r2, r3, 0};
+ v4sf sin_vector;
+
+ sin_vector = sin_ps(rad_vector);
+
+ *s0 = s4f_x(sin_vector);
+ *s1 = s4f_y(sin_vector);
+ *s2 = s4f_z(sin_vector);
+}
+#endif
+
+float VectorNormalizeLength2( const vec3_t v, vec3_t out )
+{
+ float length, ilength;
+
+ length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
+ length = sqrt( length );
+
+ if( length )
+ {
+ ilength = 1.0f / length;
+ out[0] = v[0] * ilength;
+ out[1] = v[1] * ilength;
+ out[2] = v[2] * ilength;
+ }
+
+ return length;
+}
+
+void VectorVectors( const vec3_t forward, vec3_t right, vec3_t up )
+{
+ float d;
+
+ right[0] = forward[2];
+ right[1] = -forward[0];
+ right[2] = forward[1];
+
+ d = DotProduct( forward, right );
+ VectorMA( right, -d, forward, right );
+ VectorNormalize( right );
+ CrossProduct( right, forward, up );
+}
+
+/*
+=================
+AngleVectors
+
+=================
+
+void GAME_EXPORT AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up )
+{
+ static float sr, sp, sy, cr, cp, cy;
+
+#ifdef XASH_VECTORIZE_SINCOS
+ SinCosFastVector3( DEG2RAD(angles[YAW]), DEG2RAD(angles[PITCH]), DEG2RAD(angles[ROLL]),
+ &sy, &sp, &sr,
+ &cy, &cp, &cr);
+#else
+ SinCos( DEG2RAD( angles[YAW] ), &sy, &cy );
+ SinCos( DEG2RAD( angles[PITCH] ), &sp, &cp );
+ SinCos( DEG2RAD( angles[ROLL] ), &sr, &cr );
+#endif
+
+ if( forward )
+ {
+ forward[0] = cp * cy;
+ forward[1] = cp * sy;
+ forward[2] = -sp;
+ }
+
+ if( right )
+ {
+ right[0] = (-1.0f * sr * sp * cy + -1.0f * cr * -sy );
+ right[1] = (-1.0f * sr * sp * sy + -1.0f * cr * cy );
+ right[2] = (-1.0f * sr * cp);
+ }
+
+ if( up )
+ {
+ up[0] = (cr * sp * cy + -sr * -sy );
+ up[1] = (cr * sp * sy + -sr * cy );
+ up[2] = (cr * cp);
+ }
+}
+
+*
+=================
+VectorAngles
+
+=================
+*/
+void VectorAngles( const float *forward, float *angles )
+{
+ float tmp, yaw, pitch;
+
+ if( !forward || !angles )
+ {
+ if( angles ) VectorClear( angles );
+ return;
+ }
+
+ if( forward[1] == 0 && forward[0] == 0 )
+ {
+ // fast case
+ yaw = 0;
+ if( forward[2] > 0 )
+ pitch = 90.0f;
+ else pitch = 270.0f;
+ }
+ else
+ {
+ yaw = ( atan2( forward[1], forward[0] ) * 180 / M_PI );
+ if( yaw < 0 ) yaw += 360;
+
+ tmp = sqrt( forward[0] * forward[0] + forward[1] * forward[1] );
+ pitch = ( atan2( forward[2], tmp ) * 180 / M_PI );
+ if( pitch < 0 ) pitch += 360;
+ }
+
+ VectorSet( angles, pitch, yaw, 0 );
+}
+
+/*
+=================
+VectorsAngles
+
+=================
+*/
+void VectorsAngles( const vec3_t forward, const vec3_t right, const vec3_t up, vec3_t angles )
+{
+ float pitch, cpitch, yaw, roll;
+
+ pitch = -asin( forward[2] );
+ cpitch = cos( pitch );
+
+ if( fabs( cpitch ) > EQUAL_EPSILON ) // gimball lock?
+ {
+ cpitch = 1.0f / cpitch;
+ pitch = RAD2DEG( pitch );
+ yaw = RAD2DEG( atan2( forward[1] * cpitch, forward[0] * cpitch ));
+ roll = RAD2DEG( atan2( -right[2] * cpitch, up[2] * cpitch ));
+ }
+ else
+ {
+ pitch = forward[2] > 0 ? -90.0f : 90.0f;
+ yaw = RAD2DEG( atan2( right[0], -right[1] ));
+ roll = 180.0f;
+ }
+
+ angles[PITCH] = pitch;
+ angles[YAW] = yaw;
+ angles[ROLL] = roll;
+}
+
+/*
+=================
+InterpolateAngles
+=================
+*/
+void InterpolateAngles( vec3_t start, vec3_t end, vec3_t out, float frac )
+{
+ float d, ang1, ang2;
+ int i;
+ for( i = 0; i < 3; i++ )
+ {
+ ang1 = start[i];
+ ang2 = end[i];
+ d = ang1 - ang2;
+
+ if( d > 180.0f ) d -= 360.0f;
+ else if( d < -180.0f ) d += 360.0f;
+
+ out[i] = ang2 + d * frac;
+ }
+}
+
+
+/*
+=================
+BoundsIntersect
+=================
+*/
+qboolean BoundsIntersect( const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2 )
+{
+ if( mins1[0] > maxs2[0] || mins1[1] > maxs2[1] || mins1[2] > maxs2[2] )
+ return false;
+ if( maxs1[0] < mins2[0] || maxs1[1] < mins2[1] || maxs1[2] < mins2[2] )
+ return false;
+ return true;
+}
+
+/*
+=================
+BoundsAndSphereIntersect
+=================
+*/
+qboolean BoundsAndSphereIntersect( const vec3_t mins, const vec3_t maxs, const vec3_t origin, float radius )
+{
+ if( mins[0] > origin[0] + radius || mins[1] > origin[1] + radius || mins[2] > origin[2] + radius )
+ return false;
+ if( maxs[0] < origin[0] - radius || maxs[1] < origin[1] - radius || maxs[2] < origin[2] - radius )
+ return false;
+ return true;
+}
+
+
+
+//
+// studio utils
+//
+/*
+====================
+AngleQuaternion
+
+====================
+*/
+void AngleQuaternion( const vec3_t angles, vec4_t q )
+{
+ float sr, sp, sy, cr, cp, cy;
+
+#ifdef XASH_VECTORIZE_SINCOS
+ SinCosFastVector3( angles[2] * 0.5f, angles[1] * 0.5f, angles[0] * 0.5f,
+ &sy, &sp, &sr,
+ &cy, &cp, &cr);
+#else
+ float angle;
+
+ angle = angles[2] * 0.5f;
+ SinCos( angle, &sy, &cy );
+ angle = angles[1] * 0.5f;
+ SinCos( angle, &sp, &cp );
+ angle = angles[0] * 0.5f;
+ SinCos( angle, &sr, &cr );
+#endif
+
+ q[0] = sr * cp * cy - cr * sp * sy; // X
+ q[1] = cr * sp * cy + sr * cp * sy; // Y
+ q[2] = cr * cp * sy - sr * sp * cy; // Z
+ q[3] = cr * cp * cy + sr * sp * sy; // W
+}
+
+/*
+====================
+QuaternionSlerp
+
+====================
+*/
+void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt )
+{
+ float omega, sclp, sclq;
+ float cosom, sinom;
+ float a = 0.0f;
+ float b = 0.0f;
+ int i;
+
+ // decide if one of the quaternions is backwards
+ for( i = 0; i < 4; i++ )
+ {
+ a += (p[i] - q[i]) * (p[i] - q[i]);
+ b += (p[i] + q[i]) * (p[i] + q[i]);
+ }
+
+ if( a > b )
+ {
+ for( i = 0; i < 4; i++ )
+ {
+ q[i] = -q[i];
+ }
+ }
+
+ cosom = p[0] * q[0] + p[1] * q[1] + p[2] * q[2] + p[3] * q[3];
+
+ if(( 1.0 + cosom ) > 0.000001f )
+ {
+ if(( 1.0f - cosom ) > 0.000001f )
+ {
+ omega = acos( cosom );
+
+#ifdef XASH_VECTORIZE_SINCOS
+ SinFastVector3( omega, ( 1.0f - t ) * omega, t * omega,
+ &sinom, &sclp, &sclq );
+ sclp /= sinom;
+ sclq /= sinom;
+#else
+ sinom = sin( omega );
+ sclp = sin(( 1.0f - t ) * omega ) / sinom;
+ sclq = sin( t * omega ) / sinom;
+#endif
+
+ }
+ else
+ {
+ sclp = 1.0f - t;
+ sclq = t;
+ }
+
+ for( i = 0; i < 4; i++ )
+ qt[i] = sclp * p[i] + sclq * q[i];
+ }
+ else
+ {
+ qt[0] = -q[1];
+ qt[1] = q[0];
+ qt[2] = -q[3];
+ qt[3] = q[2];
+ sclp = sin(( 1.0f - t ) * ( 0.5f * M_PI ));
+ sclq = sin( t * ( 0.5f * M_PI ));
+
+ for( i = 0; i < 3; i++ )
+ qt[i] = sclp * p[i] + sclq * qt[i];
+ }
+}
diff --git a/Projects/Android/jni/RTCWVR/mathlib.h b/Projects/Android/jni/RTCWVR/mathlib.h
new file mode 100644
index 0000000..67431d2
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/mathlib.h
@@ -0,0 +1,211 @@
+/*
+mathlib.h - base math functions
+Copyright (C) 2007 Uncle Mike
+
+This program 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.
+*/
+
+#ifndef MATHLIB_H
+#define MATHLIB_H
+
+#include
+
+#include "../quake2/src/common/header/common.h"
+#include "../quake2/src/client/header/keyboard.h"
+
+
+// euler angle order
+#define PITCH 0
+#define YAW 1
+#define ROLL 2
+
+#ifndef M_PI
+#define M_PI (float)3.14159265358979323846
+#endif
+
+#ifndef M_PI2
+#define M_PI2 (float)6.28318530717958647692
+#endif
+
+#define M_PI_F ((float)(M_PI))
+#define M_PI2_F ((float)(M_PI2))
+
+#define RAD2DEG( x ) ((float)(x) * (float)(180.f / M_PI))
+#define DEG2RAD( x ) ((float)(x) * (float)(M_PI / 180.f))
+
+#define SIDE_FRONT 0
+#define SIDE_BACK 1
+#define SIDE_ON 2
+#define SIDE_CROSS -2
+
+#define PLANE_X 0 // 0 - 2 are axial planes
+#define PLANE_Y 1 // 3 needs alternate calc
+#define PLANE_Z 2
+#define PLANE_NONAXIAL 3
+
+#define EQUAL_EPSILON 0.001f
+#define STOP_EPSILON 0.1f
+#define ON_EPSILON 0.1f
+
+#define RAD_TO_STUDIO (32768.0 / M_PI)
+#define STUDIO_TO_RAD (M_PI / 32768.0)
+#define nanmask (255<<23)
+
+#define Q_rint(x) ((x) < 0 ? ((int)((x)-0.5f)) : ((int)((x)+0.5f)))
+#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)
+
+#define VectorIsNAN(v) (IS_NAN(v[0]) || IS_NAN(v[1]) || IS_NAN(v[2]))
+//#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])
+#define DotProductAbs(x,y) (abs((x)[0]*(y)[0])+abs((x)[1]*(y)[1])+abs((x)[2]*(y)[2]))
+#define DotProductFabs(x,y) (fabs((x)[0]*(y)[0])+fabs((x)[1]*(y)[1])+fabs((x)[2]*(y)[2]))
+#define CrossProduct(a,b,c) ((c)[0]=(a)[1]*(b)[2]-(a)[2]*(b)[1],(c)[1]=(a)[2]*(b)[0]-(a)[0]*(b)[2],(c)[2]=(a)[0]*(b)[1]-(a)[1]*(b)[0])
+#define Vector2Subtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1])
+//#define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2])
+#define Vector2Add(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1])
+//#define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2])
+#define Vector2Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1])
+//#define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2])
+#define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3])
+//#define VectorScale(in, scale, out) ((out)[0] = (in)[0] * (scale),(out)[1] = (in)[1] * (scale),(out)[2] = (in)[2] * (scale))
+#define VectorCompare(v1,v2) ((v1)[0]==(v2)[0] && (v1)[1]==(v2)[1] && (v1)[2]==(v2)[2])
+//#define VectorDivide( in, d, out ) VectorScale( in, (1.0f / (d)), out )
+#define VectorMax(a) ( max((a)[0], max((a)[1], (a)[2])) )
+#define VectorAvg(a) ( ((a)[0] + (a)[1] + (a)[2]) / 3 )
+#define VectorLength(a) ( sqrt( DotProduct( a, a )))
+#define VectorLength2(a) (DotProduct( a, a ))
+#define VectorDistance(a, b) (sqrt( VectorDistance2( a, b )))
+#define VectorDistance2(a, b) (((a)[0] - (b)[0]) * ((a)[0] - (b)[0]) + ((a)[1] - (b)[1]) * ((a)[1] - (b)[1]) + ((a)[2] - (b)[2]) * ((a)[2] - (b)[2]))
+#define Vector2Average(a,b,o) ((o)[0]=((a)[0]+(b)[0])*0.5,(o)[1]=((a)[1]+(b)[1])*0.5)
+#define VectorAverage(a,b,o) ((o)[0]=((a)[0]+(b)[0])*0.5,(o)[1]=((a)[1]+(b)[1])*0.5,(o)[2]=((a)[2]+(b)[2])*0.5)
+#define Vector2Set(v, x, y) ((v)[0]=(x),(v)[1]=(y))
+//#define VectorSet(v, x, y, z) ((v)[0]=(x),(v)[1]=(y),(v)[2]=(z))
+#define Vector4Set(v, a, b, c, d) ((v)[0]=(a),(v)[1]=(b),(v)[2]=(c),(v)[3] = (d))
+//#define VectorClear(x) ((x)[0]=(x)[1]=(x)[2]=0)
+#define Vector2Lerp( v1, lerp, v2, c ) ((c)[0] = (v1)[0] + (lerp) * ((v2)[0] - (v1)[0]), (c)[1] = (v1)[1] + (lerp) * ((v2)[1] - (v1)[1]))
+#define VectorLerp( v1, lerp, v2, c ) ((c)[0] = (v1)[0] + (lerp) * ((v2)[0] - (v1)[0]), (c)[1] = (v1)[1] + (lerp) * ((v2)[1] - (v1)[1]), (c)[2] = (v1)[2] + (lerp) * ((v2)[2] - (v1)[2]))
+#define VectorNormalize( v ) { float ilength = (float)sqrt(DotProduct(v, v));if (ilength) ilength = 1.0f / ilength;v[0] *= ilength;v[1] *= ilength;v[2] *= ilength; }
+#define VectorNormalize2( v, dest ) {float ilength = (float)sqrt(DotProduct(v,v));if (ilength) ilength = 1.0f / ilength;dest[0] = v[0] * ilength;dest[1] = v[1] * ilength;dest[2] = v[2] * ilength; }
+#define VectorNormalizeFast( v ) {float ilength = (float)rsqrt(DotProduct(v,v)); v[0] *= ilength; v[1] *= ilength; v[2] *= ilength; }
+#define VectorNormalizeLength( v ) VectorNormalizeLength2((v), (v))
+//#define VectorNegate(x, y) ((y)[0] = -(x)[0], (y)[1] = -(x)[1], (y)[2] = -(x)[2])
+#define VectorM(scale1, b1, c) ((c)[0] = (scale1) * (b1)[0],(c)[1] = (scale1) * (b1)[1],(c)[2] = (scale1) * (b1)[2])
+#define VectorMA(a, scale, b, c) ((c)[0] = (a)[0] + (scale) * (b)[0],(c)[1] = (a)[1] + (scale) * (b)[1],(c)[2] = (a)[2] + (scale) * (b)[2])
+#define VectorMAMAM(scale1, b1, scale2, b2, scale3, b3, c) ((c)[0] = (scale1) * (b1)[0] + (scale2) * (b2)[0] + (scale3) * (b3)[0],(c)[1] = (scale1) * (b1)[1] + (scale2) * (b2)[1] + (scale3) * (b3)[1],(c)[2] = (scale1) * (b1)[2] + (scale2) * (b2)[2] + (scale3) * (b3)[2])
+#define VectorIsNull( v ) ((v)[0] == 0.0f && (v)[1] == 0.0f && (v)[2] == 0.0f)
+#define Vector2IsNull( v ) ((v)[0] == 0.0f && (v)[1] == 0.0f)
+#define MakeRGBA( out, x, y, z, w ) Vector4Set( out, x, y, z, w )
+#define PlaneDist(point,plane) ((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal))
+#define PlaneDiff(point,plane) (((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal)) - (plane)->dist)
+#define boundmax( num, high ) ( (num) < (high) ? (num) : (high) )
+#define boundmin( num, low ) ( (num) >= (low) ? (num) : (low) )
+#define bound( low, num, high ) ( boundmin( boundmax(num, high), low ))
+//#define bound( min, num, max ) ((num) >= (min) ? ((num) < (max) ? (num) : (max)) : (min))
+
+float rsqrt( float number );
+float anglemod( const float a );
+unsigned short FloatToHalf( float v );
+float HalfToFloat( unsigned short h );
+int SignbitsForPlane( const vec3_t normal );
+int NearestPOW( int value, qboolean roundDown );
+void SinCos( float radians, float *sine, float *cosine );
+#ifdef XASH_VECTORIZE_SINCOS
+void SinCosFastVector4(float r1, float r2, float r3, float r4,
+ float *s0, float *s1, float *s2, float *s3,
+ float *c0, float *c1, float *c2, float *c3)
+#if defined(__GNUC__)
+ __attribute__((nonnull))
+#endif
+;
+
+void SinCosFastVector3( float r1, float r2, float r3,
+ float *s0, float *s1, float *s2,
+ float *c0, float *c1, float *c2)
+#if defined(__GNUC__)
+ __attribute__((nonnull))
+#endif
+;
+
+void SinCosFastVector2( float r1, float r2,
+ float *s0, float *s1,
+ float *c0, float *c1)
+#if defined(__GNUC__)
+ __attribute__((nonnull))
+#endif
+;
+#endif
+float VectorNormalizeLength2( const vec3_t v, vec3_t out );
+void VectorVectors( const vec3_t forward, vec3_t right, vec3_t up );
+void VectorAngles( const float *forward, float *angles );
+//void AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up );
+void VectorsAngles( const vec3_t forward, const vec3_t right, const vec3_t up, vec3_t angles );
+void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees );
+
+void ClearBounds( vec3_t mins, vec3_t maxs );
+//void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs );
+qboolean BoundsIntersect( const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2 );
+qboolean BoundsAndSphereIntersect( const vec3_t mins, const vec3_t maxs, const vec3_t origin, float radius );
+float RadiusFromBounds( const vec3_t mins, const vec3_t maxs );
+
+void AngleQuaternion( const vec3_t angles, vec4_t q );
+void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt );
+float RemapVal( float val, float A, float B, float C, float D );
+float ApproachVal( float target, float value, float speed );
+void InterpolateAngles( vec3_t start, vec3_t end, vec3_t output, float frac );
+
+//
+// matrixlib.c
+//
+#define Matrix3x4_LoadIdentity( mat ) Matrix3x4_Copy( mat, matrix3x4_identity )
+#define Matrix3x4_Copy( out, in ) Q_memcpy( out, in, sizeof( matrix3x4 ))
+
+#define cmatrix3x4 vec4_t *const
+#define cmatrix4x4 vec4_t *const
+typedef vec_t matrix3x4[3][4];
+typedef vec_t matrix4x4[4][4];
+
+void Matrix3x4_VectorTransform( cmatrix3x4 in, const float v[3], float out[3] );
+void Matrix3x4_VectorITransform( cmatrix3x4 in, const float v[3], float out[3] );
+void Matrix3x4_VectorRotate( cmatrix3x4 in, const float v[3], float out[3] );
+void Matrix3x4_VectorIRotate( cmatrix3x4 in, const float v[3], float out[3] );
+void Matrix3x4_ConcatTransforms( matrix3x4 out, cmatrix3x4 in1, cmatrix3x4 in2 );
+void Matrix3x4_FromOriginQuat( matrix3x4 out, const vec4_t quaternion, const vec3_t origin );
+void Matrix3x4_CreateFromEntity( matrix3x4 out, const vec3_t angles, const vec3_t origin, float scale );
+void Matrix3x4_TransformPositivePlane( cmatrix3x4 in, const vec3_t normal, float d, vec3_t out, float *dist );
+void Matrix3x4_SetOrigin( matrix3x4 out, float x, float y, float z );
+void Matrix3x4_Invert_Simple( matrix3x4 out, cmatrix3x4 in1 );
+void Matrix3x4_OriginFromMatrix( cmatrix3x4 in, float *out );
+
+#define Matrix4x4_LoadIdentity( mat ) Matrix4x4_Copy( mat, matrix4x4_identity )
+#define Matrix4x4_Copy( out, in ) Q_memcpy( out, in, sizeof( matrix4x4 ))
+
+void Matrix4x4_Concat (matrix4x4 out, const matrix4x4 in1, const matrix4x4 in2);
+void Matrix4x4_CreateTranslate (matrix4x4 out, double x, double y, double z);
+void Matrix4x4_VectorTransform( cmatrix4x4 in, const float v[3], float out[3] );
+void Matrix4x4_VectorITransform( cmatrix4x4 in, const float v[3], float out[3] );
+void Matrix4x4_VectorRotate( cmatrix4x4 in, const float v[3], float out[3] );
+void Matrix4x4_VectorIRotate( cmatrix4x4 in, const float v[3], float out[3] );
+void Matrix4x4_ConcatTransforms( matrix4x4 out, cmatrix4x4 in1, cmatrix4x4 in2 );
+void Matrix4x4_FromOriginQuat( matrix4x4 out, const vec4_t quaternion, const vec3_t origin );
+void Matrix4x4_CreateFromEntity( matrix4x4 out, const vec3_t angles, const vec3_t origin, float scale );
+void Matrix4x4_TransformPositivePlane( cmatrix4x4 in, const vec3_t normal, float d, vec3_t out, float *dist );
+void Matrix4x4_TransformStandardPlane( cmatrix4x4 in, const vec3_t normal, float d, vec3_t out, float *dist );
+void Matrix4x4_ConvertToEntity( cmatrix4x4 in, vec3_t angles, vec3_t origin );
+void Matrix4x4_SetOrigin( matrix4x4 out, float x, float y, float z );
+void Matrix4x4_Invert_Simple( matrix4x4 out, cmatrix4x4 in1 );
+void Matrix4x4_OriginFromMatrix( cmatrix4x4 in, float *out );
+void Matrix4x4_Transpose( matrix4x4 out, cmatrix4x4 in1 );
+qboolean Matrix4x4_Invert_Full( matrix4x4 out, cmatrix4x4 in1 );
+
+extern vec3_t vec3_origin;
+extern const matrix3x4 matrix3x4_identity;
+extern const matrix4x4 matrix4x4_identity;
+
+#endif//MATHLIB_H
diff --git a/Projects/Android/jni/RTCWVR/matrixlib.c b/Projects/Android/jni/RTCWVR/matrixlib.c
new file mode 100644
index 0000000..e7e9d90
--- /dev/null
+++ b/Projects/Android/jni/RTCWVR/matrixlib.c
@@ -0,0 +1,852 @@
+/*
+matrixlib.c - internal matrixlib
+Copyright (C) 2010 Uncle Mike
+
+This program 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.
+*/
+
+//#include "common.h"
+#include "mathlib.h"
+
+const matrix3x4 matrix3x4_identity =
+{
+{ 1, 0, 0, 0 }, // PITCH [forward], org[0]
+{ 0, 1, 0, 0 }, // YAW [right] , org[1]
+{ 0, 0, 1, 0 }, // ROLL [up] , org[2]
+};
+
+/*
+========================================================================
+
+ Matrix3x4 operations
+
+========================================================================
+*/
+void Matrix3x4_VectorTransform( cmatrix3x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + in[0][3];
+ out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + in[1][3];
+ out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] + in[2][3];
+}
+
+void Matrix3x4_VectorITransform( cmatrix3x4 in, const float v[3], float out[3] )
+{
+ vec3_t dir;
+
+ dir[0] = v[0] - in[0][3];
+ dir[1] = v[1] - in[1][3];
+ dir[2] = v[2] - in[2][3];
+
+ out[0] = dir[0] * in[0][0] + dir[1] * in[1][0] + dir[2] * in[2][0];
+ out[1] = dir[0] * in[0][1] + dir[1] * in[1][1] + dir[2] * in[2][1];
+ out[2] = dir[0] * in[0][2] + dir[1] * in[1][2] + dir[2] * in[2][2];
+}
+
+void Matrix3x4_VectorRotate( cmatrix3x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2];
+ out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2];
+ out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2];
+}
+
+void Matrix3x4_VectorIRotate( cmatrix3x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[1][0] + v[2] * in[2][0];
+ out[1] = v[0] * in[0][1] + v[1] * in[1][1] + v[2] * in[2][1];
+ out[2] = v[0] * in[0][2] + v[1] * in[1][2] + v[2] * in[2][2];
+}
+
+void Matrix3x4_ConcatTransforms( matrix3x4 out, cmatrix3x4 in1, cmatrix3x4 in2 )
+{
+ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
+ out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
+ out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2];
+ out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3];
+ out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0];
+ out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1];
+ out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2];
+ out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3];
+ out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0];
+ out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1];
+ out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2];
+ out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3];
+}
+
+void Matrix3x4_SetOrigin( matrix3x4 out, float x, float y, float z )
+{
+ out[0][3] = x;
+ out[1][3] = y;
+ out[2][3] = z;
+}
+
+void Matrix3x4_OriginFromMatrix( cmatrix3x4 in, float *out )
+{
+ out[0] = in[0][3];
+ out[1] = in[1][3];
+ out[2] = in[2][3];
+}
+
+void Matrix3x4_FromOriginQuat( matrix3x4 out, const vec4_t quaternion, const vec3_t origin )
+{
+ out[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2];
+ out[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2];
+ out[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1];
+
+ out[0][1] = 2.0f * quaternion[0] * quaternion[1] - 2.0f * quaternion[3] * quaternion[2];
+ out[1][1] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[2] * quaternion[2];
+ out[2][1] = 2.0f * quaternion[1] * quaternion[2] + 2.0f * quaternion[3] * quaternion[0];
+
+ out[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1];
+ out[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0];
+ out[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1];
+
+ out[0][3] = origin[0];
+ out[1][3] = origin[1];
+ out[2][3] = origin[2];
+}
+
+void Matrix3x4_CreateFromEntity( matrix3x4 out, const vec3_t angles, const vec3_t origin, float scale )
+{
+ float angle, sr, sp, sy, cr, cp, cy;
+
+ if( angles[ROLL] )
+ {
+#ifdef XASH_VECTORIZE_SINCOS
+ SinCosFastVector3( DEG2RAD(angles[YAW]), DEG2RAD(angles[PITCH]), DEG2RAD(angles[ROLL]),
+ &sy, &sp, &sr,
+ &cy, &cp, &cr);
+#else
+ angle = angles[YAW] * (M_PI2 / 360.0f);
+ SinCos( angle, &sy, &cy );
+ angle = angles[PITCH] * (M_PI2 / 360.0f);
+ SinCos( angle, &sp, &cp );
+ angle = angles[ROLL] * (M_PI2 / 360.0f);
+ SinCos( angle, &sr, &cr );
+#endif
+
+ out[0][0] = (cp*cy) * scale;
+ out[0][1] = (sr*sp*cy+cr*-sy) * scale;
+ out[0][2] = (cr*sp*cy+-sr*-sy) * scale;
+ out[0][3] = origin[0];
+ out[1][0] = (cp*sy) * scale;
+ out[1][1] = (sr*sp*sy+cr*cy) * scale;
+ out[1][2] = (cr*sp*sy+-sr*cy) * scale;
+ out[1][3] = origin[1];
+ out[2][0] = (-sp) * scale;
+ out[2][1] = (sr*cp) * scale;
+ out[2][2] = (cr*cp) * scale;
+ out[2][3] = origin[2];
+ }
+ else if( angles[PITCH] )
+ {
+#ifdef XASH_VECTORIZE_SINCOS
+ SinCosFastVector2( DEG2RAD(angles[YAW]), DEG2RAD(angles[PITCH]),
+ &sy, &sp,
+ &cy, &cp);
+#else
+ angle = angles[YAW] * (M_PI2 / 360.0f);
+ SinCos( angle, &sy, &cy );
+ angle = angles[PITCH] * (M_PI2 / 360.0f);
+ SinCos( angle, &sp, &cp );
+#endif
+
+ out[0][0] = (cp*cy) * scale;
+ out[0][1] = (-sy) * scale;
+ out[0][2] = (sp*cy) * scale;
+ out[0][3] = origin[0];
+ out[1][0] = (cp*sy) * scale;
+ out[1][1] = (cy) * scale;
+ out[1][2] = (sp*sy) * scale;
+ out[1][3] = origin[1];
+ out[2][0] = (-sp) * scale;
+ out[2][1] = 0.0f;
+ out[2][2] = (cp) * scale;
+ out[2][3] = origin[2];
+ }
+ else if( angles[YAW] )
+ {
+ angle = angles[YAW] * (M_PI2 / 360.0f);
+ SinCos( angle, &sy, &cy );
+
+ out[0][0] = (cy) * scale;
+ out[0][1] = (-sy) * scale;
+ out[0][2] = 0.0f;
+ out[0][3] = origin[0];
+ out[1][0] = (sy) * scale;
+ out[1][1] = (cy) * scale;
+ out[1][2] = 0.0f;
+ out[1][3] = origin[1];
+ out[2][0] = 0.0f;
+ out[2][1] = 0.0f;
+ out[2][2] = scale;
+ out[2][3] = origin[2];
+ }
+ else
+ {
+ out[0][0] = scale;
+ out[0][1] = 0.0f;
+ out[0][2] = 0.0f;
+ out[0][3] = origin[0];
+ out[1][0] = 0.0f;
+ out[1][1] = scale;
+ out[1][2] = 0.0f;
+ out[1][3] = origin[1];
+ out[2][0] = 0.0f;
+ out[2][1] = 0.0f;
+ out[2][2] = scale;
+ out[2][3] = origin[2];
+ }
+}
+
+void Matrix3x4_TransformPositivePlane( cmatrix3x4 in, const vec3_t normal, float d, vec3_t out, float *dist )
+{
+ float scale = sqrt( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] );
+ float iscale = 1.0f / scale;
+
+ out[0] = (normal[0] * in[0][0] + normal[1] * in[0][1] + normal[2] * in[0][2]) * iscale;
+ out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale;
+ out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale;
+ *dist = d * scale + ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] );
+}
+
+void Matrix3x4_Invert_Simple( matrix3x4 out, cmatrix3x4 in1 )
+{
+ // we only support uniform scaling, so assume the first row is enough
+ // (note the lack of sqrt here, because we're trying to undo the scaling,
+ // this means multiplying by the inverse scale twice - squaring it, which
+ // makes the sqrt a waste of time)
+ float scale = 1.0f / (in1[0][0] * in1[0][0] + in1[0][1] * in1[0][1] + in1[0][2] * in1[0][2]);
+
+ // invert the rotation by transposing and multiplying by the squared
+ // recipricol of the input matrix scale as described above
+ out[0][0] = in1[0][0] * scale;
+ out[0][1] = in1[1][0] * scale;
+ out[0][2] = in1[2][0] * scale;
+ out[1][0] = in1[0][1] * scale;
+ out[1][1] = in1[1][1] * scale;
+ out[1][2] = in1[2][1] * scale;
+ out[2][0] = in1[0][2] * scale;
+ out[2][1] = in1[1][2] * scale;
+ out[2][2] = in1[2][2] * scale;
+
+ // invert the translate
+ out[0][3] = -(in1[0][3] * out[0][0] + in1[1][3] * out[0][1] + in1[2][3] * out[0][2]);
+ out[1][3] = -(in1[0][3] * out[1][0] + in1[1][3] * out[1][1] + in1[2][3] * out[1][2]);
+ out[2][3] = -(in1[0][3] * out[2][0] + in1[1][3] * out[2][1] + in1[2][3] * out[2][2]);
+}
+
+const matrix4x4 matrix4x4_identity =
+{
+{ 1, 0, 0, 0 }, // PITCH
+{ 0, 1, 0, 0 }, // YAW
+{ 0, 0, 1, 0 }, // ROLL
+{ 0, 0, 0, 1 }, // ORIGIN
+};
+
+/*
+========================================================================
+
+ Matrix4x4 operations
+
+========================================================================
+*/
+void Matrix4x4_VectorTransform( cmatrix4x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + in[0][3];
+ out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + in[1][3];
+ out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] + in[2][3];
+}
+
+void Matrix4x4_VectorITransform( cmatrix4x4 in, const float v[3], float out[3] )
+{
+ vec3_t dir;
+
+ dir[0] = v[0] - in[0][3];
+ dir[1] = v[1] - in[1][3];
+ dir[2] = v[2] - in[2][3];
+
+ out[0] = dir[0] * in[0][0] + dir[1] * in[1][0] + dir[2] * in[2][0];
+ out[1] = dir[0] * in[0][1] + dir[1] * in[1][1] + dir[2] * in[2][1];
+ out[2] = dir[0] * in[0][2] + dir[1] * in[1][2] + dir[2] * in[2][2];
+}
+
+void Matrix4x4_VectorRotate( cmatrix4x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2];
+ out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2];
+ out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2];
+}
+
+void Matrix4x4_VectorIRotate( cmatrix4x4 in, const float v[3], float out[3] )
+{
+ out[0] = v[0] * in[0][0] + v[1] * in[1][0] + v[2] * in[2][0];
+ out[1] = v[0] * in[0][1] + v[1] * in[1][1] + v[2] * in[2][1];
+ out[2] = v[0] * in[0][2] + v[1] * in[1][2] + v[2] * in[2][2];
+}
+
+void Matrix4x4_ConcatTransforms( matrix4x4 out, cmatrix4x4 in1, cmatrix4x4 in2 )
+{
+ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
+ out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
+ out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2];
+ out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3];
+ out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0];
+ out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1];
+ out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2];
+ out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3];
+ out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0];
+ out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1];
+ out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2];
+ out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3];
+}
+
+void Matrix4x4_SetOrigin( matrix4x4 out, float x, float y, float z )
+{
+ out[0][3] = x;
+ out[1][3] = y;
+ out[2][3] = z;
+}
+
+void Matrix4x4_OriginFromMatrix( cmatrix4x4 in, float *out )
+{
+ out[0] = in[0][3];
+ out[1] = in[1][3];
+ out[2] = in[2][3];
+}
+
+void Matrix4x4_FromOriginQuat( matrix4x4 out, const vec4_t quaternion, const vec3_t origin )
+{
+ out[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2];
+ out[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2];
+ out[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1];
+ out[0][3] = origin[0];
+ out[0][1] = 2.0f * quaternion[0] * quaternion[1] - 2.0f * quaternion[3] * quaternion[2];
+ out[1][1] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[2] * quaternion[2];
+ out[2][1] = 2.0f * quaternion[1] * quaternion[2] + 2.0f * quaternion[3] * quaternion[0];
+ out[1][3] = origin[1];
+ out[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1];
+ out[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0];
+ out[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1];
+ out[2][3] = origin[2];
+ out[3][0] = 0.0f;
+ out[3][1] = 0.0f;
+ out[3][2] = 0.0f;
+ out[3][3] = 1.0f;
+}
+
+
+void Matrix4x4_CreateTranslate (matrix4x4 out, double x, double y, double z)
+{
+ out[0][0]=1.0f;
+ out[0][1]=0.0f;
+ out[0][2]=0.0f;
+ out[0][3]=x;
+ out[1][0]=0.0f;
+ out[1][1]=1.0f;
+ out[1][2]=0.0f;
+ out[1][3]=y;
+ out[2][0]=0.0f;
+ out[2][1]=0.0f;
+ out[2][2]=1.0f;
+ out[2][3]=z;
+ out[3][0]=0.0f;
+ out[3][1]=0.0f;
+ out[3][2]=0.0f;
+ out[3][3]=1.0f;
+}
+
+void Matrix4x4_Concat (matrix4x4 out, const matrix4x4 in1, const matrix4x4 in2)
+{
+ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0] + in1[0][3] * in2[3][0];
+ out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1] + in1[0][3] * in2[3][1];
+ out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2] + in1[0][3] * in2[3][2];
+ out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3] * in2[3][3];
+ out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0] + in1[1][3] * in2[3][0];
+ out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1] + in1[1][3] * in2[3][1];
+ out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2] + in1[1][3] * in2[3][2];
+ out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3] * in2[3][3];
+ out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0] + in1[2][3] * in2[3][0];
+ out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1] + in1[2][3] * in2[3][1];
+ out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2] + in1[2][3] * in2[3][2];
+ out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3] * in2[3][3];
+ out[3][0] = in1[3][0] * in2[0][0] + in1[3][1] * in2[1][0] + in1[3][2] * in2[2][0] + in1[3][3] * in2[3][0];
+ out[3][1] = in1[3][0] * in2[0][1] + in1[3][1] * in2[1][1] + in1[3][2] * in2[2][1] + in1[3][3] * in2[3][1];
+ out[3][2] = in1[3][0] * in2[0][2] + in1[3][1] * in2[1][2] + in1[3][2] * in2[2][2] + in1[3][3] * in2[3][2];
+ out[3][3] = in1[3][0] * in2[0][3] + in1[3][1] * in2[1][3] + in1[3][2] * in2[2][3] + in1[3][3] * in2[3][3];
+}
+
+void Matrix4x4_CreateFromEntity( matrix4x4 out, const vec3_t angles, const vec3_t origin, float scale )
+{
+ float angle, sr, sp, sy, cr, cp, cy;
+
+ if( angles[ROLL] )
+ {
+#ifdef XASH_VECTORIZE_SINCOS
+ SinCosFastVector3( DEG2RAD(angles[YAW]), DEG2RAD(angles[PITCH]), DEG2RAD(angles[ROLL]),
+ &sy, &sp, &sr,
+ &cy, &cp, &cr);
+#else
+ angle = angles[YAW] * (M_PI2 / 360.0f);
+ SinCos( angle, &sy, &cy );
+ angle = angles[PITCH] * (M_PI2 / 360.0f);
+ SinCos( angle, &sp, &cp );
+ angle = angles[ROLL] * (M_PI2 / 360.0f);
+ SinCos( angle, &sr, &cr );
+#endif
+
+ out[0][0] = (cp*cy) * scale;
+ out[0][1] = (sr*sp*cy+cr*-sy) * scale;
+ out[0][2] = (cr*sp*cy+-sr*-sy) * scale;
+ out[0][3] = origin[0];
+ out[1][0] = (cp*sy) * scale;
+ out[1][1] = (sr*sp*sy+cr*cy) * scale;
+ out[1][2] = (cr*sp*sy+-sr*cy) * scale;
+ out[1][3] = origin[1];
+ out[2][0] = (-sp) * scale;
+ out[2][1] = (sr*cp) * scale;
+ out[2][2] = (cr*cp) * scale;
+ out[2][3] = origin[2];
+ out[3][0] = 0.0f;
+ out[3][1] = 0.0f;
+ out[3][2] = 0.0f;
+ out[3][3] = 1.0f;
+ }
+ else if( angles[PITCH] )
+ {
+#ifdef XASH_VECTORIZE_SINCOS
+ SinCosFastVector2( DEG2RAD(angles[YAW]), DEG2RAD(angles[PITCH]),
+ &sy, &sp,
+ &cy, &cp);
+#else
+ angle = angles[YAW] * (M_PI2 / 360.0f);
+ SinCos( angle, &sy, &cy );
+ angle = angles[PITCH] * (M_PI2 / 360.0f);
+ SinCos( angle, &sp, &cp );
+#endif
+
+ out[0][0] = (cp*cy) * scale;
+ out[0][1] = (-sy) * scale;
+ out[0][2] = (sp*cy) * scale;
+ out[0][3] = origin[0];
+ out[1][0] = (cp*sy) * scale;
+ out[1][1] = (cy) * scale;
+ out[1][2] = (sp*sy) * scale;
+ out[1][3] = origin[1];
+ out[2][0] = (-sp) * scale;
+ out[2][1] = 0.0f;
+ out[2][2] = (cp) * scale;
+ out[2][3] = origin[2];
+ out[3][0] = 0.0f;
+ out[3][1] = 0.0f;
+ out[3][2] = 0.0f;
+ out[3][3] = 1.0f;
+ }
+ else if( angles[YAW] )
+ {
+ angle = angles[YAW] * (M_PI2 / 360.0f);
+ SinCos( angle, &sy, &cy );
+
+ out[0][0] = (cy) * scale;
+ out[0][1] = (-sy) * scale;
+ out[0][2] = 0.0f;
+ out[0][3] = origin[0];
+ out[1][0] = (sy) * scale;
+ out[1][1] = (cy) * scale;
+ out[1][2] = 0.0f;
+ out[1][3] = origin[1];
+ out[2][0] = 0.0f;
+ out[2][1] = 0.0f;
+ out[2][2] = scale;
+ out[2][3] = origin[2];
+ out[3][0] = 0.0f;
+ out[3][1] = 0.0f;
+ out[3][2] = 0.0f;
+ out[3][3] = 1.0f;
+ }
+ else
+ {
+ out[0][0] = scale;
+ out[0][1] = 0.0f;
+ out[0][2] = 0.0f;
+ out[0][3] = origin[0];
+ out[1][0] = 0.0f;
+ out[1][1] = scale;
+ out[1][2] = 0.0f;
+ out[1][3] = origin[1];
+ out[2][0] = 0.0f;
+ out[2][1] = 0.0f;
+ out[2][2] = scale;
+ out[2][3] = origin[2];
+ out[3][0] = 0.0f;
+ out[3][1] = 0.0f;
+ out[3][2] = 0.0f;
+ out[3][3] = 1.0f;
+ }
+}
+
+void Matrix4x4_ConvertToEntity( cmatrix4x4 in, vec3_t angles, vec3_t origin )
+{
+ float xyDist = sqrt( in[0][0] * in[0][0] + in[1][0] * in[1][0] );
+
+ // enough here to get angles?
+ if( xyDist > 0.001f )
+ {
+ angles[0] = RAD2DEG( atan2( -in[2][0], xyDist ));
+ angles[1] = RAD2DEG( atan2( in[1][0], in[0][0] ));
+ angles[2] = RAD2DEG( atan2( in[2][1], in[2][2] ));
+ }
+ else // forward is mostly Z, gimbal lock
+ {
+ angles[0] = RAD2DEG( atan2( -in[2][0], xyDist ));
+ angles[1] = RAD2DEG( atan2( -in[0][1], in[1][1] ));
+ angles[2] = 0.0f;
+ }
+
+ origin[0] = in[0][3];
+ origin[1] = in[1][3];
+ origin[2] = in[2][3];
+}
+
+void Matrix4x4_TransformPositivePlane( cmatrix4x4 in, const vec3_t normal, float d, vec3_t out, float *dist )
+{
+ float scale = sqrt( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] );
+ float iscale = 1.0f / scale;
+
+ out[0] = (normal[0] * in[0][0] + normal[1] * in[0][1] + normal[2] * in[0][2]) * iscale;
+ out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale;
+ out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale;
+ *dist = d * scale + ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] );
+}
+
+void Matrix4x4_TransformStandardPlane( cmatrix4x4 in, const vec3_t normal, float d, vec3_t out, float *dist )
+{
+ float scale = sqrt( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] );
+ float iscale = 1.0f / scale;
+
+ out[0] = (normal[0] * in[0][0] + normal[1] * in[0][1] + normal[2] * in[0][2]) * iscale;
+ out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale;
+ out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale;
+ *dist = d * scale - ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] );
+}
+
+void Matrix4x4_Invert_Simple( matrix4x4 out, cmatrix4x4 in1 )
+{
+ // we only support uniform scaling, so assume the first row is enough
+ // (note the lack of sqrt here, because we're trying to undo the scaling,
+ // this means multiplying by the inverse scale twice - squaring it, which
+ // makes the sqrt a waste of time)
+ float scale = 1.0f / (in1[0][0] * in1[0][0] + in1[0][1] * in1[0][1] + in1[0][2] * in1[0][2]);
+
+ // invert the rotation by transposing and multiplying by the squared
+ // recipricol of the input matrix scale as described above
+ out[0][0] = in1[0][0] * scale;
+ out[0][1] = in1[1][0] * scale;
+ out[0][2] = in1[2][0] * scale;
+ out[1][0] = in1[0][1] * scale;
+ out[1][1] = in1[1][1] * scale;
+ out[1][2] = in1[2][1] * scale;
+ out[2][0] = in1[0][2] * scale;
+ out[2][1] = in1[1][2] * scale;
+ out[2][2] = in1[2][2] * scale;
+
+ // invert the translate
+ out[0][3] = -(in1[0][3] * out[0][0] + in1[1][3] * out[0][1] + in1[2][3] * out[0][2]);
+ out[1][3] = -(in1[0][3] * out[1][0] + in1[1][3] * out[1][1] + in1[2][3] * out[1][2]);
+ out[2][3] = -(in1[0][3] * out[2][0] + in1[1][3] * out[2][1] + in1[2][3] * out[2][2]);
+
+ // don't know if there's anything worth doing here
+ out[3][0] = 0.0f;
+ out[3][1] = 0.0f;
+ out[3][2] = 0.0f;
+ out[3][3] = 1.0f;
+}
+
+void Matrix4x4_Transpose( matrix4x4 out, cmatrix4x4 in1 )
+{
+ out[0][0] = in1[0][0];
+ out[0][1] = in1[1][0];
+ out[0][2] = in1[2][0];
+ out[0][3] = in1[3][0];
+ out[1][0] = in1[0][1];
+ out[1][1] = in1[1][1];
+ out[1][2] = in1[2][1];
+ out[1][3] = in1[3][1];
+ out[2][0] = in1[0][2];
+ out[2][1] = in1[1][2];
+ out[2][2] = in1[2][2];
+ out[2][3] = in1[3][2];
+ out[3][0] = in1[0][3];
+ out[3][1] = in1[1][3];
+ out[3][2] = in1[2][3];
+ out[3][3] = in1[3][3];
+}
+
+qboolean Matrix4x4_Invert_Full( matrix4x4 out, cmatrix4x4 in1 )
+{
+ float *temp;
+ float *r[4];
+ float rtemp[4][8];
+ float m[4];
+ float s;
+
+ r[0] = rtemp[0];
+ r[1] = rtemp[1];
+ r[2] = rtemp[2];
+ r[3] = rtemp[3];
+
+ r[0][0] = in1[0][0];
+ r[0][1] = in1[0][1];
+ r[0][2] = in1[0][2];
+ r[0][3] = in1[0][3];
+ r[0][4] = 1.0f;
+ r[0][5] = 0.0f;
+ r[0][6] = 0.0f;
+ r[0][7] = 0.0f;
+
+ r[1][0] = in1[1][0];
+ r[1][1] = in1[1][1];
+ r[1][2] = in1[1][2];
+ r[1][3] = in1[1][3];
+ r[1][5] = 1.0f;
+ r[1][4] = 0.0f;
+ r[1][6] = 0.0f;
+ r[1][7] = 0.0f;
+
+ r[2][0] = in1[2][0];
+ r[2][1] = in1[2][1];
+ r[2][2] = in1[2][2];
+ r[2][3] = in1[2][3];
+ r[2][6] = 1.0f;
+ r[2][4] = 0.0f;
+ r[2][5] = 0.0f;
+ r[2][7] = 0.0f;
+
+ r[3][0] = in1[3][0];
+ r[3][1] = in1[3][1];
+ r[3][2] = in1[3][2];
+ r[3][3] = in1[3][3];
+ r[3][4] = 0.0f;
+ r[3][5] = 0.0f;
+ r[3][6] = 0.0f;
+ r[3][7] = 1.0f;
+
+ if( fabs( r[3][0] ) > fabs( r[2][0] ))
+ {
+ temp = r[3];
+ r[3] = r[2];
+ r[2] = temp;
+ }
+
+ if( fabs( r[2][0] ) > fabs( r[1][0] ))
+ {
+ temp = r[2];
+ r[2] = r[1];
+ r[1] = temp;
+ }
+
+ if( fabs( r[1][0] ) > fabs( r[0][0] ))
+ {
+ temp = r[1];
+ r[1] = r[0];
+ r[0] = temp;
+ }
+
+ if( r[0][0] )
+ {
+ m[1] = r[1][0] / r[0][0];
+ m[2] = r[2][0] / r[0][0];
+ m[3] = r[3][0] / r[0][0];
+
+ s = r[0][1];
+ r[1][1] -= m[1] * s;
+ r[2][1] -= m[2] * s;
+ r[3][1] -= m[3] * s;
+
+ s = r[0][2];
+ r[1][2] -= m[1] * s;
+ r[2][2] -= m[2] * s;
+ r[3][2] -= m[3] * s;
+
+ s = r[0][3];
+ r[1][3] -= m[1] * s;
+ r[2][3] -= m[2] * s;
+ r[3][3] -= m[3] * s;
+
+ s = r[0][4];
+ if( s )
+ {
+ r[1][4] -= m[1] * s;
+ r[2][4] -= m[2] * s;
+ r[3][4] -= m[3] * s;
+ }
+
+ s = r[0][5];
+ if( s )
+ {
+ r[1][5] -= m[1] * s;
+ r[2][5] -= m[2] * s;
+ r[3][5] -= m[3] * s;
+ }
+
+ s = r[0][6];
+ if( s )
+ {
+ r[1][6] -= m[1] * s;
+ r[2][6] -= m[2] * s;
+ r[3][6] -= m[3] * s;
+ }
+
+ s = r[0][7];
+ if( s )
+ {
+ r[1][7] -= m[1] * s;
+ r[2][7] -= m[2] * s;
+ r[3][7] -= m[3] * s;
+ }
+
+ if( fabs( r[3][1] ) > fabs( r[2][1] ))
+ {
+ temp = r[3];
+ r[3] = r[2];
+ r[2] = temp;
+ }
+
+ if( fabs( r[2][1] ) > fabs( r[1][1] ))
+ {
+ temp = r[2];
+ r[2] = r[1];
+ r[1] = temp;
+ }
+
+ if( r[1][1] )
+ {
+ m[2] = r[2][1] / r[1][1];
+ m[3] = r[3][1] / r[1][1];
+ r[2][2] -= m[2] * r[1][2];
+ r[3][2] -= m[3] * r[1][2];
+ r[2][3] -= m[2] * r[1][3];
+ r[3][3] -= m[3] * r[1][3];
+
+ s = r[1][4];
+ if( s )
+ {
+ r[2][4] -= m[2] * s;
+ r[3][4] -= m[3] * s;
+ }
+
+ s = r[1][5];
+ if( s )
+ {
+ r[2][5] -= m[2] * s;
+ r[3][5] -= m[3] * s;
+ }
+
+ s = r[1][6];
+ if( s )
+ {
+ r[2][6] -= m[2] * s;
+ r[3][6] -= m[3] * s;
+ }
+
+ s = r[1][7];
+ if( s )
+ {
+ r[2][7] -= m[2] * s;
+ r[3][7] -= m[3] * s;
+ }
+
+ if( fabs( r[3][2] ) > fabs( r[2][2] ))
+ {
+ temp = r[3];
+ r[3] = r[2];
+ r[2] = temp;
+ }
+
+ if( r[2][2] )
+ {
+ m[3] = r[3][2] / r[2][2];
+ r[3][3] -= m[3] * r[2][3];
+ r[3][4] -= m[3] * r[2][4];
+ r[3][5] -= m[3] * r[2][5];
+ r[3][6] -= m[3] * r[2][6];
+ r[3][7] -= m[3] * r[2][7];
+
+ if( r[3][3] )
+ {
+ s = 1.0f / r[3][3];
+ r[3][4] *= s;
+ r[3][5] *= s;
+ r[3][6] *= s;
+ r[3][7] *= s;
+
+ m[2] = r[2][3];
+ s = 1.0f / r[2][2];
+ r[2][4] = s * (r[2][4] - r[3][4] * m[2]);
+ r[2][5] = s * (r[2][5] - r[3][5] * m[2]);
+ r[2][6] = s * (r[2][6] - r[3][6] * m[2]);
+ r[2][7] = s * (r[2][7] - r[3][7] * m[2]);
+
+ m[1] = r[1][3];
+ r[1][4] -= r[3][4] * m[1];
+ r[1][5] -= r[3][5] * m[1];
+ r[1][6] -= r[3][6] * m[1];
+ r[1][7] -= r[3][7] * m[1];
+
+ m[0] = r[0][3];
+ r[0][4] -= r[3][4] * m[0];
+ r[0][5] -= r[3][5] * m[0];
+ r[0][6] -= r[3][6] * m[0];
+ r[0][7] -= r[3][7] * m[0];
+
+ m[1] = r[1][2];
+ s = 1.0f / r[1][1];
+ r[1][4] = s * (r[1][4] - r[2][4] * m[1]);
+ r[1][5] = s * (r[1][5] - r[2][5] * m[1]);
+ r[1][6] = s * (r[1][6] - r[2][6] * m[1]);
+ r[1][7] = s * (r[1][7] - r[2][7] * m[1]);
+
+ m[0] = r[0][2];
+ r[0][4] -= r[2][4] * m[0];
+ r[0][5] -= r[2][5] * m[0];
+ r[0][6] -= r[2][6] * m[0];
+ r[0][7] -= r[2][7] * m[0];
+
+ m[0] = r[0][1];
+ s = 1.0f / r[0][0];
+ r[0][4] = s * (r[0][4] - r[1][4] * m[0]);
+ r[0][5] = s * (r[0][5] - r[1][5] * m[0]);
+ r[0][6] = s * (r[0][6] - r[1][6] * m[0]);
+ r[0][7] = s * (r[0][7] - r[1][7] * m[0]);
+
+ out[0][0] = r[0][4];
+ out[0][1] = r[0][5];
+ out[0][2] = r[0][6];
+ out[0][3] = r[0][7];
+ out[1][0] = r[1][4];
+ out[1][1] = r[1][5];
+ out[1][2] = r[1][6];
+ out[1][3] = r[1][7];
+ out[2][0] = r[2][4];
+ out[2][1] = r[2][5];
+ out[2][2] = r[2][6];
+ out[2][3] = r[2][7];
+ out[3][0] = r[3][4];
+ out[3][1] = r[3][5];
+ out[3][2] = r[3][6];
+ out[3][3] = r[3][7];
+
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
diff --git a/Projects/Android/local.properties b/Projects/Android/local.properties
new file mode 100644
index 0000000..8e88db3
--- /dev/null
+++ b/Projects/Android/local.properties
@@ -0,0 +1,9 @@
+## This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+#Thu Dec 12 20:21:11 GMT 2019
+ndk.dir=C\:\\Users\\Simon\\AppData\\Local\\Android\\Sdk\\ndk-bundle
+sdk.dir=C\:\\Users\\Simon\\AppData\\Local\\Android\\Sdk
diff --git a/Projects/Android/settings.gradle b/Projects/Android/settings.gradle
new file mode 100644
index 0000000..31a6295
--- /dev/null
+++ b/Projects/Android/settings.gradle
@@ -0,0 +1,4 @@
+rootProject.projectDir = new File(settingsDir, '../../../..')
+rootProject.name = "Quake2Quest"
+
+include ':', 'VrSamples:Quake2Quest:Projects:Android'
diff --git a/VrSamples-RTCWQuest.iml b/VrSamples-RTCWQuest.iml
new file mode 100644
index 0000000..fb8ee18
--- /dev/null
+++ b/VrSamples-RTCWQuest.iml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java/com/drbeef/rtcwquest/GLES3JNIActivity.java b/java/com/drbeef/rtcwquest/GLES3JNIActivity.java
new file mode 100644
index 0000000..9276255
--- /dev/null
+++ b/java/com/drbeef/rtcwquest/GLES3JNIActivity.java
@@ -0,0 +1,426 @@
+
+package com.drbeef.quake2quest;
+
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.AudioTrack;
+import android.media.MediaRecorder;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.WindowManager;
+import android.view.KeyEvent;
+
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+
+import static android.system.Os.setenv;
+
+@SuppressLint("SdCardPath") public class GLES3JNIActivity extends Activity implements SurfaceHolder.Callback
+{
+ // Load the gles3jni library right away to make sure JNI_OnLoad() gets called as the very first thing.
+ static
+ {
+ System.loadLibrary( "yquake2" );
+ }
+
+ private static final String TAG = "Quake2Quest";
+
+ private int permissionCount = 0;
+ private static final int READ_EXTERNAL_STORAGE_PERMISSION_ID = 1;
+ private static final int WRITE_EXTERNAL_STORAGE_PERMISSION_ID = 2;
+
+ String commandLineParams;
+
+ private SurfaceView mView;
+ private SurfaceHolder mSurfaceHolder;
+ private long mNativeHandle;
+
+ // Main components
+ protected static GLES3JNIActivity mSingleton;
+
+ // Audio
+ protected static AudioTrack mAudioTrack;
+ protected static AudioRecord mAudioRecord;
+
+ public static void initialize() {
+ // The static nature of the singleton and Android quirkyness force us to initialize everything here
+ // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
+ mSingleton = null;
+ mAudioTrack = null;
+ mAudioRecord = null;
+ }
+
+ @Override protected void onCreate( Bundle icicle )
+ {
+ Log.v( TAG, "----------------------------------------------------------------" );
+ Log.v( TAG, "GLES3JNIActivity::onCreate()" );
+ super.onCreate( icicle );
+
+ GLES3JNIActivity.initialize();
+
+ // So we can call stuff from static callbacks
+ mSingleton = this;
+
+
+ mView = new SurfaceView( this );
+ setContentView( mView );
+ mView.getHolder().addCallback( this );
+
+ // Force the screen to stay on, rather than letting it dim and shut off
+ // while the user is watching a movie.
+ getWindow().addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON );
+
+ // Force screen brightness to stay at maximum
+ WindowManager.LayoutParams params = getWindow().getAttributes();
+ params.screenBrightness = 1.0f;
+ getWindow().setAttributes( params );
+
+ checkPermissionsAndInitialize();
+ }
+
+ /** Initializes the Activity only if the permission has been granted. */
+ private void checkPermissionsAndInitialize() {
+ // Boilerplate for checking runtime permissions in Android.
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED){
+ ActivityCompat.requestPermissions(
+ GLES3JNIActivity.this,
+ new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ WRITE_EXTERNAL_STORAGE_PERMISSION_ID);
+ }
+ else
+ {
+ permissionCount++;
+ }
+
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED)
+ {
+ ActivityCompat.requestPermissions(
+ GLES3JNIActivity.this,
+ new String[] {Manifest.permission.READ_EXTERNAL_STORAGE},
+ READ_EXTERNAL_STORAGE_PERMISSION_ID);
+ }
+ else
+ {
+ permissionCount++;
+ }
+
+ if (permissionCount == 2) {
+ // Permissions have already been granted.
+ create();
+ }
+ }
+
+ /** Handles the user accepting the permission. */
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
+ if (requestCode == READ_EXTERNAL_STORAGE_PERMISSION_ID) {
+ if (results.length > 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
+ permissionCount++;
+ }
+ else
+ {
+ System.exit(0);
+ }
+ }
+
+ if (requestCode == WRITE_EXTERNAL_STORAGE_PERMISSION_ID) {
+ if (results.length > 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
+ permissionCount++;
+ }
+ else
+ {
+ System.exit(0);
+ }
+ }
+
+ checkPermissionsAndInitialize();
+ }
+
+ public void create()
+ {
+ //This will copy the shareware version of quake2 if user doesn't have anything installed
+ copy_asset("/sdcard/Quake2Quest", "pak0.pak");
+
+ //HD Textures
+ copy_asset("/sdcard/Quake2Quest", "pak6.pak");
+
+ //Custom weapons
+ copy_asset("/sdcard/Quake2Quest", "pak99.pak");
+
+ //Configuration files
+ copy_asset("/sdcard/Quake2Quest", "config.cfg");
+ copy_asset("/sdcard/Quake2Quest", "autoexec.cfg");
+ copy_asset("/sdcard/Quake2Quest", "commandline.txt");
+
+ //Comfort Vignette Mask
+ copy_asset("/sdcard/Quake2Quest", "vignette.tga");
+
+ //Read these from a file and pass through
+ commandLineParams = new String("quake2");
+
+ //See if user is trying to use command line params
+ if(new File("/sdcard/Quake2Quest/commandline.txt").exists()) // should exist!
+ {
+ BufferedReader br;
+ try {
+ br = new BufferedReader(new FileReader("/sdcard/Quake2Quest/commandline.txt"));
+ String s;
+ StringBuilder sb=new StringBuilder(0);
+ while ((s=br.readLine())!=null)
+ sb.append(s + " ");
+ br.close();
+
+ commandLineParams = new String(sb.toString());
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ try {
+ setenv("YQUAKE2_GAMELIBDIR", getFilesDir().getParentFile().getPath() + "/lib", true);
+ }
+ catch (Exception e)
+ {
+
+ }
+
+ mNativeHandle = GLES3JNILib.onCreate( this, commandLineParams );
+ }
+
+ public void copy_asset(String path, String name) {
+ File f = new File(path + "/" + name);
+ if (!f.exists()) {
+
+ //Ensure we have an appropriate folder
+ String fullname = path + "/" + name;
+ String directory = fullname.substring(0, fullname.lastIndexOf("/"));
+ new File(directory).mkdirs();
+ _copy_asset(name, path + "/" + name);
+ }
+ }
+
+ public void _copy_asset(String name_in, String name_out) {
+ AssetManager assets = this.getAssets();
+
+ try {
+ InputStream in = assets.open(name_in);
+ OutputStream out = new FileOutputStream(name_out);
+
+ copy_stream(in, out);
+
+ out.close();
+ in.close();
+
+ } catch (Exception e) {
+
+ e.printStackTrace();
+ }
+
+ }
+
+ public static void copy_stream(InputStream in, OutputStream out)
+ throws IOException {
+ byte[] buf = new byte[1024];
+ while (true) {
+ int count = in.read(buf);
+ if (count <= 0)
+ break;
+ out.write(buf, 0, count);
+ }
+ }
+
+ @Override protected void onStart()
+ {
+ Log.v( TAG, "GLES3JNIActivity::onStart()" );
+ super.onStart();
+
+ GLES3JNILib.onStart( mNativeHandle );
+ }
+
+ @Override protected void onResume()
+ {
+ Log.v( TAG, "GLES3JNIActivity::onResume()" );
+ super.onResume();
+
+ GLES3JNILib.onResume( mNativeHandle );
+ }
+
+ @Override protected void onPause()
+ {
+ Log.v( TAG, "GLES3JNIActivity::onPause()" );
+ GLES3JNILib.onPause( mNativeHandle );
+ super.onPause();
+ }
+
+ @Override protected void onStop()
+ {
+ Log.v( TAG, "GLES3JNIActivity::onStop()" );
+ GLES3JNILib.onStop( mNativeHandle );
+ super.onStop();
+ }
+
+ @Override protected void onDestroy()
+ {
+ Log.v( TAG, "GLES3JNIActivity::onDestroy()" );
+
+ if ( mSurfaceHolder != null )
+ {
+ GLES3JNILib.onSurfaceDestroyed( mNativeHandle );
+ }
+
+ GLES3JNILib.onDestroy( mNativeHandle );
+
+ super.onDestroy();
+ // Reset everything in case the user re opens the app
+ GLES3JNIActivity.initialize();
+ mNativeHandle = 0;
+ }
+
+ @Override public void surfaceCreated( SurfaceHolder holder )
+ {
+ Log.v( TAG, "GLES3JNIActivity::surfaceCreated()" );
+ if ( mNativeHandle != 0 )
+ {
+ GLES3JNILib.onSurfaceCreated( mNativeHandle, holder.getSurface() );
+ mSurfaceHolder = holder;
+ }
+ }
+
+ @Override public void surfaceChanged( SurfaceHolder holder, int format, int width, int height )
+ {
+ Log.v( TAG, "GLES3JNIActivity::surfaceChanged()" );
+ if ( mNativeHandle != 0 )
+ {
+ GLES3JNILib.onSurfaceChanged( mNativeHandle, holder.getSurface() );
+ mSurfaceHolder = holder;
+ }
+ }
+
+ @Override public void surfaceDestroyed( SurfaceHolder holder )
+ {
+ Log.v( TAG, "GLES3JNIActivity::surfaceDestroyed()" );
+ if ( mNativeHandle != 0 )
+ {
+ GLES3JNILib.onSurfaceDestroyed( mNativeHandle );
+ mSurfaceHolder = null;
+ }
+ }
+
+ // Audio
+
+ /**
+ * This method is called by SDL using JNI.
+ */
+ public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
+ int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
+ int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
+
+ Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+ // Let the user pick a larger buffer if they really want -- but ye
+ // gods they probably shouldn't, the minimums are horrifyingly high
+ // latency already
+ desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
+
+ if (mAudioTrack == null) {
+ mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
+ channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
+
+ // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
+ // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
+ // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
+
+ if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+ Log.e(TAG, "Failed during initialization of Audio Track");
+ mAudioTrack = null;
+ return -1;
+ }
+
+ mAudioTrack.play();
+ }
+
+ Log.v(TAG, "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+ return 0;
+ }
+
+ /**
+ * This method is called by SDL using JNI.
+ */
+ public static void audioWriteShortBuffer(short[] buffer) {
+ for (int i = 0; i < buffer.length; ) {
+ int result = mAudioTrack.write(buffer, i, buffer.length - i);
+ if (result > 0) {
+ i += result;
+ } else if (result == 0) {
+ try {
+ Thread.sleep(1);
+ } catch(InterruptedException e) {
+ // Nom nom
+ }
+ } else {
+ Log.w(TAG, "SDL audio: error return from write(short)");
+ return;
+ }
+ }
+ }
+
+ /**
+ * This method is called by SDL using JNI.
+ */
+ public static void audioWriteByteBuffer(byte[] buffer) {
+ for (int i = 0; i < buffer.length; ) {
+ int result = mAudioTrack.write(buffer, i, buffer.length - i);
+ if (result > 0) {
+ i += result;
+ } else if (result == 0) {
+ try {
+ Thread.sleep(1);
+ } catch(InterruptedException e) {
+ // Nom nom
+ }
+ } else {
+ Log.w(TAG, "SDL audio: error return from write(byte)");
+ return;
+ }
+ }
+ }
+
+ /** This method is called by SDL using JNI. */
+ public static void audioClose() {
+ if (mAudioTrack != null) {
+ mAudioTrack.stop();
+ mAudioTrack.release();
+ mAudioTrack = null;
+ }
+ }
+
+}
diff --git a/java/com/drbeef/rtcwquest/GLES3JNILib.java b/java/com/drbeef/rtcwquest/GLES3JNILib.java
new file mode 100644
index 0000000..87d8569
--- /dev/null
+++ b/java/com/drbeef/rtcwquest/GLES3JNILib.java
@@ -0,0 +1,25 @@
+
+package com.drbeef.quake2quest;
+
+import android.app.Activity;
+import android.view.Surface;
+
+import java.nio.ByteBuffer;
+
+// Wrapper for native library
+
+public class GLES3JNILib
+{
+ // Activity lifecycle
+ public static native long onCreate( Activity obj, String commandLineParams );
+ public static native void onStart( long handle );
+ public static native void onResume( long handle );
+ public static native void onPause( long handle );
+ public static native void onStop( long handle );
+ public static native void onDestroy( long handle );
+
+ // Surface lifecycle
+ public static native void onSurfaceCreated( long handle, Surface s );
+ public static native void onSurfaceChanged( long handle, Surface s );
+ public static native void onSurfaceDestroyed( long handle );
+}
diff --git a/res/drawable-hdpi/ic_qquest.png b/res/drawable-hdpi/ic_qquest.png
new file mode 100644
index 0000000..6fea375
Binary files /dev/null and b/res/drawable-hdpi/ic_qquest.png differ
diff --git a/res/drawable-mdpi/ic_qquest.png b/res/drawable-mdpi/ic_qquest.png
new file mode 100644
index 0000000..a889c4b
Binary files /dev/null and b/res/drawable-mdpi/ic_qquest.png differ
diff --git a/res/drawable-xhdpi/ic_qquest.png b/res/drawable-xhdpi/ic_qquest.png
new file mode 100644
index 0000000..2999012
Binary files /dev/null and b/res/drawable-xhdpi/ic_qquest.png differ
diff --git a/res/drawable-xxhdpi/ic_qquest.png b/res/drawable-xxhdpi/ic_qquest.png
new file mode 100644
index 0000000..6a0800d
Binary files /dev/null and b/res/drawable-xxhdpi/ic_qquest.png differ
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..944b182
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+ Quake2Quest
+
+