/*
 * cmdline_def.h -- transcode command line option definitions
 * Written by Andrew Church <achurch@achurch.org>
 *
 * This file is part of transcode, a video stream processing tool.
 * transcode is free software, distributable under the terms of the GNU
 * General Public License (version 2 or later).  See the file COPYING
 * for details.
 */

/* This file contains definitions of all command-line options supported by
 * transcode.  The options are defined using macros which evaluate to
 * different things depending on which of the following symbols is defined
 * (exactly one must be defined before including this file):
 *
 * TC_OPTIONS_TO_ENUM
 *     Output a constant name for each long option, for use in defining an
 *     enum for the "val" field in "struct option".
 *
 * TC_OPTIONS_TO_STRUCT_OPTION
 *     Output a "struct option" definition for each option, for use in
 *     initializing a long-option array.
 *
 * TC_OPTIONS_TO_SHORTOPTS
 *     Output code to create a string of the form "x" or "x:" for each
 *     option which has a short option equivalent, for use in creating the
 *     short-option list passed to getopt().  The resulting string pointer
 *     is assigned to the variable named by the TC_OPTIONS_TO_SHORTOPTS
 *     symbol.
 *
 * TC_OPTIONS_TO_CODE
 *     Output code to process each option, for use in a switch statement.
 *
 * TC_OPTIONS_TO_OPTWIDTH
 *     Output code to compute the maximum width of any long option name
 *     (including the leading "--"), storing the result in the variable
 *     named by the TC_OPTIONS_TO_OPTWIDTH symbol, which must have been
 *     initialized to zero.
 *
 * TC_OPTIONS_TO_HELP
 *     Output a printf() statement to print the help text for each option.
 *     Also output printf() statements for option group headers (defined
 *     with the TC_HEADER macro).  The variable named by the
 *     TC_OPTIONS_TO_HELP symbol is used as the option name field width
 *     (presumed to have been computed with TC_OPTIONS_TO_OPTWIDTH).
 *
 * If none of the above are defined, this file simply declares the
 * parse_cmdline() prototype.
 *
 * Note that we explicitly do not use a #ifdef/#endif pair to avoid
 * multiple inclusion of this file, because it is included multiple times
 * by transcode.c, each time with a different one of the above symbols
 * defined.
 */

/*************************************************************************/

/* Check what we've been included for, and define macros appropriately.
 * The macros defined are:
 *
 * TC_OPTION(name, shortopt, argname, helptext, code)
 *     Defines an option "--name" (name should _not_ be quoted).  The name
 *     must be a valid C identifier, except that it may begin with a digit.
 *     * "shortopt" should be the character constant for the option's
 *       option's short form (e.g. 'x' if the short form is "-x"), or
 *       zero if the option has no short form.
 *     * "argname" should be a string naming the option's argument (for
 *       display in help text) if the option takes an argument, zero
 *       (0, not the string "0") if it does not.
 *     * "helptext" should be the option's help text, as a string.
 *     * "code" should be the code to process the option (which will be in
 *       its own block, so local variables are allowed--however, a comma in
 *       variable declarations will be interpreted as a macro argument
 *       delimiter, so each variable must go in its own statement; see the
 *       -x code for an example).
 *
 * TC_HEADER(name)
 *     Defines an option group header, which will be printed as part of the
 *     help message.
 *
 * _TCO_INIT
 * _TCO_FINI
 *     Local macros containing any appropriate header and trailer code for
 *     the requested output.  Defined only when necessary.
 */

#ifdef TC_OPTION
# error TC_OPTION is already defined!  Check for symbol name conflicts.
#endif
#ifdef TC_HEADER
# error TC_HEADER is already defined!  Check for symbol name conflicts.
#endif
#ifdef _TCO_INIT
# error _TCO_INIT is already defined!  Check for symbol name conflicts.
#endif
#ifdef _TCO_FINI
# error _TCO_FINI is already defined!  Check for symbol name conflicts.
#endif

#ifdef TC_OPTIONS_TO_ENUM
# ifdef TC_OPTION
#  error More than one TC_OPTIONS symbol defined!
# endif
/* Define the option value to either the character value of the short form
 * or a unique enum constant, while not disturbing the enum sequence */
# define TC_OPTION(name,shortopt,argname,helptext,code)   \
    OPT_TMP1_##name,                                      \
    OPT_##name = shortopt + (!shortopt * OPT_TMP1_##name),\
    OPT_TMP2_##name = OPT_TMP1_##name,
# define TC_HEADER(name)  /* nothing */
#endif

#ifdef TC_OPTIONS_TO_STRUCT_OPTION
# ifdef TC_OPTION
#  error More than one TC_OPTIONS symbol defined!
# endif
# define TC_OPTION(name,shortopt,argname,helptext,code) \
    { #name, (argname) ? required_argument : no_argument, NULL, OPT_##name },
# define TC_HEADER(name)  /* nothing */
# define _TCO_FINI {0,0,0,0}
#endif

#ifdef TC_OPTIONS_TO_SHORTOPTS
# ifdef TC_OPTION
#  error More than one TC_OPTIONS symbol defined!
# endif
# define _TCO_INIT {           \
    static char shortbuf[513]; \
    char *ptr = shortbuf;      \
    *shortbuf = 0;
# define TC_OPTION(name,shortopt,argname,helptext,code)   \
    if (shortopt && ptr-shortbuf < sizeof(shortbuf)-3) {  \
         *ptr++ = shortopt;                               \
         if (argname)                                     \
             *ptr++ = ':';                                \
    }
# define TC_HEADER(name)  /* nothing */
# define _TCO_FINI                      \
    *ptr = 0;                           \
    TC_OPTIONS_TO_SHORTOPTS = shortbuf; \
}
#endif

#ifdef TC_OPTIONS_TO_CODE
# ifdef TC_OPTION
#  error More than one TC_OPTIONS symbol defined!
# endif
# define TC_OPTION(name,shortopt,argname,helptext,code)   \
    case OPT_##name: { code; break; }
# define TC_HEADER(name)  /* nothing */
#endif

#ifdef TC_OPTIONS_TO_OPTWIDTH
# ifdef TC_OPTION
#  error More than one TC_OPTIONS symbol defined!
# endif
# define TC_OPTION(name,shortopt,argname,helptext,code) { \
    int len = (sizeof(#name)-1)+2;                        \
    if (argname)                                          \
        len += (sizeof(argname)-1)+1;                     \
    if (len > TC_OPTIONS_TO_OPTWIDTH)                     \
        TC_OPTIONS_TO_OPTWIDTH = len;                     \
}
# define TC_HEADER(name)  /* nothing */
#endif

#ifdef TC_OPTIONS_TO_HELP
# ifdef TC_OPTION
#  error More than one TC_OPTIONS symbol defined!
# endif
# define TC_OPTION(name,shortopt,argname,helptext,code)   \
    print_option_help(#name, shortopt, argname, helptext, TC_OPTIONS_TO_HELP);
# define TC_HEADER(name) \
    printf("\n  ======== %s ========\n\n", name);
#endif

#ifndef TC_OPTION
# define TC_OPTION(name,shortopt,argname,helptext,code)  /* nothing */
# define TC_HEADER(name)  /* nothing */
/* The parsing routine: */
extern int parse_cmdline(int argc, char **argv, vob_t *vob);
#endif


/* Output header code, if any. */
#ifdef _TCO_INIT
_TCO_INIT
#endif

/*************************************************************************/

/* The actual option definitions. */
TC_OPTION(help,               'h', 0,
                "print this usage message and exit",
                usage();
                return 0;
)
TC_OPTION(version,            'v', 0,
                "print version and exit",
                version();
                return 0;
)
TC_OPTION(verbose,            'q', "level",
                "verbosity (0=quiet,1=info,2=debug) [1]",
                verbose = strtol(optarg, &optarg, 10);
                if (*optarg) {
                    tc_error("Invalid argument for -q/--verbose");
                    goto short_usage;
                }
                if (verbose)  // ensure TC_INFO is always set if not silent
                    verbose |= TC_INFO;
                vob->verbose = verbose;
)

/********/ TC_HEADER("Input, output, and control files") /********/

TC_OPTION(input,              'i', "file",
#ifdef HAVE_LIBDVDREAD
                "input file/directory/device/mountpoint name",
#else
                "input file/directory name",
#endif
                vob->video_in_file = optarg;
)

TC_OPTION(multi_input,     0,   0,
                "enable EXPERIMENTAL multiple input mode (see manpage)",
                core_mode = TC_MODE_DIRECTORY;
)
TC_OPTION(output,             'o', "file",
                "output file name",
                vob->video_out_file = optarg;
)
TC_OPTION(avi_limit,          0,   "size",
                "split output AVI file after \"size\" MB [2048]",
                tc_avi_limit = strtol(optarg, &optarg, 10);
                if (*optarg) {
                    tc_error("Invalid argument for --avi_limit");
                    goto short_usage;
                }
                if (tc_avi_limit <= 0)
                    tc_avi_limit = (unsigned int)-1;
)
TC_OPTION(avi_comments,       0,   "file",
                "read AVI header comments from file [off]",
                vob->avi_comment_fd = xio_open(optarg, O_RDONLY);
                if (vob->avi_comment_fd == -1) {
                    tc_error("Cannot open comment file \"%s\"", optarg);
                    goto short_usage;
                }
)
TC_OPTION(split,              't', "n,base",
                "split output to base%03d.avi with n frames [off]",
                /* DANGER WILL ROBINSON!  scanf("%1000s") will read up to
                 * 1000 characters--EXCLUDING the trailing null, so a
                 * buffer of 1001 bytes is needed! */
                char buf[1001] = "";
                if (sscanf(optarg, "%d,%1000[^,]", &splitavi_frames, buf) != 2
                 || splitavi_frames <= 0
                ) {
                    tc_error("Invalid argument for -t/--split");
                    goto short_usage;
                }
                if (strlen(buf) > sizeof(base)-1) {
                    tc_error("Base name too long for -t/--split");
                    goto short_usage;
                }
                memcpy(base, buf, strlen(buf)+1);
                core_mode = TC_MODE_AVI_SPLIT;
)
TC_OPTION(audio_input,        'p', "file",
                "read audio stream from separate file [off]",
                vob->audio_in_file = optarg;
)
TC_OPTION(audio_output,       'm', "file",
                "write audio stream to separate file [off]",
                if (*optarg == '-')
                    goto short_usage;
                vob->audio_out_file = optarg;
                vob->audio_file_flag = 1;
)
TC_OPTION(nav_seek,           0,   "file",
                "use VOB navigation file [off]",
                if (*optarg == '-')
                    goto short_usage;
                vob->nav_seek_file = optarg;
                nav_seek_file = optarg;
)
TC_OPTION(socket,             0,   "file",
                "socket file for run-time control [off]",
                if (*optarg == '-')
                    goto short_usage;
                socket_file = optarg;
)
TC_OPTION(write_pid,          0,   "file",
                "write pid of transcode process to \"file\" [off]",
                FILE *f;
                if (*optarg == '-')
                    goto short_usage;
                f = fopen(optarg, "w");
                if (f) {
                    fprintf(f, "%d\n", writepid);
                    fclose(f);
                }
)
TC_OPTION(config_dir,         0,   "dir",
                "assume config files are in this dir [off]",
                if (*optarg == '-')
                    goto short_usage;
                tc_set_config_dir(optarg);
)

/********/ TC_HEADER("Input stream selection") /********/

TC_OPTION(extract_track,      'a', "a[,v]",
                "extract audio[,video] track [0,0]",
                if (sscanf(optarg, "%d,%d", &vob->a_track, &vob->v_track) < 1
                 || vob->a_track < 0
                 || vob->v_track < 0
                ) {
                    tc_error("Invalid argument for -a/--extract_track");
                    goto short_usage;
                }
                preset_flag |= TC_PROBE_NO_TRACK;
)
TC_OPTION(frames,             'c', "f1-f2[,f3-f4...]",
                "encode only given range (frames or HH:MM:SS),"
                " f2,f4,... are *not* encoded [all]",
                if (*optarg == '-')
                    goto short_usage;
                fc_ttime_string = optarg;
)
TC_OPTION(frame_interval,     0,   "N",
                "select only every Nth frame to be exported [1]",
                vob->frame_interval = strtol(optarg, &optarg, 0);
                if (*optarg || vob->frame_interval < 1) {
                    tc_error("Invalid argument for --frame_interval");
                    goto short_usage;
                }
)
TC_OPTION(title,              'T', "t[,c[-d][,a]]",
                "select DVD title[,chapters[,angle]] [1,all,1]",
                if (sscanf(optarg, "%d,%d-%d,%d", &vob->dvd_title,
                           &vob->dvd_chapter1, &vob->dvd_chapter2,
                           &vob->dvd_angle) >= 3
                ) {
                    /* Chapter range given */
                } else if (sscanf(optarg, "%d,%d,%d", &vob->dvd_title,
                                  &vob->dvd_chapter1, &vob->dvd_angle) >= 1
                ) {
                    /* Single (or no) chapter given */
                    vob->dvd_chapter2 = -1;  /* indicate single chapter */
                } else {
                    tc_error("Invalid argument for -T/--title");
                    goto short_usage;
                }
                if (vob->dvd_title < 1) {
                    tc_error("Invalid title for -T/--title");
                    goto short_usage;
                }
                if (vob->dvd_chapter1 != -1) {
                    if (vob->dvd_chapter1 < 1
                        || (vob->dvd_chapter2 != -1
                            && vob->dvd_chapter2 < vob->dvd_chapter1)
                    ) {
                        tc_error("Invalid chapter(s) for -T/--title");
                        goto short_usage;
                    }
                }
                if (vob->dvd_angle < 1) {
                    tc_error("Invalid angle for -T/--title");
                    goto short_usage;
                }
)
TC_OPTION(psu,                'S', "unit[,s1-s2]",
                "process program stream unit[,s1-s2] sequences [0,all]",
                if (sscanf(optarg, "%d,%d-%d", &vob->ps_unit,
                           &vob->ps_seq1, &vob->ps_seq2) < 0
                 || vob->ps_unit < 0
                 || vob->ps_seq1 < 0
                 || vob->ps_seq2 < 0
                 || vob->ps_seq1 > vob->ps_seq2
                ) {
                    tc_error("Invalid argument for -S/--psu");
                    goto short_usage;
                }
                preset_flag |= TC_PROBE_NO_SEEK;
)
TC_OPTION(vob_seek,           'L', "N",
                "seek to VOB stream offset Nx2kB [0]",
                vob->vob_offset = strtol(optarg, &optarg, 10);
                if (*optarg || vob->vob_offset < 0) {
                    tc_error("Invalid argument for -L/--vob_seek");
                    goto short_usage;
                }
)
TC_OPTION(ts_pid,             0,   "0xNN",
                "transport video stream pid [0]",
                vob->ts_pid1 = strtol(optarg, &optarg, 16);
                if (*optarg) {
                    tc_error("Invalid argument for --ts_pid");
                    goto short_usage;
                }
                vob->ts_pid2 = vob->ts_pid1;
)

/********/ TC_HEADER("Input stream format options") /********/

TC_OPTION(probe,              'H', "n",
                "auto-probe n MB of source (0=off) [1]",
                seek_range = strtol(optarg, &optarg, 10);
                if (*optarg || seek_range < 0) {
                    tc_error("Invalid argument for -H/--probe");
                    goto short_usage;
                }
                if (seek_range == 0)
                    auto_probe = 0;
)
TC_OPTION(mplayer_probe,      0,   0,
                "use (external) mplayer to probe source [off]",
                preset_flag |= TC_PROBE_NO_BUILTIN;
)
TC_OPTION(import_with,        'x', "vmod[,amod]",
                "video[,audio] import modules [null]",
                /* Careful here!  "static char vbuf[1001], abuf[1001]" will
                 * be treated as two separate macro arguments by the
                 * preprocessor, so we have to declare each variable in a
                 * separate statement. */
                static char vbuf[1001];
                static char abuf[1001];
                char quote;
                char *s;
                int n;
                /* Scan the string ourselves, rather than using scanf(),
                 * so we can handle internal quotes properly for -x mplayer */
                if (!*optarg) {
                    tc_error("Invalid argument for -x/--import_with");
                    goto short_usage;
                }
                *vbuf = *abuf = 0;
                quote = 0;
                n = 0;
                s = vbuf;
                while (*optarg) {
                    if (*optarg == quote) {
                        quote = 0;
                    } else if (!quote && (*optarg == '"' || *optarg == '\'')) {
                        quote = *optarg;
                    } else if (!quote && *optarg == ',') {
                        if (s == vbuf) {
                            s = abuf;
                            n = 0;
                        } else {
                            tc_error("Invalid argument for -x/--import_with");
                            goto short_usage;
                        }
                    } else {
                        if (n < (s==vbuf ? sizeof(vbuf) : sizeof(abuf))-1) {
                            s[n++] = *optarg;
                            s[n] = 0;
                        }
                    }
                    optarg++;
                }
                if (quote) {
                    tc_error("Invalid argument for -x/--import_with"
                             " (unbalanced quotes)");
                }
                s[n] = 0;
                n = (s==vbuf ? 1 : 2);
                im_vid_mod = vbuf;
                // FIXME: vin -> v_in to match no_v_out_codec (same w/audio)
                no_vin_codec = 0;
                if ((s = strchr(im_vid_mod, '=')) != NULL) {
                    *s++ = 0;
                    if (!*s) {
                        tc_error("Invalid option string for video import"
                                 " module");
                        goto short_usage;
                    }
                    vob->im_v_string = s;
                }
                if (n >= 2) {
                    im_aud_mod = abuf;
                    no_ain_codec = 0;
                    if ((s = strchr(im_aud_mod, '=')) != NULL) {
                        *s++ = 0;
                        if (!*s) {
                            tc_error("Invalid option string for audio import"
                                     " module");
                            goto short_usage;
                        }
                        vob->im_a_string = s;
                    }
                } else {
                    im_aud_mod = im_vid_mod;
                }
                /* "auto" checks have to come here, to catch "auto=..." */
                if (strcmp(im_vid_mod, "auto") == 0) {
                    im_vid_mod = NULL;
                    no_vin_codec = 1;
                }
                if (strcmp(im_aud_mod, "auto") == 0) {
                    im_aud_mod = NULL;
                    no_ain_codec = 1;
                }
)
TC_OPTION(frame_size,         'g', "WxH",
                "video frame size [720x576]",
                if (sscanf(optarg, "%dx%d", &vob->im_v_width,
                           &vob->im_v_height) != 2
                 || vob->im_v_width <= 0
                 || vob->im_v_height <= 0
                ) {
                    tc_error("Invalid argument for -g/--frame_size");
                    goto short_usage;
                }
                if (vob->im_v_width  > TC_MAX_V_FRAME_WIDTH
                 || vob->im_v_height > TC_MAX_V_FRAME_HEIGHT
                ) {
                    tc_error("Video frame size out of range (max %dx%d)",
                             TC_MAX_V_FRAME_WIDTH, TC_MAX_V_FRAME_HEIGHT);
                    goto short_usage;
                }
                preset_flag |= TC_PROBE_NO_FRAMESIZE;
)
TC_OPTION(import_asr,         0,   "C",
                "set import display aspect ratio code C [auto]",
                 vob->im_asr = strtol(optarg, &optarg, 10);
                 if (*optarg || vob->im_asr < 0) {
                     tc_error("Invalid argument for --import_asr");
                     goto short_usage;
                 }
                 preset_flag |= TC_PROBE_NO_IMASR;
)
TC_OPTION(import_fps,         'f', "rate[,frc]",
                "input video frame rate[,frc] [25.000,0]",
                int n = sscanf(optarg, "%lf,%d", &vob->fps, &vob->im_frc);
                if (n == 2) {
                    if (vob->im_frc < 0 || vob->im_frc > 15) {
                        tc_error("invalid frame rate code for option -f");
                        goto short_usage;
                    }
                    tc_frc_code_to_value(vob->im_frc, &vob->fps);
                } else {
                    if (n < 1 || vob->fps < MIN_FPS) {
                        tc_error("invalid frame rate for option -f");
                        goto short_usage;
                    }
                }
                preset_flag |= TC_PROBE_NO_FPS;
)
TC_OPTION(hard_fps,           0,   0,
                "disable smooth dropping (for variable fps clips) [enabled]",
                vob->hard_fps_flag = TC_TRUE;
)
TC_OPTION(import_afmt,        'e', "r[,b[,c]]",
                "import audio sample format [48000,16,2]",
                int n = sscanf(optarg, "%d,%d,%d", &vob->a_rate,
                               &vob->a_bits, &vob->a_chan);
                switch (n) {
                  case 3:
                    if (vob->a_chan != 0
                     && vob->a_chan != 1
                     && vob->a_chan != 2
                     && vob->a_chan != 6
                    ) {
                        tc_error("Invalid channels argument for"
                                 " -e/--import_afmt");
                        goto short_usage;
                    }
                    preset_flag |= TC_PROBE_NO_CHAN;
                    /* fall through */
                  case 2:
                    if (vob->a_bits != 8 && vob->a_bits != 16) {
                        tc_error("Invalid bits argument for"
                                 " -e/--import_afmt");
                        goto short_usage;
                    }
                    preset_flag |= TC_PROBE_NO_BITS;
                    /* fall through */
                  case 1:
                    if (vob->a_rate <= 0 || vob->a_rate > RATE) {
                        tc_error("Invalid rate argument for"
                                 " -e/--import_afmt");
                        goto short_usage;
                    }
                    preset_flag |= TC_PROBE_NO_RATE;
                    break;
                  default:
                    tc_error("Invalid argument for -e/--import_afmt");
                    break;
                }
)
TC_OPTION(import_codec,       'n', "0xNN",
                "import audio codec ID [0x2000]",
                vob->a_codec_flag = strtol(optarg, &optarg, 16);
                if (*optarg) {
                    tc_error("Invalid argument for -n/--import_format");
                    goto short_usage;
                }
                preset_flag |= TC_PROBE_NO_ACODEC;
)
TC_OPTION(no_audio_adjust,    0,   0,
                "disable audio frame size adjustment [enabled]",
                no_audio_adjust = TC_TRUE;
)

/********/ TC_HEADER("Output stream format options") /********/

TC_OPTION(export_prof,        0,   "profile",
                "export profile: {vcd|svcd|xvcd|dvd}[-pal|-ntsc|-secam]"
                " [none]",
                if            (strncasecmp(optarg, "vcd", 3) == 0) {
                    vob->mpeg_profile = VCD;  // need to guess NTSC/PAL later
                    if        (strncasecmp(optarg, "vcd-pal", 7) == 0) {
                        vob->mpeg_profile = VCD_PAL;
                    } else if (strncasecmp(optarg, "vcd-ntsc", 8) == 0) {
                        vob->mpeg_profile = VCD_NTSC;
                    } else if (strncasecmp(optarg, "vcd-secam", 9) == 0) {
                        vob->mpeg_profile = VCD_PAL;
                    }
                } else if     (strncasecmp(optarg, "svcd", 4) == 0) {
                    vob->mpeg_profile = SVCD;
                    if        (strncasecmp(optarg, "svcd-pal", 8) == 0) {
                        vob->mpeg_profile = SVCD_PAL;
                    } else if (strncasecmp(optarg, "svcd-ntsc", 9) == 0) {
                        vob->mpeg_profile = SVCD_NTSC;
                    } else if (strncasecmp(optarg, "svcd-secam", 10) == 0) {
                        vob->mpeg_profile = SVCD_PAL;
                    }
                } else if     (strncasecmp(optarg, "dvd", 3) == 0) {
                    vob->mpeg_profile = DVD;
                    if        (strncasecmp(optarg, "dvd-pal", 7) == 0) {
                        vob->mpeg_profile = DVD_PAL;
                    } else if (strncasecmp(optarg, "dvd-ntsc", 8) == 0) {
                        vob->mpeg_profile = DVD_NTSC;
                    } else if (strncasecmp(optarg, "dvd-secam", 9) == 0) {
                        vob->mpeg_profile = DVD_PAL;
                    }
                } else if     (strncasecmp(optarg, "xvcd", 4) == 0) {
                    vob->mpeg_profile = XVCD;
                    if        (strncasecmp(optarg, "xvcd-pal", 8) == 0) {
                        vob->mpeg_profile = XVCD_PAL;
                    } else if (strncasecmp(optarg, "xvcd-ntsc", 9) == 0) {
                        vob->mpeg_profile = XVCD_NTSC;
                    } else if (strncasecmp(optarg, "xvcd-secam", 10) == 0) {
                        vob->mpeg_profile = XVCD_PAL;
                    }
                } else {
                    tc_error("Invalid argument for --export_prof");
                }
)
TC_OPTION(export_with,        'y', "vm[,am[,mm]]",
                "video[,audio[,mplex]] export modules [null]",
                static char vbuf[1001];
                static char abuf[1001];
                static char mbuf[1001];
                char *s;
                int n;
                /* FIXME: handle embedded quotes like -x? */
                if ((n = sscanf(optarg, "%1000[^,],%1000[^,],%1000[^,]",
                                vbuf, abuf, mbuf)) < 1
                ) {
                    tc_error("Invalid argument for -y/--export_with");
                    goto short_usage;
                }
                ex_vid_mod = vbuf;
                no_v_out_codec = 0;
                vob->export_attributes |= TC_EXPORT_ATTRIBUTE_VMODULE;
                if ((s = strchr(ex_vid_mod, '=')) != NULL) {
                    *s++ = 0;
                    if (!*s) {
                        tc_error("Invalid option string for video export"
                                 " module");
                        goto short_usage;
                    }
                    vob->ex_v_string = s;
                }
                if (n >= 2) {
                    ex_aud_mod = abuf;
                    no_a_out_codec = 0;
                    if ((s = strchr(ex_aud_mod, '=')) != NULL) {
                        *s++ = 0;
                        if (!*s) {
                            tc_error("Invalid option string for audio export"
                                     " module");
                            goto short_usage;
                        }
                        vob->ex_a_string = s;
                    }
                } else {
                    ex_aud_mod = ex_vid_mod;
                }
                if (n >= 3) {
                    ex_mplex_mod = mbuf;
                    if ((s = strchr(ex_mplex_mod, '=')) != NULL) {
                        *s++ = 0;
                        if (!*s) {
                            tc_error("Invalid option string for multiplex"
                                     " module");
                            goto short_usage;
                        }
                        vob->ex_m_string = s;
                    }
                }
)
TC_OPTION(export_param,       'F', "string",
                "encoder parameter strings [module dependent]",
                char *s;
                if ((s = strchr(optarg, ',')) != NULL) {
                    char *s2;
                    *s = 0;
                    vob->ex_a_fcc = s+1;
                    if ((s2 = strchr(vob->ex_a_fcc,',')) != NULL) {
                        *s2 = 0;
                        vob->ex_profile_name = s2+1;
                    }
                }
                vob->ex_v_fcc = optarg;
                vob->export_attributes |= TC_EXPORT_ATTRIBUTE_VCODEC;
)
TC_OPTION(export_codec,       'N', "format",
                "export audio codec [mp3]",
                if (*optarg == '-')
                    goto short_usage;
                if (optarg[0] == '0' && optarg[1] == 'x') {
                    /* old behaviour */
                    vob->ex_a_codec = strtol(optarg, &optarg, 16);
                    if (*optarg) {
                        tc_error("Invalid argument for -N/--export_format");
                        goto short_usage;
                    }
                    vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ACODEC;
                } else {
                    char acodec[33];
                    char vcodec[33];
                    int n;
                    /* new behaviour */
                    n = sscanf(optarg, "%32[^,],%32[^,]", vcodec, acodec);
                    /* codecs in reversed order for backward compatibility */
                    switch (n) {
                      case 2: /* audio AND video codec */
                        vob->ex_v_codec = tc_codec_from_string(vcodec);
                        if (vob->ex_v_codec == TC_CODEC_ERROR) {
                            tc_error("Unknown video format for"
                                     " -N/--export_format");
                            goto short_usage;
                        }
                        vob->export_attributes |= TC_EXPORT_ATTRIBUTE_VCODEC;
                        /* move acodec to vcodec */
                        memcpy(vcodec, acodec, sizeof(vcodec));
                        /* fallthrough */
                      case 1: /* audio codec */
                        vob->ex_a_codec = tc_codec_from_string(acodec);
                        if (vob->ex_a_codec == TC_CODEC_ERROR) {
                            tc_error("Unknown audio format for"
                                     " -N/--export_format");
                            goto short_usage;
                        }
                        vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ACODEC;
                        break;
                      default:
                        tc_error("Invalid argument for -N/--export_format");
                        goto short_usage;
                    }
                }
)
TC_OPTION(multipass,          'R', "N[,vf[,af]]",
                "enable multi-pass encoding (0-3) [0,divx4.log,pcm.log]",
                static char vlogfile[1001] = "divx4.log";
                static char alogfile[1001] = "pcm.log";
                if (sscanf(optarg, "%d,%1000[^,],%1000[^,]",
                           &vob->divxmultipass, vlogfile, alogfile) < 1
                 || vob->divxmultipass < 0 || vob->divxmultipass > 3
                 || !*vlogfile
                 || !*alogfile
                ) {
                    tc_error("Invalid argument for -R/--multipass");
                    goto short_usage;
                }
                vob->divxlogfile = vlogfile;
                vob->audiologfile = alogfile;
)
TC_OPTION(vbitrate,           'w', "r[,k[,c]]",
                "encoder bitrate[,keyframes[,crispness]] [1800,250,100]",
                float ratefact = 1.0f;
                int n = sscanf(optarg, "%f,%d,%d", &ratefact,
                               &vob->divxkeyframes, &vob->divxcrispness);
                switch (n) {
                  case 3:
                    if (vob->divxcrispness < 0 || vob->divxcrispness > 100) {
                        tc_error("Invalid crispness argument for"
                                 " -w/--vbitrate");
                        goto short_usage;
                    }
                  case 2:
                    vob->export_attributes |= TC_EXPORT_ATTRIBUTE_GOP;
                  case 1:
                    if (vob->divxbitrate <= 0) {
                        tc_error("Invalid bitrate argument for"
                                 " -w/--vbitrate");
                        goto short_usage;
                    }
                    vob->divxbitrate = (int)ratefact;
                    vob->m2v_requant =      ratefact;
                    vob->export_attributes |= TC_EXPORT_ATTRIBUTE_VBITRATE;
                    break;
                  default:
                    tc_error("Invalid argument for -w/--vbitrate");
                    goto short_usage;
                }
)
TC_OPTION(video_max_bitrate,  0,   "r",
                "maximum bitrate when encoding variable bitrate MPEG-2"
                " streams [same as -w]",
                vob->video_max_bitrate = strtol(optarg, &optarg, 10);
                if (*optarg || vob->video_max_bitrate < 0) {
                    tc_error("Invalid argument for --video_max_bitrate");
                    goto short_usage;
                }
)
TC_OPTION(export_fps,         0,   "f[,c]",
                "output video frame rate[,code] [as input]",
                int n = sscanf(optarg, "%lf,%d", &vob->ex_fps, &vob->ex_frc);
                if (n < 1 || n > 2) {
                    tc_error("Invalid argument for --export_fps");
                    goto short_usage;
                }
                vob->export_attributes |= TC_EXPORT_ATTRIBUTE_FPS;
                if (n == 2) {
                    if (vob->ex_frc < 0 || vob->ex_frc > 15) {
                        tc_error("Invalid frc value for --export_fps");
                        goto short_usage;
                    }
                    vob->export_attributes |= TC_EXPORT_ATTRIBUTE_FRC;
                    tc_frc_code_to_value(vob->ex_frc, &vob->ex_fps);
                } else {
                    if (vob->ex_fps < MIN_FPS) {
                        tc_error("Invalid fps value for --export_fps");
                        goto short_usage;
                    }
                    vob->ex_frc = 0;
                }
)
TC_OPTION(export_frc,         0,   "C",
                "set export frame rate code C independently of actual"
                " frame rate [derived from export FPS]",
                vob->ex_frc = strtol(optarg, &optarg, 10);
                if (*optarg || vob->ex_frc < 0 || vob->ex_frc > 15) {
                    tc_error("Invalid frc value for --export_frc");
                    goto short_usage;
                }
                vob->export_attributes |= TC_EXPORT_ATTRIBUTE_FRC;
)
TC_OPTION(export_asr,         0,   "C",
                "set export display aspect ratio code C [as input]",
                vob->ex_asr = strtol(optarg, &optarg, 10);
                if (*optarg || vob->ex_asr < 0 || vob->ex_asr > 4) {
                    tc_error("Invalid argument for --export_asr");
                    goto short_usage;
                }
                vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ASR;
)
TC_OPTION(export_par,         0,   "{C | N,D}",
                "set export pixel aspect ratio [auto]",
                int n = sscanf(optarg, "%d,%d", &vob->ex_par_width,
                               &vob->ex_par_height);
                if (n == 1) {
                    /* Only one argument: PAR code */
                    vob->ex_par = vob->ex_par_width;
                    if (vob->ex_par < 0 || vob->ex_par > 5) {
                        tc_error("--export_par must be between 0 and 5");
                        goto short_usage;
                    }
                    tc_par_code_to_ratio(vob->ex_par,
                                         &vob->ex_par_width,
                                         &vob->ex_par_height);
                } else if (n == 2) {
                    /* Two arguments: use nonstandard PAR */
                    vob->ex_par = 0;
                    if (vob->ex_par_width <= 0 || vob->ex_par_height <= 0) {
                        tc_error("bad PAR values for --export_par:"
                                 " %d/%d not [>0]/[>0]",
                                 vob->ex_par_width, vob->ex_par_height);
                        goto short_usage;
                    }
                    /* correct common misbehaviour */
                    if (vob->ex_par_width == 1 && vob->ex_par_height == 1) {
                        vob->ex_par = 1;
                        tc_info("given PAR values of 1/1, reset PAR code"
                                " to 1");
                    }
                } else {
                    /* Bad number of arguments (<1 || >2) */
                    tc_error("Invalid argument for --export_par");
                    goto short_usage;
                }
                vob->export_attributes |= TC_EXPORT_ATTRIBUTE_PAR;
)
TC_OPTION(encode_fields,      0,   "C",
                "enable field-based encoding if supported [off]\n"
                "C can be t (top-first), b (bottom-first),\n"
                "         p (progressive), u (unknown)",
                switch (*optarg) {
                  case 't':
                    vob->encode_fields = TC_ENCODE_FIELDS_TOP_FIRST;    break;
                  case 'b':
                    vob->encode_fields = TC_ENCODE_FIELDS_BOTTOM_FIRST; break;
                  case 'p':
                    vob->encode_fields = TC_ENCODE_FIELDS_PROGRESSIVE;  break;
                  case 'u':
                    vob->encode_fields = TC_ENCODE_FIELDS_UNKNOWN;      break;
                  default:
                    tc_error("Invalid argument for --encode_fields");
                    goto short_usage;
                }
                vob->export_attributes |= TC_EXPORT_ATTRIBUTE_FIELDS;
)
TC_OPTION(pulldown,           0,   0,
                "set MPEG 3:2 pulldown flags on export [off]",
                vob->pulldown = TC_TRUE;
)
TC_OPTION(abitrate,           'b', "r[,v[,q[,m]]]",
                "audio encoder bitrate kBits/s[,vbr[,quality[,mode]]]"
                " [128,0,5,0]",
                if (sscanf(optarg, "%d,%d,%f,%d", &vob->mp3bitrate,
                           &vob->a_vbr, &vob->mp3quality, &vob->mp3mode) < 1
                 || vob->mp3bitrate < 0
                 || vob->a_vbr < 0
                 || vob->mp3quality < -1.00001
                 || vob->mp3mode < 0
                ) {
                    tc_error("Invalid argument for -b/--abitrate");
                    goto short_usage;
                }
                vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ABITRATE;
)
TC_OPTION(export_afmt,        'E', "r[,b[,c]]",
                "audio output samplerate, bits, channels [as input]",
                int n = sscanf(optarg, "%d,%d,%d", &vob->mp3frequency,
                               &vob->dm_bits, &vob->dm_chan);
                switch (n) {
                  case 3:
                    if (vob->dm_chan < 0 || vob->dm_chan > 6) {
                        tc_error("Invalid channels argument for"
                                 " -E/--export_afmt");
                        goto short_usage;
                    }
                    vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ACHANS;
                    /* fall through */
                  case 2:
                    if (vob->dm_bits != 0
                     && vob->dm_bits != 8
                     && vob->dm_bits != 16
                     && vob->dm_bits != 24
                    ) {
                        tc_error("Invalid bits argument for -E/--export_afmt");
                        goto short_usage;
                    }
                    vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ABITS;
                    /* fall through */
                  case 1:
                    if (vob->mp3frequency < 0) {
                        tc_error("Invalid rate argument for -E/--export_afmt");
                        goto short_usage;
                    }
                    vob->export_attributes |= TC_EXPORT_ATTRIBUTE_ARATE;
                    break;
                  default:
                    tc_error("Invalid argument for -E/--export_afmt");
                    break;
                }
)

/********/ TC_HEADER("Video processing options") /********/

TC_OPTION(pre_clip,           0,   "t[,l[,b[,r]]]",
                "select initial frame region by clipping [off]",
                int n = sscanf(optarg, "%d,%d,%d,%d",
                               &vob->pre_im_clip_top,
                               &vob->pre_im_clip_left,
                               &vob->pre_im_clip_bottom,
                               &vob->pre_im_clip_right);
                if (n < 1) {
                    tc_error("Invalid argument for --pre_clip");
                    goto short_usage;
                }
                pre_im_clip = TC_TRUE;
                // Symmetrical clipping for only 1-3 arguments
                if (n < 2)
                    vob->pre_im_clip_left   = 0;
                if (n < 3)
                    vob->pre_im_clip_bottom = vob->pre_im_clip_top;
                if (n < 4)
                    vob->pre_im_clip_right  = vob->pre_im_clip_left;
)
TC_OPTION(im_clip,            'j', "t[,l[,b[,r]]]",
                "clip or add frame border before filters [off]",
                int n = sscanf(optarg, "%d,%d,%d,%d",
                               &vob->im_clip_top,
                               &vob->im_clip_left,
                               &vob->im_clip_bottom,
                               &vob->im_clip_right);
                if (n < 1) {
                    tc_error("Invalid argument for -j/--im_clip");
                    goto short_usage;
                }
                im_clip = TC_TRUE;
                // Symmetrical clipping for only 1-3 arguments
                if (n < 2)
                    vob->im_clip_left   = 0;
                if (n < 3)
                    vob->im_clip_bottom = vob->im_clip_top;
                if (n < 4)
                    vob->im_clip_right  = vob->im_clip_left;
)
TC_OPTION(deinterlace,        'I', "mode",
                "deinterlace video using given mode (1-5) [off]",
                vob->deinterlace = strtol(optarg, &optarg, 10);
                if (*optarg || vob->deinterlace < 1 || vob->deinterlace > 5) {
                    tc_error("Invalid argument for -I/--deinterlace");
                    goto short_usage;
                }
)
TC_OPTION(expand,             'X', "n[,m[,M]]",
                "expand to height+n*M rows, width+m*M columns [0,0,32]",
                vob->hori_resize2 = 0;
                if (sscanf(optarg, "%d,%d,%d", &vob->vert_resize2,
                           &vob->hori_resize2, &vob->resize2_mult) < 1
                ) {
                    tc_error("Invalid argument for -X/--expand");
                    goto short_usage;
                }
                if (vob->resize2_mult != 8
                 && vob->resize2_mult != 16
                 && vob->resize2_mult != 32
                ) {
                    tc_error("Invalid multiplier for -X/--expand (must be"
                             " 8, 16, or 32)");
                    goto short_usage;
                }
                resize2 = TC_TRUE;
)
TC_OPTION(shrink,             'B', "n[,m[,M]]",
                "shrink to height-n*M rows, width-m*M columns [0,0,32]",
                vob->hori_resize1 = 0;
                if (sscanf(optarg, "%d,%d,%d", &vob->vert_resize1,
                           &vob->hori_resize1, &vob->resize1_mult) < 1
                ) {
                    tc_error("Invalid argument for -X/--expand");
                    goto short_usage;
                }
                if (vob->resize1_mult != 8
                 && vob->resize1_mult != 16
                 && vob->resize1_mult != 32
                ) {
                    tc_error("Invalid multiplier for -B/--shrink (must be"
                             " 8, 16, or 32)");
                    goto short_usage;
                }
                resize1 = TC_TRUE;
)
TC_OPTION(zoom,               'Z', "[W]x[H][,mode]",
                "resize to W columns, H rows w/filtering [off]",
                char *s = optarg;
                if (isdigit(*s)) {
                    vob->zoom_width = strtol(s, &s, 10);
                    if (vob->zoom_width > TC_MAX_V_FRAME_WIDTH) {
                        tc_error("Invalid width for -Z/--zoom (maximum %d)",
                                 TC_MAX_V_FRAME_WIDTH);
                        goto short_usage;
                    }
                } else {
                    vob->zoom_width = 0;
                }
                if (*s++ != 'x') {
                    tc_error("Invalid argument for -Z/--zoom");
                    goto short_usage;
                }
                if (isdigit(*s)) {
                    vob->zoom_height = strtol(s, &s, 10);
                    if (vob->zoom_height > TC_MAX_V_FRAME_HEIGHT) {
                        tc_error("Invalid height for -Z/--zoom (maximum %d)",
                                 TC_MAX_V_FRAME_HEIGHT);
                        goto short_usage;
                    }
                } else {
                    vob->zoom_height = 0;
                }
                zoom = TC_TRUE;
                if (*s == ',') {
                    s++;
                    if (strncmp(s, "fast", strlen(s)) == 0)
                        fast_resize = TC_TRUE;
                    else if (strncmp(s, "interlaced", strlen(s)) == 0)
                        vob->zoom_interlaced = TC_TRUE;
                }
)
TC_OPTION(zoom_filter,        0,   "filter",
                "use given filter for -Z resizing [Lanczos3]",
                vob->zoom_filter = tcv_zoom_filter_from_string(optarg);
		if (vob->zoom_filter == TCV_ZOOM_NULL) {
                    tc_error("invalid argument for --zoom_filter\n"
                             "filter must be one of:\n"
                             "   bell box b_spline hermite lanczos3"
                             " mitchell triangle cubic_keys4 sinc8");
                    goto short_usage;
                }
)
TC_OPTION(ex_clip,            'Y', "t[,l[,b[,r]]]",
                "clip or add frame border after filters [off]",
                int n = sscanf(optarg, "%d,%d,%d,%d",
                               &vob->ex_clip_top,
                               &vob->ex_clip_left,
                               &vob->ex_clip_bottom,
                               &vob->ex_clip_right);
                if (n < 1) {
                    tc_error("Invalid argument for -Y/--ex_clip");
                    goto short_usage;
                }
                ex_clip = TC_TRUE;
                // Symmetrical clipping for only 1-3 arguments
                if (n < 2)
                    vob->ex_clip_left   = 0;
                if (n < 3)
                    vob->ex_clip_bottom = vob->ex_clip_top;
                if (n < 4)
                    vob->ex_clip_right  = vob->ex_clip_left;
)
TC_OPTION(reduce,             'r', "n[,m]",
                "reduce video height/width by n[,m] [off]",
                int n = sscanf(optarg, "%d,%d", &vob->reduce_h,&vob->reduce_w);
                if (n == 1)
                    vob->reduce_w = vob->reduce_h;
                if (n < 1 || vob->reduce_h <= 0 || vob->reduce_w <= 0) {
                    tc_error("Invalid argument for -r/--reduce");
                    goto short_usage;
                }
	        rescale = TC_TRUE;
)
TC_OPTION(flip,               'z', 0,
                "flip video frame upside down [off]",
                flip = TC_TRUE;
)
TC_OPTION(mirror,             'l', 0,
                "mirror video frame [off]",
                mirror = TC_TRUE;
)
TC_OPTION(swap_colors,        'k', 0,
                "swap red/blue (Cb/Cr) in video frame [off]",
                rgbswap = TC_TRUE;
)
TC_OPTION(grayscale,          'K', 0,
                "enable grayscale mode [off]",
                decolor = TC_TRUE;
                vob->decolor = TC_TRUE;
)
TC_OPTION(gamma,              'G', "val",
                "gamma correction (0.0-10.0) [off]",
                vob->gamma = strtod(optarg, &optarg);
                if (*optarg || vob->gamma < 0) {
                    tc_error("Invalid argument for -G/--gamma");
                    goto short_usage;
                }
                dgamma = TC_TRUE;
)
TC_OPTION(antialias,          'C', "mode",
                "enable anti-aliasing mode (1-3) [off]",
                vob->antialias = strtol(optarg, &optarg, 10);
                if (*optarg || vob->antialias < 1 || vob->antialias > 3) {
                    tc_error("Invalid argument for -C/--antialias");
                    goto short_usage;
                }
)
TC_OPTION(antialias_para,     0,   "w,b",
                "center pixel weight, xy-bias [0.333,0.500]",
                if (sscanf(optarg, "%lf,%lf",
                               &vob->aa_weight, &vob->aa_bias) != 2) {
                    tc_error("Invalid argument for --antialias_para");
                    goto short_usage;
                }
                if (vob->aa_weight < 0.0 || vob->aa_weight > 1.0) {
                    tc_error("Invalid weight for --antlalias_para"
                             " (0.0 <= w <= 1.0)");
                    goto short_usage;
                }
                if (vob->aa_bias < 0.0 || vob->aa_bias > 1.0) {
                    tc_error("Invalid bias for --antlalias_para"
                             " (0.0 <= b <= 1.0)");
                    goto short_usage;
                }
)
TC_OPTION(post_clip,          0,   "t[,l[,b[,r]]]",
                "select final frame region by clipping [off]",
                int n = sscanf(optarg, "%d,%d,%d,%d",
                               &vob->post_ex_clip_top,
                               &vob->post_ex_clip_left,
                               &vob->post_ex_clip_bottom,
                               &vob->post_ex_clip_right);
                if (n < 1) {
                    tc_error("Invalid argument for --post_clip");
                    goto short_usage;
                }
                post_ex_clip = TC_TRUE;
                // Symmetrical clipping for only 1-3 arguments
                if (n < 2)
                    vob->post_ex_clip_left   = 0;
                if (n < 3)
                    vob->post_ex_clip_bottom = vob->post_ex_clip_top;
                if (n < 4)
                    vob->post_ex_clip_right  = vob->post_ex_clip_left;
)
TC_OPTION(video_format,       'V', "fmt",
                "select internal video format [yuv420p]\n"
                "one of: yuv420p, yuv422p, rgb24",
                if (strcmp(optarg, "yuv420p") == 0) {
                    tc_info("yuv420p is already the default for -V");
                    /* anyway... */
                    vob->im_v_codec = CODEC_YUV;
                } else if (strcmp(optarg, "yuv422p") == 0) {
                    vob->im_v_codec = CODEC_YUV422;
                } else if (strcmp(optarg, "rgb24") == 0) {
                    vob->im_v_codec = CODEC_RGB;
                } else {
                    tc_error("bad argument for -V/--video_format, should"
                             " be one of: yuv420p (default), yuv422p, rgb24");
                    goto short_usage;
                }
)
TC_OPTION(keep_asr,           0,   0,
                "try to keep aspect ratio (broken)",
                tc_error("--keep_asr is unavailable, calculate frame"
                         " parameters manually");
)

/********/ TC_HEADER("Audio processing options") /********/

TC_OPTION(audio_swap,         'd', 0,
                "swap bytes in audio stream [off]",
                pcmswap = TC_TRUE;
)
TC_OPTION(audio_scale,        's', "g[,c,f,r]",
                "scale volume by gain[,center,front,rear] [1,1,1,1]",
                vob->ac3_gain[0] = 1.0;
                vob->ac3_gain[1] = 1.0;
                vob->ac3_gain[2] = 1.0;
                if (sscanf(optarg, "%lf,%lf,%lf,%lf", &vob->volume,
                           &vob->ac3_gain[0], &vob->ac3_gain[1],
                           &vob->ac3_gain[2]) < 1
                 || vob->volume < 0
                ) {
                    tc_error("Invalid argument for -s/--audio_scale");
                    goto short_usage;
                }
)
TC_OPTION(audio_use_ac3,      'A', 0,
                "use AC3 as internal audio codec [off]",
                vob->im_a_codec = CODEC_AC3;
)

/********/ TC_HEADER("Other processing options") /********/

TC_OPTION(filter,             'J', "f1[,f2...]",
                "apply external audio/video filters [none]",
                static int size_plugstr = 0;
                int newlen;
                if (*optarg == '-')
                    goto short_usage;
                newlen = size_plugstr + strlen(optarg) + 1;  // \0
                if (size_plugstr) // it's an append...
                    newlen++; // ... so add the and ',' separator
                plugins_string = tc_realloc(plugins_string, newlen);
                if (!plugins_string)
                    return 0;
                snprintf(plugins_string + size_plugstr,
                         newlen - size_plugstr,
                         "%s%s", size_plugstr ? "," : "", optarg);
                size_plugstr = newlen - 1;
                // cut the \0 for the next append (if any)
)
TC_OPTION(quality,            'Q', "enc[,dec]",
                "encoding[,decoding] quality (0=fastest-5=best) [5,5]",
                if (sscanf(optarg,"%d,%d",&vob->divxquality,&vob->quality) < 1
                 || vob->divxquality < 0
                 || vob->quality < 0
                ) {
                    tc_error("Invalid argument for -Q/--quality");
                    goto short_usage;
                }
)
TC_OPTION(passthrough,        'P', "flag",
                "pass-through flag (0=off|1=V|2=A|3=A+V) [0]",
                vob->pass_flag = strtol(optarg, &optarg, 10);
                if (*optarg || vob->pass_flag < 0 || vob->pass_flag > 3) {
                    tc_error("Invalid argument for -P/--passthrough");
                    goto short_usage;
                }
)
TC_OPTION(sync_frame,         'D', "N",
                "sync video start with audio frame num [0]",
                vob->sync = strtol(optarg, &optarg, 10);
                if (*optarg) {
                    tc_error("Invalid argument for -D/--sync_frame");
                    goto short_usage;
                }
                sync_seconds = vob->sync;
                preset_flag |= TC_PROBE_NO_AVSHIFT;
)
TC_OPTION(av_fine_ms,         0,   "time",
                "AV fine-tuning shift in millisecs [autodetect]",
                vob->sync_ms = strtol(optarg, &optarg, 10);
                if (*optarg) {
                    tc_error("Invalid argument for --av_sync_ms");
                    goto short_usage;
                }
                preset_flag |= TC_PROBE_NO_AV_FINE;
)
TC_OPTION(demuxer_sync,     'M',   "N",
                "demuxer PES AV sync mode\n"
                "(0=off|1=PTS only|2=full) [1]",
                vob->demuxer = strtol(optarg, &optarg, 10);
                if (*optarg || vob->demuxer < 0 || vob->demuxer > 4) {
                    tc_error("Invalid argument for -M/--demuxer_sync");
                    goto short_usage;
                }
                preset_flag |= TC_PROBE_NO_DEMUX;
)

/********/ TC_HEADER("Codec-specific options") /********/

TC_OPTION(dv_yv12_mode,       0,   0,
                "(libdv) force YV12 mode for PAL\n"
                "Use this option if transcode autodetection fails,"
                " with DV video.",
                vob->dv_yuy2_mode = TC_FALSE;
)
TC_OPTION(dv_yuy2_mode,       0,   0,
                "(libdv) use YUY2 mode for PAL [YV12]\n"
                "If you experience crashes decoding DV video,"
                " try this option.",
                vob->dv_yuy2_mode = TC_TRUE;
)
TC_OPTION(quantizers,         0,   "min,max",
                "min/max quantizer, for MPEG-like codecs [2,31]",
                if (sscanf(optarg, "%d,%d", &vob->min_quantizer,
                           &vob->max_quantizer) != 2
                 || vob->min_quantizer < 1 || vob->min_quantizer > 31
                 || vob->max_quantizer < 1 || vob->max_quantizer > 31
                ) {
                    tc_error("Invalid argument for --quantizers");
                    goto short_usage;
                }
)
TC_OPTION(divx_quant,         0,   "min,max",
                "(DivX) min/max quantizer (deprecated) [2,31]",
                tc_warn("--divx_quant option is deprecated and will be removed soon.");
                tc_warn("use --quantizers instead.");
                if (sscanf(optarg, "%d,%d", &vob->min_quantizer,
                           &vob->max_quantizer) != 2
                 || vob->min_quantizer < 1 || vob->min_quantizer > 31
                 || vob->max_quantizer < 1 || vob->max_quantizer > 31
                ) {
                    tc_error("Invalid argument for --divx_quant");
                    goto short_usage;
                }
)
TC_OPTION(divx_rc,            0,   "p,rp,rr",
                "(DivX) rate control parameter [2000,10,20]",
                if (sscanf(optarg, "%d,%d,%d", &vob->rc_period,
                           &vob->rc_reaction_period,
                           &vob->rc_reaction_ratio) != 3
                ) {
                    tc_error("Invalid argument for --divx_rc");
                    goto short_usage;
                }
)
TC_OPTION(divx_vbv_prof,      0,   "N",
                "(DivX) VBV profile (0=free-5=hiqhq) [3]",
                vob->divx5_vbv_prof = strtol(optarg, &optarg, 10);
                if (*optarg
                 || vob->divx5_vbv_prof < 0
                 || vob->divx5_vbv_prof > 5
                ) {
                    tc_error("Invalid argument for --divx_vbv_prof");
                    goto short_usage;
                }
)
TC_OPTION(divx_vbv,           0,   "br,sz,oc",
                "(DivX) VBV params (bitrate,size,occupancy) [10000,192,36864]",
                if (sscanf(optarg, "%d,%d,%d", &vob->divx5_vbv_bitrate,
                           &vob->divx5_vbv_size,
                           &vob->divx5_vbv_occupancy) != 3
                ) {
                    tc_error("Invalid argument for --divx_vbv");
                    goto short_usage;
                }
)
TC_OPTION(lame_preset,        0,   "N[,fast]",
                "(LAME) use preset named N [off]",
                if (*optarg == '-')
                    goto short_usage;
                vob->lame_preset = optarg;
)
TC_OPTION(no_bitreservoir,    0,   0,
                "(LAME) disable bitreservoir [off]",
                vob->bitreservoir = TC_FALSE;
)
TC_OPTION(encoder_noflush,           'O', 0,
                "avoid to flush buffer(s) on encoder stop [enabled]",
                vob->encoder_flush = TC_FALSE;
)
TC_OPTION(a52_demux,          0,   0,
                "(liba52) demux AC3/A52 to separate channels [off]",
                vob->a52_mode |= TC_A52_DEMUX;
)
TC_OPTION(a52_drc_off,        0,   0,
                "(liba52) disable dynamic range compression [enabled]",
                vob->a52_mode |= TC_A52_DRC_OFF;
)
TC_OPTION(a52_dolby_off,      0,   0,
                "(liba52) disable Dolby surround [enabled]",
                vob->a52_mode |= TC_A52_DOLBY_OFF;
)

/********/ TC_HEADER("Cluster/PSU/chapter mode processing") /********/

TC_OPTION(autosplit,          'W', "n,m[,file]",
                "autosplit VOB and process part n of m [off]",
                static char vob_logfile[1001] = "";
                if (sscanf(optarg, "%d,%d,%1000[^,]", &vob->vob_chunk,
                           &vob->vob_chunk_max, vob_logfile) < 2
                 || vob->vob_chunk < 0
                 || vob->vob_chunk_max <= 0
                 || vob->vob_chunk >= vob->vob_chunk_max + 1
                ) {
                    tc_error("Invalid parameter for -W/--autosplit");
                    goto short_usage;
                }
                if (*vob_logfile)
                    vob->vob_info_file = vob_logfile;
                tc_cluster_mode = TC_TRUE;
)
TC_OPTION(cluster_percentage, 0,   0,
                "use percentage mode for cluster encoding [off]",
                vob->vob_percentage = TC_TRUE;
)
TC_OPTION(cluster_chunks,     0,   "a-b",
                "process chunk range instead of selected chunk [off]",
                if (sscanf(optarg,"%d-%d",
                           &vob->vob_chunk_num1, &vob->vob_chunk_num2) != 2
                 || vob->vob_chunk_num1 < 0
                 || vob->vob_chunk_num2 <= 0
                 || vob->vob_chunk_num1 >= vob->vob_chunk_num2
                ) {
                    tc_error("invalid parameter for --cluster_chunks");
                    goto short_usage;
                }
)
TC_OPTION(psu_mode,           0,   0,
                "process VOB in PSU, -o is a filemask incl. %d [off]",
                psu_mode = TC_TRUE;
                core_mode = TC_MODE_PSU;
                tc_cluster_mode = TC_TRUE;
)
TC_OPTION(psu_chunks,         0,   "a-b",
                "process only units a-b for PSU mode [all]",
                if (sscanf(optarg, "%d-%d,%d",
                           &vob->vob_psu_num1, &vob->vob_psu_num2,
                           &psu_frame_threshold) < 2
                 || vob->vob_psu_num1 < 0
                 || vob->vob_psu_num2 <= 0
                 || vob->vob_psu_num1 >= vob->vob_psu_num2
                ) {
                    tc_error("Invalid parameter for --psu_chunks");
                    goto short_usage;
                }
)
TC_OPTION(no_split,           0,   0,
                "encode to single file in chapter/psu mode [off]",
                no_split = TC_TRUE;
)
TC_OPTION(chapter_mode,       'U', "base",
                "process DVD in chapter mode to base-ch%02d.avi [off]",
                if (*optarg == '-')
                    goto short_usage;
                chbase = optarg;
                core_mode = TC_MODE_DVD_CHAPTER;
)

/********/ TC_HEADER("Miscellaneous options") /********/

#ifdef TC_OPTIONS_TO_HELP
/* produce ONLY help messages since this option require special tratment */
TC_OPTION(log_no_color,      0,  0,
                "disable colors in log messages [use colors]",
                ; /* nothing */
)
#endif /* TC_OPTIONS_TO_HELP */

TC_OPTION(buffers,            'u', "N",
                "use N framebuffers for AV processing [10]",
                /* FIXME: threads ought to be a separate option */
                int threads_dummy = 0;
                if (sscanf(optarg, "%d,%d,%d,%d", &max_frame_buffer,
                           &threads_dummy, &tc_buffer_delay_dec,
                           &tc_buffer_delay_enc) < 1
                 || max_frame_buffer < 0
                ) {
                    tc_error("Invalid argument for -u/--buffers");
                    goto short_usage;
                }
                if (threads_dummy != 0 && threads_dummy != max_frame_threads) {
                    tc_warn("Use --threads to specify number of threads,"
                            " not -u");
                    /* FIXME: deprecated in 1.1.0, remove later */
                    max_frame_threads = threads_dummy;
                }
                preset_flag |= TC_PROBE_NO_BUFFER;
)
TC_OPTION(threads,            0,   "N",
                "use N threads for AV processing [1]",
                max_frame_threads = strtol(optarg, &optarg, 10);
                if (*optarg
                 || max_frame_threads < 0
                 || max_frame_threads > TC_FRAME_THREADS_MAX
                ) {
                    tc_error("Invalid argument for -u/--threads");
                    goto short_usage;
                }
)
TC_OPTION(progress_meter,     0,   "N",
                "select type of progress meter [1]",
                tc_progress_meter = strtol(optarg, &optarg, 0);
                if (*optarg || tc_progress_meter < 0) {
                    tc_error("Invalid argument for --progress_meter");
                    goto short_usage;
                }
)
TC_OPTION(progress_rate,      0,   "N",
                "print progress every N frames [1]",
                tc_progress_rate = strtol(optarg, &optarg, 0);
                if (*optarg || tc_progress_rate <= 0) {
                    tc_error("Invalid argument for --progress_rate");
                    goto short_usage;
                }
)
TC_OPTION(nice,               0,   "N",
                "set niceness to N [off]",
                tc_niceness = strtol(optarg, &optarg, 0);
                if (*optarg) {
                    tc_error("Invalid argument for --nice");
                    goto short_usage;
                }
)
TC_OPTION(accel,              0,   "type[,type...]",
                "override CPU acceleration flags (for debugging)",
#if defined(ARCH_X86) || defined(ARCH_X86_64)
                int parsed = ac_parseflags(optarg, &tc_accel);
                if (!parsed) {		
                    tc_error("bad --accel type, valid types: C %s",
                             ac_flagstotext(AC_ALL));			
                    goto short_usage;
                }
#else
                /* Not supported--leave a statement in so the macro doesn't
                 * complain about a missing argument */
                break;
#endif
)
#if 0
TC_OPTION(debug,              0,   0,
                "enable debugging mode [disabled]",
                core_mode = TC_MODE_DEBUG;
)
#endif

/*************************************************************************/

/* Output trailer code, if any. */
#ifdef _TCO_FINI
_TCO_FINI
#endif

/* Undefine the macros we defined above. */

#undef TC_OPTION
#undef TC_HEADER
#undef _TCO_INIT
#undef _TCO_FINI

/*************************************************************************/

/*
 * Local variables:
 *   c-file-style: "stroustrup"
 *   c-file-offsets: ((case-label . *) (statement-case-intro . *))
 *   indent-tabs-mode: nil
 * End:
 *
 * vim: expandtab shiftwidth=4:
 */
