Add new argument parser
This commit is contained in:
parent
982fad256e
commit
7b5953322f
|
@ -24,6 +24,7 @@ libpsxav = static_library('psxav', [
|
|||
libpsxav_dep = declare_dependency(include_directories: include_directories('libpsxav'), link_with: libpsxav)
|
||||
|
||||
executable('psxavenc', [
|
||||
'psxavenc/args.c',
|
||||
'psxavenc/cdrom.c',
|
||||
'psxavenc/decoding.c',
|
||||
'psxavenc/filefmt.c',
|
||||
|
|
|
@ -0,0 +1,711 @@
|
|||
/*
|
||||
psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend
|
||||
|
||||
Copyright (c) 2019, 2020 Adrian "asie" Siekierka
|
||||
Copyright (c) 2019 Ben "GreaseMonkey" Russell
|
||||
Copyright (c) 2023, 2025 spicyjpeg
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "args.h"
|
||||
|
||||
#define INVALID_PARAM -1
|
||||
|
||||
static int parse_int(
|
||||
int *output,
|
||||
const char *name,
|
||||
const char *value,
|
||||
int min_value,
|
||||
int max_value
|
||||
) {
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "Missing %s value after option\n", name);
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
*output = strtol(value, NULL, 0);
|
||||
|
||||
if (
|
||||
(*output < min_value) ||
|
||||
(max_value >= 0 && *output > max_value)
|
||||
) {
|
||||
if (max_value >= 0)
|
||||
fprintf(stderr, "Invalid %s: %d (must be in %d-%d range)\n", name, *output, min_value, max_value);
|
||||
else
|
||||
fprintf(stderr, "Invalid %s: %d (must be %d or greater)\n", name, *output, min_value);
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int parse_int_one_of(
|
||||
int *output,
|
||||
const char *name,
|
||||
const char *value,
|
||||
int value_a,
|
||||
int value_b
|
||||
) {
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "Missing %s value after option\n", name);
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
*output = strtol(value, NULL, 0);
|
||||
|
||||
if (*output != value_a && *output != value_b) {
|
||||
fprintf(stderr, "Invalid %s: %d (must be %d or %d)\n", name, *output, value_a, value_b);
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int parse_enum(
|
||||
int *output,
|
||||
const char *name,
|
||||
const char *value,
|
||||
const char *const *choices,
|
||||
int count
|
||||
) {
|
||||
if (value == NULL) {
|
||||
fprintf(stderr, "Missing %s value after option\n", name);
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (strcmp(value, choices[i]) == 0) {
|
||||
*output = i;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(
|
||||
stderr,
|
||||
"Invalid %s: %s\n"
|
||||
"Must be one of the following values:\n",
|
||||
name,
|
||||
value
|
||||
);
|
||||
for (int i = 0; i < count; i++)
|
||||
fprintf(stderr, " %s\n", choices[i]);
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
static const char *const general_options_help =
|
||||
"General options:\n"
|
||||
" -h Show this help message and exit\n"
|
||||
" -V Show version information and exit\n"
|
||||
" -q Suppress all non-error messages\n"
|
||||
" -t format Use (or show help for) specified output format\n"
|
||||
" xa: [A.] XA-ADPCM, 2336-byte sectors\n"
|
||||
" xacd: [A.] XA-ADPCM, 2352-byte sectors\n"
|
||||
" spu: [A.] raw SPU-ADPCM mono data\n"
|
||||
" spui: [A.] raw SPU-ADPCM interleaved data\n"
|
||||
" vag: [A.] .vag SPU-ADPCM mono\n"
|
||||
" vagi: [A.] .vag SPU-ADPCM interleaved\n"
|
||||
" str: [AV] .str video, 2336-byte sectors\n"
|
||||
" strcd: [AV] .str video, 2352-byte sectors\n"
|
||||
" strspu: [AV] .str video, 2048-byte sectors\n"
|
||||
" strv: [.V] .str video, 2048-byte sectors\n"
|
||||
" sbs: [.V] .sbs video\n"
|
||||
" -R key=value,... Pass custom options to libswresample (see FFmpeg docs)\n"
|
||||
" -S key=value,... Pass custom options to libswscale (see FFmpeg docs)\n"
|
||||
"\n";
|
||||
|
||||
static const char *const format_names[NUM_FORMATS] = {
|
||||
"xa",
|
||||
"xacd",
|
||||
"spu",
|
||||
"vag",
|
||||
"spui",
|
||||
"vagi",
|
||||
"str",
|
||||
"strcd",
|
||||
"strspu",
|
||||
"strv",
|
||||
"sbs"
|
||||
};
|
||||
|
||||
static void init_default_args(args_t *args) {
|
||||
args->flags = 0;
|
||||
|
||||
args->input_file = NULL;
|
||||
args->output_file = NULL;
|
||||
args->swresample_options = NULL;
|
||||
args->swscale_options = NULL;
|
||||
|
||||
if (
|
||||
args->format == FORMAT_XA || args->format == FORMAT_XACD ||
|
||||
args->format == FORMAT_STR || args->format == FORMAT_STRCD
|
||||
)
|
||||
args->audio_frequency = 37800;
|
||||
else
|
||||
args->audio_frequency = 44100;
|
||||
if (args->format == FORMAT_SPU || args->format == FORMAT_VAG)
|
||||
args->audio_channels = 1;
|
||||
else
|
||||
args->audio_channels = 2;
|
||||
|
||||
args->audio_bit_depth = 4;
|
||||
args->audio_xa_file = 0;
|
||||
args->audio_xa_channel = 0;
|
||||
args->audio_interleave = 2048;
|
||||
args->audio_loop_point = -1;
|
||||
|
||||
args->video_codec = BS_CODEC_V2;
|
||||
args->video_width = 320;
|
||||
args->video_height = 240;
|
||||
|
||||
args->str_fps_num = 15;
|
||||
args->str_fps_den = 1;
|
||||
args->str_cd_speed = 2;
|
||||
|
||||
if (args->format == FORMAT_SPU || args->format == FORMAT_VAG)
|
||||
args->alignment = 64;
|
||||
else if (args->format == FORMAT_SBS)
|
||||
args->alignment = 8192;
|
||||
else
|
||||
args->alignment = 2048;
|
||||
}
|
||||
|
||||
static int parse_general_option(args_t *args, char option, const char *param) {
|
||||
int parsed;
|
||||
|
||||
switch (option) {
|
||||
case '-':
|
||||
args->flags |= FLAG_IGNORE_OPTIONS;
|
||||
return 1;
|
||||
|
||||
case 'h':
|
||||
args->flags |= FLAG_PRINT_HELP;
|
||||
return 1;
|
||||
|
||||
case 'V':
|
||||
args->flags |= FLAG_PRINT_VERSION;
|
||||
return 1;
|
||||
|
||||
case 'q':
|
||||
args->flags |= FLAG_QUIET | FLAG_HIDE_PROGRESS;
|
||||
return 1;
|
||||
|
||||
case 't':
|
||||
parsed = parse_enum(&(args->format), "format", param, format_names, NUM_FORMATS);
|
||||
if (parsed > 0)
|
||||
init_default_args(args);
|
||||
return parsed;
|
||||
|
||||
case 'R':
|
||||
if (param == NULL) {
|
||||
fprintf(stderr, "Missing libswresample parameter list after option\n");
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
args->swresample_options = param;
|
||||
return 2;
|
||||
|
||||
case 'S':
|
||||
if (param == NULL) {
|
||||
fprintf(stderr, "Missing libswscale parameter list after option\n");
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
args->swscale_options = param;
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const xa_options_help =
|
||||
"XA-ADPCM options:\n"
|
||||
" [-f 18900|37800] [-c 1|2] [-b 4|8] [-F 0-255] [-C 0-31]\n"
|
||||
"\n"
|
||||
" -f 18900|37800 Use specified sample rate (default 37800)\n"
|
||||
" -c 1|2 Use specified channel count (default 2)\n"
|
||||
" -b 4|8 Use specified bit depth (default 4)\n"
|
||||
" -F 0-255 Set CD-XA file number (for both audio and video, default 0)\n"
|
||||
" -C 0-31 Set CD-XA channel number (for both audio and video, default 0)\n"
|
||||
"\n";
|
||||
|
||||
static int parse_xa_option(args_t *args, char option, const char *param) {
|
||||
switch (option) {
|
||||
case 'f':
|
||||
return parse_int_one_of(&(args->audio_frequency), "sample rate", param, 18900, 37800);
|
||||
|
||||
case 'c':
|
||||
return parse_int_one_of(&(args->audio_channels), "channel count", param, 1, 2);
|
||||
|
||||
case 'b':
|
||||
return parse_int_one_of(&(args->audio_bit_depth), "bit depth", param, 4, 8);
|
||||
|
||||
case 'F':
|
||||
return parse_int(&(args->audio_xa_file), "file number", param, 0, 255);
|
||||
|
||||
case 'C':
|
||||
return parse_int(&(args->audio_xa_channel), "channel number", param, 0, 31);
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const spu_options_help =
|
||||
"SPU-ADPCM options:\n"
|
||||
" [-f freq] [-a size] [-l ms | -L] [-D]\n"
|
||||
"\n"
|
||||
" -f freq Use specified sample rate (default 44100)\n"
|
||||
" -a size Pad audio data excluding header to multiple of given size (default 64)\n"
|
||||
" -l ms Add loop point at specified offset (in milliseconds)\n"
|
||||
" -L Set loop end flag at the end of data but do not add a loop point\n"
|
||||
" -D Do not prepend encoded data with a dummy silent block\n"
|
||||
"\n";
|
||||
|
||||
static int parse_spu_option(args_t *args, char option, const char *param) {
|
||||
switch (option) {
|
||||
case 'f':
|
||||
return parse_int(&(args->audio_frequency), "sample rate", param, 1, -1);
|
||||
|
||||
case 'a':
|
||||
return parse_int(&(args->alignment), "alignment", param, 1, -1);
|
||||
|
||||
case 'l':
|
||||
args->flags |= FLAG_SPU_LOOP_END;
|
||||
return parse_int(&(args->audio_loop_point), "loop offset", param, 0, -1);
|
||||
|
||||
case 'L':
|
||||
args->flags |= FLAG_SPU_LOOP_END;
|
||||
return 1;
|
||||
|
||||
case 'D':
|
||||
args->flags |= FLAG_SPU_NO_LEADING_DUMMY;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const spui_options_help =
|
||||
"Interleaved SPU-ADPCM options:\n"
|
||||
" [-f freq] [-c channels] [-i size] [-a size] [-L] [-D]\n"
|
||||
"\n"
|
||||
" -f freq Use specified sample rate (default 44100)\n"
|
||||
" -c channels Use specified channel count (default 2)\n"
|
||||
" -i size Use specified channel interleave size (default 2048)\n"
|
||||
" -a size Pad .vag header and each audio chunk to multiples of given size\n"
|
||||
" (default 2048)\n"
|
||||
" -L Set loop end flag at the end of each audio chunk\n"
|
||||
" -D Do not prepend first chunk's data with a dummy silent block\n"
|
||||
"\n";
|
||||
|
||||
static int parse_spui_option(args_t *args, char option, const char *param) {
|
||||
int parsed;
|
||||
|
||||
switch (option) {
|
||||
case 'f':
|
||||
return parse_int(&(args->audio_frequency), "sample rate", param, 1, -1);
|
||||
|
||||
case 'c':
|
||||
return parse_int(&(args->audio_channels), "channel count", param, 1, -1);
|
||||
|
||||
case 'i':
|
||||
parsed = parse_int(&(args->audio_interleave), "interleave", param, 16, -1);
|
||||
|
||||
// Round up to nearest multiple of 16
|
||||
args->audio_interleave = (args->audio_interleave + 15) & ~15;
|
||||
return parsed;
|
||||
|
||||
case 'a':
|
||||
return parse_int(&(args->alignment), "alignment", param, 1, -1);
|
||||
|
||||
case 'L':
|
||||
args->flags |= FLAG_SPU_LOOP_END;
|
||||
return 1;
|
||||
|
||||
case 'D':
|
||||
args->flags |= FLAG_SPU_NO_LEADING_DUMMY;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const bs_options_help =
|
||||
"Video options:\n"
|
||||
" [-v v2|v3|v3dc] [-s WxH] [-I]\n"
|
||||
"\n"
|
||||
" -v codec Use specified video codec\n"
|
||||
" v2: MDEC BS v2 (default)\n"
|
||||
" v3: MDEC BS v3\n"
|
||||
" v3dc: MDEC BS v3, expect decoder to wrap DC coefficients\n"
|
||||
" -s WxH Rescale input file to fit within specified size\n"
|
||||
" (16x16-640x512 in 16-pixel increments, default 320x240)\n"
|
||||
" -I Force stretching to given size without preserving aspect ratio\n"
|
||||
"\n";
|
||||
|
||||
const char *const bs_codec_names[NUM_BS_CODECS] = {
|
||||
"v2",
|
||||
"v3",
|
||||
"v3dc"
|
||||
};
|
||||
|
||||
static int parse_bs_option(args_t *args, char option, const char *param) {
|
||||
char *next = NULL;
|
||||
|
||||
switch (option) {
|
||||
case 'v':
|
||||
return parse_enum(&(args->video_codec), "video codec", param, bs_codec_names, NUM_BS_CODECS);
|
||||
|
||||
case 's':
|
||||
if (param == NULL) {
|
||||
fprintf(stderr, "Missing video size after option\n");
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
args->video_width = strtol(param, &next, 10);
|
||||
|
||||
if (next && *next == 'x') {
|
||||
args->video_height = strtol(next + 1, NULL, 10);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid video size (must be specified as <width>x<height>)\n");
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (args->video_width < 16 || args->video_width > 640) {
|
||||
fprintf(stderr, "Invalid video width: %d (must be in 16-640 range)\n", args->video_width);
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
if (args->video_height < 16 || args->video_height > 512) {
|
||||
fprintf(stderr, "Invalid video height: %d (must be in 16-512 range)\n", args->video_height);
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
// Round up to nearest multiples of 16
|
||||
args->video_width = (args->video_width + 15) & ~15;
|
||||
args->video_height = (args->video_height + 15) & ~15;
|
||||
return 2;
|
||||
|
||||
case 'I':
|
||||
args->flags |= FLAG_BS_IGNORE_ASPECT;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const str_options_help =
|
||||
".str container options:\n"
|
||||
" [-r num[/den]] [-x 1|2] [-A]\n"
|
||||
"\n"
|
||||
" -r num[/den] Set video frame rate to specified integer or fraction (default 15)\n"
|
||||
" -x 1|2 Set CD-ROM speed the file is meant to played at (default 2)\n"
|
||||
" -A Place audio sectors after corresponding video sectors\n"
|
||||
" (rather than ahead of them)\n"
|
||||
"\n";
|
||||
|
||||
static int parse_str_option(args_t *args, char option, const char *param) {
|
||||
char *next = NULL;
|
||||
int fps;
|
||||
|
||||
switch (option) {
|
||||
case 'r':
|
||||
if (param == NULL) {
|
||||
fprintf(stderr, "Missing frame rate value after option\n");
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
args->str_fps_num = strtol(param, &next, 10);
|
||||
|
||||
if (next && *next == '/')
|
||||
args->str_fps_den = strtol(next + 1, NULL, 10);
|
||||
else
|
||||
args->str_fps_den = 1;
|
||||
|
||||
if (args->str_fps_num <= 0 || args->str_fps_den <= 0) {
|
||||
fprintf(stderr, "Invalid frame rate (must be a non-zero integer or fraction)\n");
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
|
||||
fps = args->str_fps_num / args->str_fps_den;
|
||||
|
||||
if (fps < 1 || fps > 60) {
|
||||
fprintf(stderr, "Invalid frame rate: %d/%d (must be in 1-60 range)\n", args->str_fps_num, args->str_fps_den);
|
||||
return INVALID_PARAM;
|
||||
}
|
||||
return 2;
|
||||
|
||||
case 'x':
|
||||
return parse_int_one_of(&(args->str_cd_speed), "CD-ROM speed", param, 1, 2);
|
||||
|
||||
case 'A':
|
||||
args->flags |= FLAG_STR_TRAILING_AUDIO;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const sbs_options_help =
|
||||
".sbs container options:\n"
|
||||
" [-a size]\n"
|
||||
"\n"
|
||||
" -a size Set size of each video frame (default 8192)\n"
|
||||
"\n";
|
||||
|
||||
static int parse_sbs_option(args_t *args, char option, const char *param) {
|
||||
switch (option) {
|
||||
case 'a':
|
||||
return parse_int(&(args->alignment), "video frame size", param, 256, -1);
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const general_usage =
|
||||
"Usage:\n"
|
||||
" psxavenc -t xa|xacd [xa-options] <in> <out.xa>\n"
|
||||
" psxavenc -t spu|vag [spu-options] <in> <out.vag>\n"
|
||||
" psxavenc -t spui|vagi [spui-options] <in> <out.vag>\n"
|
||||
" psxavenc -t str|strcd [xa-options] [bs-options] [str-options] <in> <out.str>\n"
|
||||
" psxavenc -t strspu [spui-options] [bs-options] [str-options] <in> <out.str>\n"
|
||||
" psxavenc -t strv [bs-options] [str-options] <in> <out.str>\n"
|
||||
" psxavenc -t sbs [bs-options] [sbs-options] <in> <out.sbs>\n"
|
||||
"\n";
|
||||
|
||||
static const struct {
|
||||
const char *usage;
|
||||
const char *audio_options_help;
|
||||
const char *video_options_help;
|
||||
const char *container_options_help;
|
||||
int (*parse_audio_option)(args_t *, char, const char *);
|
||||
int (*parse_video_option)(args_t *, char, const char *);
|
||||
int (*parse_container_option)(args_t *, char, const char *);
|
||||
} format_info[NUM_FORMATS] = {
|
||||
{
|
||||
.usage = "psxavenc -t xa [xa-options] <in> <out.xa>",
|
||||
.audio_options_help = xa_options_help,
|
||||
.video_options_help = NULL,
|
||||
.container_options_help = NULL,
|
||||
.parse_audio_option = parse_xa_option,
|
||||
.parse_video_option = NULL,
|
||||
.parse_container_option = NULL
|
||||
}, {
|
||||
.usage = "psxavenc -t xacd [xa-options] <in> <out.xa>",
|
||||
.audio_options_help = xa_options_help,
|
||||
.video_options_help = NULL,
|
||||
.container_options_help = NULL,
|
||||
.parse_audio_option = parse_xa_option,
|
||||
.parse_video_option = NULL,
|
||||
.parse_container_option = NULL
|
||||
}, {
|
||||
.usage = "psxavenc -t spu [spu-options] <in> <out>",
|
||||
.audio_options_help = spu_options_help,
|
||||
.video_options_help = NULL,
|
||||
.container_options_help = NULL,
|
||||
.parse_audio_option = parse_spu_option,
|
||||
.parse_video_option = NULL,
|
||||
.parse_container_option = NULL
|
||||
}, {
|
||||
.usage = "psxavenc -t vag [spu-options] <in> <out.vag>",
|
||||
.audio_options_help = spu_options_help,
|
||||
.video_options_help = NULL,
|
||||
.container_options_help = NULL,
|
||||
.parse_audio_option = parse_spu_option,
|
||||
.parse_video_option = NULL,
|
||||
.parse_container_option = NULL
|
||||
}, {
|
||||
.usage = "psxavenc -t spui [spui-options] <in> <out>",
|
||||
.audio_options_help = spui_options_help,
|
||||
.video_options_help = NULL,
|
||||
.container_options_help = NULL,
|
||||
.parse_audio_option = parse_spui_option,
|
||||
.parse_video_option = NULL,
|
||||
.parse_container_option = NULL
|
||||
}, {
|
||||
.usage = "psxavenc -t vagi [spui-options] <in> <out.vag>",
|
||||
.audio_options_help = spui_options_help,
|
||||
.video_options_help = NULL,
|
||||
.container_options_help = NULL,
|
||||
.parse_audio_option = parse_spui_option,
|
||||
.parse_video_option = NULL,
|
||||
.parse_container_option = NULL
|
||||
}, {
|
||||
.usage = "psxavenc -t str [xa-options] [bs-options] [str-options] <in> <out.str>",
|
||||
.audio_options_help = xa_options_help,
|
||||
.video_options_help = bs_options_help,
|
||||
.container_options_help = str_options_help,
|
||||
.parse_audio_option = parse_xa_option,
|
||||
.parse_video_option = parse_bs_option,
|
||||
.parse_container_option = parse_str_option
|
||||
}, {
|
||||
.usage = "psxavenc -t strcd [xa-options] [bs-options] [str-options] <in> <out.str>",
|
||||
.audio_options_help = xa_options_help,
|
||||
.video_options_help = bs_options_help,
|
||||
.container_options_help = str_options_help,
|
||||
.parse_audio_option = parse_xa_option,
|
||||
.parse_video_option = parse_bs_option,
|
||||
.parse_container_option = parse_str_option
|
||||
}, {
|
||||
.usage = "psxavenc -t strspu [spui-options] [bs-options] [str-options] <in> <out.str>",
|
||||
.audio_options_help = spui_options_help,
|
||||
.video_options_help = bs_options_help,
|
||||
.container_options_help = str_options_help,
|
||||
.parse_audio_option = parse_spui_option,
|
||||
.parse_video_option = parse_bs_option,
|
||||
.parse_container_option = parse_str_option
|
||||
}, {
|
||||
.usage = "psxavenc -t strv [bs-options] [str-options] <in> <out.str>",
|
||||
.audio_options_help = NULL,
|
||||
.video_options_help = bs_options_help,
|
||||
.container_options_help = str_options_help,
|
||||
.parse_audio_option = NULL,
|
||||
.parse_video_option = parse_bs_option,
|
||||
.parse_container_option = parse_str_option
|
||||
}, {
|
||||
.usage = "psxavenc -t sbs [bs-options] [sbs-options] <in> <out.sbs>",
|
||||
.audio_options_help = NULL,
|
||||
.video_options_help = bs_options_help,
|
||||
.container_options_help = sbs_options_help,
|
||||
.parse_audio_option = NULL,
|
||||
.parse_video_option = parse_bs_option,
|
||||
.parse_container_option = parse_sbs_option
|
||||
}
|
||||
};
|
||||
|
||||
static int parse_option(args_t *args, char option, const char *param) {
|
||||
int parsed = parse_general_option(args, option, param);
|
||||
|
||||
if (parsed == 0 && args->format != FORMAT_INVALID) {
|
||||
if (format_info[args->format].parse_audio_option != NULL)
|
||||
parsed = format_info[args->format].parse_audio_option(args, option, param);
|
||||
}
|
||||
if (parsed == 0 && args->format != FORMAT_INVALID) {
|
||||
if (format_info[args->format].parse_video_option != NULL)
|
||||
parsed = format_info[args->format].parse_video_option(args, option, param);
|
||||
}
|
||||
if (parsed == 0 && args->format != FORMAT_INVALID) {
|
||||
if (format_info[args->format].parse_container_option != NULL)
|
||||
parsed = format_info[args->format].parse_container_option(args, option, param);
|
||||
}
|
||||
if (parsed == 0) {
|
||||
if (args->format == FORMAT_INVALID)
|
||||
fprintf(
|
||||
stderr,
|
||||
"Unknown general option: -%c\n"
|
||||
"(if this is a format-specific option, it shall be passed after -t)\n",
|
||||
option
|
||||
);
|
||||
else
|
||||
fprintf(stderr, "Unknown option for format %s: -%c\n", format_names[args->format], option);
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static void print_help(format_t format) {
|
||||
if (format == FORMAT_INVALID) {
|
||||
printf(
|
||||
"%s%s%s%s%s%s%s%s",
|
||||
general_usage,
|
||||
general_options_help,
|
||||
xa_options_help,
|
||||
spu_options_help,
|
||||
spui_options_help,
|
||||
bs_options_help,
|
||||
str_options_help,
|
||||
sbs_options_help
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
"Usage:\n"
|
||||
" %s\n"
|
||||
"\n"
|
||||
"%s",
|
||||
format_info[format].usage,
|
||||
general_options_help
|
||||
);
|
||||
if (format_info[format].audio_options_help != NULL)
|
||||
printf("%s", format_info[format].audio_options_help);
|
||||
if (format_info[format].video_options_help != NULL)
|
||||
printf("%s", format_info[format].video_options_help);
|
||||
if (format_info[format].container_options_help != NULL)
|
||||
printf("%s", format_info[format].container_options_help);
|
||||
}
|
||||
|
||||
bool parse_args(args_t *args, const char *const *options, int count) {
|
||||
int arg_index = 0;
|
||||
|
||||
while (arg_index < count) {
|
||||
const char *option = options[arg_index];
|
||||
|
||||
if (option[0] == '-' && option[2] == 0 && !(args->flags & FLAG_IGNORE_OPTIONS)) {
|
||||
const char *param;
|
||||
if ((arg_index + 1) < count)
|
||||
param = options[arg_index + 1];
|
||||
else
|
||||
param = NULL;
|
||||
|
||||
int parsed = parse_option(args, option[1], param);
|
||||
if (parsed <= 0)
|
||||
return false;
|
||||
|
||||
arg_index += parsed;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (args->input_file == NULL) {
|
||||
args->input_file = option;
|
||||
} else if (args->output_file == NULL) {
|
||||
args->output_file = option;
|
||||
} else {
|
||||
fprintf(stderr, "There should be no arguments after the output file path\n");
|
||||
return false;
|
||||
}
|
||||
arg_index++;
|
||||
}
|
||||
|
||||
if (args->flags & FLAG_PRINT_HELP) {
|
||||
print_help(args->format);
|
||||
return false;
|
||||
}
|
||||
if (args->format == FORMAT_INVALID || args->input_file == NULL || args->output_file == NULL) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"%s"
|
||||
"For more information about the options supported for a given output format, run:\n"
|
||||
" psxavenc -t <format> -h\n"
|
||||
"To view the full list of supported options, run:\n"
|
||||
" psxavenc -h\n",
|
||||
general_usage
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
psxavenc: MDEC video + SPU/XA-ADPCM audio encoder frontend
|
||||
|
||||
Copyright (c) 2019, 2020 Adrian "asie" Siekierka
|
||||
Copyright (c) 2019 Ben "GreaseMonkey" Russell
|
||||
Copyright (c) 2023, 2025 spicyjpeg
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define NUM_FORMATS 11
|
||||
#define NUM_BS_CODECS 3
|
||||
|
||||
enum {
|
||||
FLAG_IGNORE_OPTIONS = 1 << 0,
|
||||
FLAG_QUIET = 1 << 1,
|
||||
FLAG_HIDE_PROGRESS = 1 << 2,
|
||||
FLAG_PRINT_HELP = 1 << 3,
|
||||
FLAG_PRINT_VERSION = 1 << 4,
|
||||
FLAG_SPU_LOOP_END = 1 << 5,
|
||||
FLAG_SPU_NO_LEADING_DUMMY = 1 << 6,
|
||||
FLAG_BS_IGNORE_ASPECT = 1 << 7,
|
||||
FLAG_STR_TRAILING_AUDIO = 1 << 8
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
FORMAT_INVALID = -1,
|
||||
FORMAT_XA,
|
||||
FORMAT_XACD,
|
||||
FORMAT_SPU,
|
||||
FORMAT_VAG,
|
||||
FORMAT_SPUI,
|
||||
FORMAT_VAGI,
|
||||
FORMAT_STR,
|
||||
FORMAT_STRCD,
|
||||
FORMAT_STRSPU,
|
||||
FORMAT_STRV,
|
||||
FORMAT_SBS
|
||||
} format_t;
|
||||
|
||||
typedef enum {
|
||||
BS_CODEC_INVALID = -1,
|
||||
BS_CODEC_V2,
|
||||
BS_CODEC_V3,
|
||||
BS_CODEC_V3DC
|
||||
} bs_codec_t;
|
||||
|
||||
typedef struct {
|
||||
int flags;
|
||||
|
||||
format_t format;
|
||||
const char *input_file;
|
||||
const char *output_file;
|
||||
const char *swresample_options;
|
||||
const char *swscale_options;
|
||||
|
||||
int audio_frequency; // 18900 or 37800 Hz
|
||||
int audio_channels;
|
||||
int audio_bit_depth; // 4 or 8
|
||||
int audio_xa_file; // 00-FF
|
||||
int audio_xa_channel; // 00-1F
|
||||
int audio_interleave;
|
||||
int audio_loop_point;
|
||||
|
||||
bs_codec_t video_codec;
|
||||
int video_width;
|
||||
int video_height;
|
||||
|
||||
int str_fps_num;
|
||||
int str_fps_den;
|
||||
int str_cd_speed; // 1 or 2
|
||||
int alignment;
|
||||
} args_t;
|
||||
|
||||
bool parse_args(args_t *args, const char *const *options, int count);
|
|
@ -108,7 +108,7 @@ void print_version(void) {
|
|||
printf("psxavenc " VERSION "\n");
|
||||
}
|
||||
|
||||
int parse_args(settings_t* settings, int argc, char** argv) {
|
||||
int parse_args_old(settings_t* settings, int argc, char** argv) {
|
||||
int c, i;
|
||||
char *next;
|
||||
while ((c = getopt(argc, argv, "?hVqt:F:C:f:b:c:LR:i:a:s:IS:r:x:")) != -1) {
|
||||
|
@ -389,7 +389,7 @@ int main(int argc, char **argv) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
arg_offset = parse_args(&settings, argc, argv);
|
||||
arg_offset = parse_args_old(&settings, argc, argv);
|
||||
if (arg_offset < 0) {
|
||||
return 1;
|
||||
} else if (argc < arg_offset + 2) {
|
||||
|
|
Loading…
Reference in New Issue