#include #include #include #include #include #include #include "converter.h" #include "wav_file.h" static void usage() { fprintf(stderr, "wav2agb\n"); fprintf(stderr, "\n"); fprintf(stderr, "Usage: wav2agb [options] []\n"); fprintf(stderr, "\n"); fprintf(stderr, "Options:\n"); fprintf(stderr, "-s, --symbol | symbol name for wave header (default: file name)\n"); fprintf(stderr, "-l, --lookahead | DPCM compression lookahead 1..8 (default: 3)\n"); fprintf(stderr, "-c, --compress | compress output with DPCM\n"); fprintf(stderr, "-f, --fast-compress | compress output with DPCM fast\n"); fprintf(stderr, "--no-pad | omit trailing padding in compressed output\n"); fprintf(stderr, "-b, --binary | output raw binary instead of assembly\n"); fprintf(stderr, "--loop-start | override loop start (integer)\n"); fprintf(stderr, "--loop-end | override loop end (integer)\n"); fprintf(stderr, "--tune | override tuning (float)\n"); fprintf(stderr, "--key | override midi key (int)\n"); fprintf(stderr, "--rate | override base samplerate (int)\n"); fprintf(stderr, "--set-agbl | adds the custom agbl chunk to the given input .wav file\n"); exit(1); } static void version() { printf("wav2agb v1.1 (c) 2019 ipatix\n"); exit(0); } static void die(const char *msg, ...) { va_list args; va_start(args, msg); vfprintf(stderr, msg, args); va_end(args); exit(1); } static void fix_str(std::string& str) { // replaces all characters that are not alphanumerical for (size_t i = 0; i < str.size(); i++) { if (str[i] >= 'a' && str[i] <= 'z') continue; if (str[i] >= 'A' && str[i] <= 'Z') continue; if (str[i] >= '0' && str[i] <= '9' && i > 0) continue; str[i] = '_'; } } static char path_seperators[] = { '/', #ifdef _WIN32 '\\', #endif '\0' }; static std::string filename_without_ext(const std::string& str) { size_t last_path_seperator = 0; char *sep = path_seperators; while (*sep) { size_t pos = str.find_last_of(*sep); if (pos != std::string::npos) last_path_seperator = std::max(pos, last_path_seperator); sep += 1; } size_t file_ext_dot_pos = str.find_last_of('.'); if (file_ext_dot_pos == std::string::npos) return std::string(str); assert(file_ext_dot_pos != last_path_seperator); if (file_ext_dot_pos > last_path_seperator) return str.substr(0, file_ext_dot_pos); return std::string(str); } static std::string filename_without_dir(const std::string& str) { size_t last_path_seperator = 0; bool path_seperator_found = false; char *sep = path_seperators; while (*sep) { size_t pos = str.find_last_of(*sep); if (pos != std::string::npos) { last_path_seperator = std::max(pos, last_path_seperator); path_seperator_found = true; } sep += 1; } if (str.size() > 0 && path_seperator_found) { return str.substr(last_path_seperator + 1); } else { return std::string(str); } } static cmp_type arg_compress = cmp_type::none; static out_type arg_output_type = out_type::assembly; static std::string arg_sym; static bool arg_input_file_read = false; static bool arg_output_file_read = false; static std::string arg_input_file; static std::string arg_output_file; static bool arg_set_agbl = false; static int32_t arg_agbl_value = 0; int main(int argc, char *argv[]) { try { if (argc == 1) usage(); for (int i = 1; i < argc; i++) { std::string st(argv[i]); if (st == "-s" || st == "--symbol") { if (++i >= argc) die("-s: missing symbol name\n"); arg_sym = argv[i]; fix_str(arg_sym); } else if (st == "-c" || st == "--compress") { arg_compress = cmp_type::dpcm; } else if (st == "-f" || st == "--compress-fast") { arg_compress = cmp_type::dpcm; enable_dpcm_lookahead_fast(); } else if (st == "--no-pad") { disable_dpcm_padding(); } else if (st == "-b" || st == "--binary") { arg_output_type = out_type::binary; } else if (st == "--verbose") { enable_dpcm_verbose(); } else if (st == "-l" || st == "--lookahead") { if (++i >= argc) die("-l: missing parameter"); set_dpcm_lookahead(std::stoul(argv[i], nullptr, 10)); } else if (st == "--version") { version(); } else if (st == "--loop-start") { if (++i >= argc) die("--loop-start: missing parameter"); uint32_t start = static_cast(std::stoul(argv[i], nullptr, 10)); set_wav_loop_start(start); } else if (st == "--loop-end") { if (++i >= argc) die("--loop-end: missing parameter"); uint32_t end = static_cast(std::stoul(argv[i], nullptr, 10)); set_wav_loop_end(end); } else if (st == "--tune") { if (++i >= argc) die("--tune: missing parameter"); double tune = std::stod(argv[i], nullptr); set_wav_tune(tune); } else if (st == "--key") { if (++i >= argc) die("--key: missing parameter"); int key = std::stoi(argv[i], nullptr, 10); if (key < 0) key = 0; if (key > 127) key = 127; set_wav_key(static_cast(key)); } else if (st == "--rate") { if (++i >= argc) die("--rate: missing parameter"); uint32_t rate = static_cast(std::stoul(argv[i], nullptr, 10)); set_wav_rate(rate); } else if (st == "--set-agbl") { if (++i >= argc) die("--set-agbl: missing parameter"); arg_agbl_value = std::stoi(argv[i], nullptr, 10); arg_set_agbl = true; } else { if (st == "--") { if (++i >= argc) die("--: missing file name\n"); } if (!arg_input_file_read) { arg_input_file = argv[i]; if (arg_input_file.size() < 1) die("empty input file name\n"); arg_input_file_read = true; } else if (!arg_output_file_read) { arg_output_file = argv[i]; if (arg_output_file.size() < 1) die("empty output file name\n"); arg_output_file_read = true; } else { die("Too many files specified\n"); } } } // check arguments if (!arg_input_file_read) { die("No input file specified\n"); } if (!arg_output_file_read) { // create output file name if none is provided if (arg_set_agbl) { arg_output_file = arg_input_file; } else if (arg_output_type == out_type::binary) { arg_output_file = filename_without_ext(arg_input_file) + ".bin"; } else { arg_output_file = filename_without_ext(arg_input_file) + ".s"; } arg_output_file_read = true; } if (arg_sym.size() == 0) { arg_sym = filename_without_dir(filename_without_ext(arg_output_file)); fix_str(arg_sym); } if (arg_set_agbl) { // Parse the WAV file once to get both chunks and metadata wav_file wav(arg_input_file); // Calculate actual loop-end value uint32_t loop_end_value; if (arg_agbl_value < 0) { // Negative value: offset from end of samples int64_t calculated = static_cast(wav.numSamples) + arg_agbl_value; if (calculated < 0) { die("--set-agbl: negative offset %d exceeds total samples %u\n", arg_agbl_value, wav.numSamples); } loop_end_value = static_cast(calculated); } else { // Positive value: use directly loop_end_value = static_cast(arg_agbl_value); } write_wav_with_agbl_chunk(arg_output_file, wav.chunks, loop_end_value); return 0; } convert(arg_input_file, arg_output_file, arg_sym, arg_compress, arg_output_type); return 0; } catch (const std::exception& e) { fprintf(stderr, "std lib error:\n%s\n", e.what()); } return 1; }