JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
joysticks: configurable, working, buttons enable
[vor.git] / args.c
diff --git a/args.c b/args.c
index 06efba3..3ff1412 100644 (file)
--- a/args.c
+++ b/args.c
-#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
 #include "args.h"
-#include "config.h"
-
-// Gameplay Variations
-float opt_bounciness;
-float opt_gamespeed;
-float opt_max_lead;
+#include <config.h>
+#include "vorconfig.h"
 
 // Look and Feel
 int opt_fullscreen;
-int opt_music;
 int opt_sound;
+int opt_joystick_enabled;
+int opt_joystick_number;
+int opt_joystick_x_axis;
+int opt_joystick_y_axis;
+
+int opt_autopilot;
 
+static void
+show_help(void)
+{
+       puts("Dodge the rocks until you die.");
+       putchar('\n');
+       puts("  -f, --full-screen");
+       puts("  -s, --silent             No explosion sounds or music");
+       puts("  -j x,y, --joystick=x,y   set the axis numbers. defaults to 0,1");
+       puts("                           press a joystick button to activate that joystick");
+       puts("  -V, --version            Print program version");
+       puts("  -?, --help               Give this help list");
+       putchar('\n');
+       puts("Report bugs at http://jasonwoof.com/contact.html");
+}
 
-#ifndef WIN32
+// Advances buf while reading a positive int, terminated by [^0-9]
+// buf is left pointing at first [^0-9]
+// If digits are found, the parsed number is written to *out and 0 is returned.
+// Otherwise *buf and *out are left unchanged, and non-zero is returned.
+int
+parse_next_int(char **buf, int* out) {
+       int ret = 0, mul = 1;
+       if(!*buf) {
+               return 1;
+       }
+       // make sure *buf starts -?[0-9]
+       if(**buf == '-') {
+               mul = -1;
+               if((*buf)[1] < '0' || (*buf)[1] > '9') {
+                       return 2;
+               }
+               *buf += 1;
+       } else {
+               if(**buf < '0' || **buf > '9') {
+                       return 2;
+               }
+       }
+       while(**buf >= '0' && **buf <= '9') {
+               ret *= 10;
+               ret += **buf - '0';
+               *buf += 1;
+       }
+       *out = ret * mul;
 
-error_t parse_opt(int, char*, struct argp_state *);
+       return 0;
+}
 
-const char *argp_program_version = "Variations on Rockdodger " VERSION;
-const char *argp_program_bug_address = "<josh@qualdan.com>";
-static char doc[] = "Dodge the rocks until you die.";
-static struct argp_option opts[] = {
-       {0, 0, 0, 0, "Gameplay Variations:"},
-       {"bounciness", 'b', "N%", 0, "Keep N% of speed when hitting edges (default 50%)"},
-       {"game-speed", 'g', "N%", 0, "50-100% (default 100%)"},
-       {"max-lead", 'l', "#SCREENS", OPTION_HIDDEN,
-               "Max distance ahead you can get\n (default 1 screen; < 0 means no limit)"},
-       {0, 0, 0, 0, "Look and Feel:"},
-       {"full-screen", 'f', 0, 0, ""},
-       {"music", 'm', 0, 0, "Enable music"},
-       {"silent", 's', 0, 0, "No explosion sounds or music"},
-       {0, 0, 0, 0, "Informational:", -1},
-       {0}
-};
+// returns 1 on success. return 0 causes usage message
+int
+parse_joystick_opts(char *arg) {
+       char *arg_was = arg;
+       static char* bad_arg = "Error: invalid argument to -j/--joystick.";
 
-struct argp argp = { opts, &parse_opt, 0, doc };
+       // argument is required
+       if(arg == NULL) {
+               puts(bad_arg);
+               return 0;
+       }
 
-#endif // !WIN32
+       // read x axis
+       if(parse_next_int(&arg, &opt_joystick_x_axis)) {
+               puts(bad_arg);
+               return 0;
+       }
+
+       // skip comma
+       if(*arg != ',') {
+               puts(bad_arg);
+               return 0;
+       }
+       arg += 1;
+
+       // read y axis
+       if(parse_next_int(&arg, &opt_joystick_y_axis)) {
+               puts(bad_arg);
+               return 0;
+       }
+
+       // optionally joystick number
+       if(*arg == ',') {
+               arg += 1; // skip comma
+               if(parse_next_int(&arg, &opt_joystick_number)) {
+                       puts(bad_arg);
+                       return 0;
+               }
+               opt_joystick_enabled = 1;
+       }
+
+       // end with a comma or end of string
+       if(*arg != 0 && *arg != ',') {
+               puts(bad_arg);
+               return 0;
+       }
+
+       // mark arg as consumed (so it won't be parsed as a commandline switch)
+       arg_was[0] = 0;
+
+       // return success
+       return 1;
+}
+int
+short_opt(char c, char *arg)
+{
+       switch(c) {
+               case 'f': opt_fullscreen = 1; break;
+               case 's': opt_sound = 0; break;
+               case 'j':
+                       return parse_joystick_opts(arg);
+               case 'V':
+                                 printf("Variations on Rockdodger %s\n", PACKAGE_VERSION);
+                                 exit(0);
+               case '?':
+               case 'h': return 0;
+               case 'a': opt_autopilot = 1; break;
+               default: 
+                                 fprintf(stderr, "unknown option -%c\n\n", c);
+                                 return 0;
+       }
+       return 1;
+}
+
+int
+parse_short_opts(const char *s, char *arg)
+{
+       while(s[1]) if(!short_opt(*s++, NULL)) return 0;
+       return short_opt(*s, arg);
+}
+
+static char *long_opts[] = { "full-screen", "silent", "joystick", "version", "help", "autopilot" };
+
+static char short_opts[] = { 'f', 's', 'j', 'V', 'h', 'a' };
+
+int
+parse_long_opt(const char *s, char *arg)
+{
+       int i;
+       for(i=0; i<sizeof(short_opts); i++) {
+               if(strcmp(s, long_opts[i]) == 0)
+                       return short_opt(short_opts[i], arg);
+       }
+       fprintf(stderr, "unknown long option --%s\n\n", s);
+       return 0;
+}
 
 void
 init_opts(void)
 {
-       // Gameplay Variations
-       opt_bounciness = 0.50; // lose 50% when you hit the screen edge.
-       opt_gamespeed = 1.00; // Run game at full speed.
-       opt_max_lead = 1.00*XSIZE;  // you can get 1 screen ahead.
-
-       // Look and Feel
        opt_fullscreen = 0;
        opt_sound = 1;
-       opt_music = 0;
+       opt_joystick_enabled = 0; // can also be enabled by pressing one of its buttons
+       opt_joystick_number = 0;
+       opt_joystick_x_axis = 0;
+       opt_joystick_y_axis = 1;
+       opt_autopilot = 0;
 }
 
-#ifndef WIN32
-
-error_t
-parse_opt(int key, char *arg, struct argp_state *state)
+int
+parse_opts(int argc, char *argv[])
 {
        int i;
+       char *r;
 
-       switch(key) {
-               case 'b': if(!sscanf(arg, "%d%%", &i)) {
-                                         fprintf(stderr, "bad --bounciness (-b) value (should be 0-100%%)\n");
-                                         argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
-                                         return EINVAL;
-                                 }
-                                 if(i < 50) i = 50; else if(i > 100) i = 100;
-                                 opt_bounciness = (float)i / 100;
-                                 break;
-               case 'f': opt_fullscreen = 1; break;
-               case 'g': if(!sscanf(arg, "%d%%", &i)) {
-                                         fprintf(stderr, "bad --gamespeed (-g) value (should be 50-100%%)\n");
-                                         argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
-                                         return EINVAL;
-                                 }
-                                 if(i < 0) i = 0; else if(i > 200) i = 100;
-                                 opt_gamespeed = (float)i / 100;
-                                 break;
-               case 'l': if(!sscanf(arg, "%f", &opt_max_lead)) {
-                                         fprintf(stderr, "bad --max-lead (-l) value (must be a number)\n");
-                                         argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
-                                         return EINVAL;
-                                 }
-                                 opt_max_lead *= XSIZE;
-                                 break;
-               case 'm': opt_music = 1; break;
-               case 's': opt_sound = 0; opt_music = 0; break;
-               default: return ARGP_ERR_UNKNOWN;
+       init_opts();
+       for(i=1; i<argc; i++) {
+               char *s, *arg;
+               // args that are consumed as arguments to an option (such as the zero
+               // in: -j 0) have their first byte set to null. So skip them:
+               s = argv[i]; if(!*s) continue;
+               if(*s++ != '-') {
+                       fputs("not an option\n\n", stderr);
+                       show_help();
+                       return 0;
+               }
+
+               arg = NULL;
+               for(r=s; *r; r++) if(*r == '=') { *r = 0; arg = r+1; break; }
+
+               if(arg == NULL && i + 1 < argc) {
+                       arg = argv[i+1];
+                       // if this is used, it's first byte will be set to null, and
+                       // it'll be skipped. See above.
+               }
+
+               if(*s == '-') {
+                       if(!parse_long_opt(s+1, arg)) { show_help(); return 0; }
+               } else {
+                  if(!parse_short_opts(s, arg)) { show_help(); return 0; }
+               }
        }
-       return 0;
+       return 1;
 }
-
-#endif // !WIN32