/*
 * wav2wma.js
 * まとめて WAV を WMA に変換する.
 * - 入力ファイルやフォルダはコマンドラインで指定する. フォルダの場合はそのフォ
 *   ルダ内の *.wav を入力ファイルとして扱う.
 * - 出力ファイル名は入力ファイル名の拡張子を .wma に変えたものになる.
 *
 * 例: D:\sound1\*.wav と D:\sound2\*.wav を WMA にする
 *   wav2wma.js D:\sound1 D:\sound2
 *
 * 例: E:\sound3\*.wav を "Professional" コーデック, VBR 音質 50% の WMA にする
 *   wav2wma.js E:\sound3 /pro /vbr:50
 *
 * 例: 開かれているファイル群を VBR 音質 75% の WMA でフォルダ F:\sound4 に出力
 *   wav2wma.js F:\sound4 /vbr:75 /export
 *
 * Options:
 * /cbr:<bitrate in kbps>
 * /cbr2:<bitrate in kbps>
 * /vbr:<quality in %>
 * /vbr2:<bitrate in kbps>
 * /fs:<sampling rate in kHz>
 * /pro
 * /lossless
 * /export
 * /force
 * /suspend
 */

@set @EXIT_SUCCESS = 0
@set @EXIT_FAILURE = 1

@set @DEFAULT_CH_KBPS = 64      // チャンネル当たりのビットレート (kbps)
@set @DEFAULT_QUALITY = 75      // 音質 (%)

var InputExtRegExp = /^wav$/i;  // 入力ファイル拡張子 (正規表現オブジェクト)
var OutputExt = "wma";          // 出力ファイル拡張子

var Shell= new ActiveXObject("WScript.Shell");
var Fso = new ActiveXObject("Scripting.FileSystemObject");
var Opt = new struct_options();

main();
exit();

// =======================================================================

// 文字端末にテキストを表示.

function echo(text)
{
    Script.Echo(text);
}

// スクリプトの終了関数. 引数は省略可.

function exit(exit_code)
{
    Script.Quit(exit_code);
}

// スクリプトの使用方法を表示.

function print_usage()
{
    echo(Script.ScriptName);
    echo("  [/cbr[:<ビットレート kbps>] |");
    echo("   /cbr2[:<ビットレート kbps>] |");
    echo("   /vbr[:<音質 %>] |");
    echo("   /vbr2[:<ビットレート kbps>]]");
    echo("  [/fs:<サンプリング レート kHz>]");
    echo("  [/pro | /lossless] [/force] [/suspend]");
    echo("  <フォルダ名/ファイル名> [<フォルダ名/ファイル名 2> [...]]");
    echo();
    echo("  /cbr       CBR, 1-pass でエンコードする。");
    echo("  /cbr2      CBR, 2-pass でエンコードする。");
    echo("  /vbr       VBR, 1-pass でエンコードする (音質 1〜99%)。");
    echo("  /vbr2      VBR, 2-pass でエンコードする (平均ビットレート指定)。");
    echo("  /fs        優先するサンプリング レート (kHz)。");
    echo("  /pro       Windows Media Audio Professional を使用。");
    echo("  /lossless  Windows Media Audio Lossless を使用。");
    echo("  /export    開かれているファイル群を圧縮する。");
    echo("  /force     既存ファイルがあっても上書きする。");
    echo("  /suspend   終了後 OS を待機状態にする。");
    echo();
    echo("注: ビットレートの指定は 2 ch の場合の値 (kbps) を書く。");
    echo();
    echo("注: ビットレートの既定値は " + (@DEFAULT_CH_KBPS * 2) + " kbps。音質の既定値は " + @DEFAULT_QUALITY + "%。");
    echo();
    echo("注: フォルダ名やファイル名が一つも指定されていない場合は、指定コー");
    echo("    デックのフォーマットが列挙表示される。");
    echo();
}

// 使用可能フォーマットを表示. 下の print_formats() から呼ばれる.

function print_fmts(codec_info, fs, ch, is_vbr, is_2pass)
{
    var fmt_list, fmt, i;

    codec_info.IsVBR = is_vbr;
    codec_info.IsTwoPass = is_2pass;

    fmt_list = codec_info.GetCodecFormats(fs, ch);
    fmt_list = fmt_list.toArray();      // VBArray -> JScript array

    if (fmt_list.length) {
        echo("--- " +
            (fs / 1000) + " kHz / " +
            ch + " ch / " +
            (is_vbr ? "V" : "C") + "BR / " +
            (is_2pass ? 2 : 1) + "-pass" +
            " ---");

        for (i in fmt_list)
            echo(fmt_list[i].Desc);

        echo();
    }
    return fmt_list.length;
}

// 使用可能フォーマットを表示

function print_formats(codec_info)
{
    var fs_list = new Array();
    var n;
    for (n = 1; n <= 8; n *= 2) {
        fs_list[fs_list.length] = 96000 / n;
        fs_list[fs_list.length] = 88200 / n;
        fs_list[fs_list.length] = 64000 / n;
    }

    echo("使用可能な圧縮フォーマット:");

    var num_fmts = 0;
    var fs, ch, i;
    for (i in fs_list) {
        for (ch = 2; ch; --ch) {
            fs = fs_list[i];
            for (n = 0; n < 4; ++n) {
                num_fmts += print_fmts(codec_info, fs, ch,
                    ((n & 0x02) != 0),      // is VBR
                    ((n & 0x01) != 0));     // is 2-pass
            }
        }
    }

    if (num_fmts == 0)      // たぶん 0 にはならないと思う.
        echo("(なし)");

    echo("※全てのフォーマットを列挙していない可能性があります。");
}

// コマンドラインオプションの既定値

function struct_options()
{
    this.CodecHint      = "";
    this.IsVBR          = false;
    this.IsTwoPass      = false;
    this.ChKbps         = @DEFAULT_CH_KBPS;     // チャンネル当たりのビットレート (kbps)
    this.Quality        = 0;
    this.SampleRate     = 0;
    this.ExportMode     = false;
    this.ForceOverwrite = false;
    this.Suspend        = false;
    this.InputFiles     = new Array();
}

// 文字列→数値変換 (チャンネル当たりのビットレート, kbps).
// read_options() の部品.

function str2chkbps(str)
{
    var val = @DEFAULT_CH_KBPS;

    if ((typeof(str) == "string") && str.length) {
        val = parseInt(str);
        if (val >= 1000)        // たぶん bps
            val = Math.round(val / 1000);
        val /= 2;               // チャンネル当たりの kbps
    }
    return val;
}

// 文字列→数値変換 (音質設定, 1 - 100%).
// read_options() の部品.

function str2quality(str)
{
    var val = @DEFAULT_QUALITY;

    if ((typeof(str) == "string") && str.length) {
        val = parseInt(str);
        if (val < 1)
            val = 1;
        else if (val > 100)
            val = 100;
    }
    return val;
}

// 文字列→数値変換 (サンプリングレート, Hz).
// read_options() の部品.

function str2fs(str)
{
    var val = 0;        // 既定の戻り値

    if ((typeof(str) == "string") && str.length) {
        val = parseFloat(str);
        if (val < 1000)     // たぶん kHz
            val *= 1000;

        if (val < 8000)
            val = 8000;
        else if (val > 48000*4)
            val = 48000*4;

        val = Math.round(val);      // 整数化
        switch (val) {
        case 44000*4:
        case 44000*2:
        case 44000:
        case 22000:
        case 11000:
            val += val / 440;       // 44000 なら 44100 になる.
            break;
        }
    }
    return val;
}

// コマンドラインのオプションからグローバル変数 Opt.XXX を設定する.

function read_options()
{
    var args = Script.Arguments;

    // ヘルプオプションがあれば使用方法を表示して終了.

    if (args.Named.Exists("?")) {
        print_usage();
        exit(@EXIT_FAILURE);
    }

    // 名前付きオプションを調べる.
    var e, key;

    for (e = new Enumerator(args.Named); !e.atEnd(); e.moveNext()) {
        key = e.item().toLowerCase();

        // CBR / VBR / 1-pass / 2-pass

        if (key == "cbr") {         // CBR, 1-pass
            Opt.IsVBR       = false;
            Opt.IsTwoPass   = false;
            Opt.ChKbps      = str2chkbps(args.Named(key));
            Opt.Quality     = 0;
        }
        else if (key == "cbr2") {   // CBR, 2-pass
            Opt.IsVBR       = false;
            Opt.IsTwoPass   = true;
            Opt.ChKbps      = str2chkbps(args.Named(key));
            Opt.Quality     = 0;
        }
        else if (key == "vbr") {    // VBR, 1-pass
            Opt.IsVBR       = true;
            Opt.IsTwoPass   = false;
            Opt.ChKbps      = 0;
            Opt.Quality     = str2quality(args.Named(key));
        }
        else if (key == "vbr2") {   // VBR, 2-pass
            Opt.IsVBR       = true;
            Opt.IsTwoPass   = true;
            Opt.ChKbps      = str2chkbps(args.Named(key));
            Opt.Quality     = 0;
        }

        // サンプリングレート
        else if (key == "fs")
            Opt.SampleRate = str2fs(args.Named(key));

        // コーデック
        else if (key == "pro")
            Opt.CodecHint = "professional";
        else if (key == "lossless")
            Opt.CodecHint = key;

        // 開かれているファイルの圧縮
        else if (key == "export")
            Opt.ExportMode = true;

        // ファイル上書きオプション
        else if (key == "force")
            Opt.ForceOverwrite = true;

        // 電源コントロール
        else if (key == "suspend")
            Opt.Suspend = true;

        else {
            echo("エラー: 不明なオプション " + e.item());
            exit(@EXIT_FAILURE);
        }
    }

    // フォルダ名/ファイル名
    var i;

    for (i = 0; i < args.Unnamed.length; ++i)
        Opt.InputFiles[i] = args.Unnamed(i);
}

// 使用コーデックを選ぶ.

function select_codec(writer)
{
    var ci_list = writer.CodecInfoList;
    var name, i;

    if (Opt.CodecHint.length) {
        for (i = 0; i < ci_list.Count; ++i) {
            name = ci_list(i).Name.toLowerCase();
            if (name.indexOf("windows media audio") >= 0)
            if (name.indexOf(Opt.CodecHint) >= 0)
                return ci_list(i);
        }
    }

    // 標準 WMA コーデックを探す.

    for (i = 0; i < ci_list.Count; ++i) {
        name = ci_list(i).Name.toLowerCase();
        if (name.indexOf("windows media audio") >= 0) {
            if (name.indexOf("professional") < 0)
            if (name.indexOf("lossless") < 0)
            if (name.indexOf("voice") < 0)
                return ci_list(i);
        }
    }

    echo("コーデックが見つかりません。");
    exit(@EXIT_FAILURE);
}

// lossless フォーマットの選択.
// 引数の bits_per_sample は可能ならこれ以上のビット数を選ぶという意味で使う.
// 下の select_lossless_format() から呼ばれる.

function select_lossless_fmt(codec_info, sample_rate, channels, bits_per_sample)
{
    var fmt_list, fmt;
    var fmt_cand = null;        // 戻り値候補
    var i, diff, diff2;

    codec_info.IsVBR = true;
    codec_info.IsTwoPass = false;

    fmt_list = codec_info.GetCodecFormats(sample_rate, channels);
    fmt_list = fmt_list.toArray();      // VBArray -> JScript array

    for (i in fmt_list) {
        fmt = fmt_list[i];

        diff = fmt.BitsPerSample - bits_per_sample;
        if (diff == 0)
            return fmt;

        if (fmt_cand == null)
            fmt_cand = fmt;
        else {
            diff2 = fmt_cand.BitsPerSample - bits_per_sample;
            if (diff2 * diff >= 0) {    // 同符号
                if (Math.abs(diff2) > Math.abs(diff))
                    fmt_cand = fmt;
            }
            else {
                // 異符号の場合は lossless なので fmt.BitsPerSample > bits_per_sample
                // の場合に fmt を残す.
                if (diff >= 0)
                    fmt_cand = fmt;
            }
        }
    }

    return fmt_cand;        // null の場合あり
}

function select_lossless_format(codec_info, sample_rate, channels, bits_per_sample)
{
    var fmt = select_lossless_fmt(codec_info, sample_rate, channels, bits_per_sample);

    if ((fmt == null) && (channels == 1))
        fmt = select_lossless_fmt(codec_info, sample_rate, 2, bits_per_sample);

    return fmt;
}

// lossy フォーマットの選択.
// Opt.Bitrate/Opt.Quality はこれ以下の設定を選ぶという意味で使う.
// 引数の bits_per_sample には可能なら合わせる.
// 下の select_lossy_format() から呼ばれる.

function select_lossy_fmt(codec_info, sample_rate, channels, bits_per_sample)
{
    var fmt_list, fmt_cand, fmt, fmtx;
    var i, x, diff, diff2;

    // 16 か 24 のどちらかに限定する.
    bits_per_sample = (bits_per_sample <= 16) ? 16 : 24;

    codec_info.IsVBR = Opt.IsVBR;
    codec_info.IsTwoPass = Opt.IsTwoPass;

    fmt_list = codec_info.GetCodecFormats(sample_rate, channels);
    fmt_list = fmt_list.toArray();      // VBArray -> JScript array

    fmt_cand = new Array();     // 戻り値候補
    fmt_cand[0] = null;         // 16 bit
    fmt_cand[1] = null;         // 24 bit

    for (i in fmt_list) {
        fmt = fmt_list[i];

        // VBR 1-pass
        if (Opt.Quality && fmt.IsQualityBased) {
            diff = fmt.Quality - Opt.Quality;
            if ((diff == 0) && (fmt.BitsPerSample == bits_per_sample))
                return fmt;

            switch (fmt.BitsPerSample) {
            case 16:
            case 24:
                x = fmt.BitsPerSample / 8 - 2;
                fmtx = fmt_cand[x];
                if ((fmtx == null) || (diff == 0))
                    fmtx = fmt;
                else {
                    diff2 = fmtx.Quality - Opt.Quality;
                    if (diff2 * diff >= 0) {        // 同符号
                        if (Math.abs(diff2) > Math.abs(diff))
                            fmtx = fmt;
                    }
                    else {  // 異符号の場合はユーザー指定音質より低いほうを残す.
                        if (diff <= 0)
                            fmtx = fmt;
                    }
                }
                fmt_cand[x] = fmtx;
                break;
            }
        }
        // VBR 1-pass 以外.
        else if (!Opt.Quality && !fmt.IsQualityBased) {
            diff = Math.round(fmt.Bitrate / 1000) - Opt.ChKbps * channels;
            if ((diff == 0) && (fmt.BitsPerSample == bits_per_sample))
                return fmt;

            switch (fmt.BitsPerSample) {
            case 16:
            case 24:
                x = fmt.BitsPerSample / 8 - 2;
                fmtx = fmt_cand[x];
                if ((fmtx == null) || (diff == 0))
                    fmtx = fmt;
                else {
                    diff2 = Math.round(fmtx.Bitrate / 1000) - Opt.ChKbps * channels;
                    if (diff2 * diff >= 0) {        // 同符号
                        if (Math.abs(diff2) > Math.abs(diff))
                            fmtx = fmt;
                    }
                    else {  // 異符号の場合はユーザー指定 bitrate より低いほうを残す.
                        if (diff <= 0)
                            fmtx = fmt;
                    }
                }
                fmt_cand[x] = fmtx;
                break;
            }
        }
    }

    // ビット数が引数と同じほうを優先して戻り値にする. null が返る場合あり.
    x = bits_per_sample / 8 - 2;
    return fmt_cand[x] ? fmt_cand[x] : fmt_cand[x ^ 1];
}

function select_lossy_format(codec_info, sample_rate, channels, bits_per_sample)
{
    var fmt = select_lossy_fmt(codec_info, sample_rate, channels, bits_per_sample);

    if ((fmt == null) && (channels == 1))
        fmt = select_lossy_fmt(codec_info, sample_rate, 2, bits_per_sample);

    return fmt;
}

// ショートカット (*.lnk) なら, それの指すファイル名に置き換える (フルパス).
// ショートカットでなければそのまま戻り値にする.

function get_link_target(file_path)
{
    if (Fso.GetExtensionName(file_path).toLowerCase() == "lnk")
        return Shell.CreateShortcut(file_path).TargetPath;
    else
        return file_path;
}

// 引数のファイルを writer で変換して圧縮ファイルを作る.
// 出力ファイル名は入力ファイル名の拡張子を置き換えたものになる.

function process_file(writer, codec_info, input_file)
{
    var input_ext = Fso.GetExtensionName(input_file);       // '.' は含まない.
    var output_file = input_file.slice(0, -input_ext.length) + OutputExt;

    echo(output_file);

    // ファイルが存在して作成時刻も元ファイルより新しければ, そのままにする.
    // ただし /force オプション指定時は上書きする.

    if (!Opt.ForceOverwrite)
    if (Fso.FileExists(output_file))
    if (Fso.GetFile(output_file).DateLastModified > Fso.GetFile(input_file).DateLastModified) {
        echo(" ファイルが既に存在します。");
        return;
    }

    // 元ファイルを開く.

    var clip = Application.CreateSoundClip(input_file);

    // 圧縮フォーマット選択.
    // サンプリングレートはユーザーの指定, 元ファイルのそれ, 44 kHz の順で使えるものを選ぶ.
    // チャンネル数は元ファイルのそれ, 2 ch の順で使えるものを選ぶ.

    var fmt = null;
    if (Opt.CodecHint.indexOf("lossless") >= 0) {
        if (Opt.SampleRate)
            fmt = select_lossless_format(codec_info, Opt.SampleRate, clip.Channels, clip.BitsPerSample);

        if ((fmt == null) && (Opt.SampleRate != clip.SampleRate))
            fmt = select_lossless_format(codec_info, clip.SampleRate, clip.Channels, clip.BitsPerSample);

        if ((fmt == null) && (Opt.SampleRate != 44100) && (clip.SampleRate != 44100))
            fmt = select_lossless_format(codec_info, 44100, clip.Channels, 16);
    }
    else {  // lossy codec
        if (Opt.SampleRate)
            fmt = select_lossy_format(codec_info, Opt.SampleRate, clip.Channels, clip.BitsPerSample);

        if ((fmt == null) && (Opt.SampleRate != clip.SampleRate))
            fmt = select_lossy_format(codec_info, clip.SampleRate, clip.Channels, clip.BitsPerSample);

        if ((fmt == null) && (Opt.SampleRate != 44100) && (clip.SampleRate != 44100))
            fmt = select_lossy_format(codec_info, 44100, clip.Channels, clip.BitsPerSample);
    }

    if (fmt == null) {
        echo(" 圧縮フォーマットが見つかりません。");
        echo("(途中終了)");
        exit(@EXIT_FAILURE);
    }

    echo(" 圧縮フォーマット: " + fmt.Desc);

    // 実行
    var time;       // in milli seconds

    writer.CodecFormat = fmt;
    time = Application.System.GetTickCount();
    writer.Process(clip, output_file);
    time = Application.System.GetTickCount() - time;

    // かかった時間を表示
    var sec, min;

    sec = Math.round(time / 1000);
    min = Math.floor(sec / 60);     // 端数切捨て
    sec -= min * 60;
    echo(" 経過時間: " + min + " 分 " + sec + " 秒");

    // ファイルサイズの比率を表示

    var fi = Fso.GetFile(get_link_target(input_file));
    var fo = Fso.GetFile(output_file);
    echo(" ファイル サイズ比: " + Math.round(fo.Size / fi.Size * 100) + "%");
}

// フォルダ folder_path (文字列) 内を調べ, 拡張子が InputExtRegExp にマッチする
// ファイルがあれば圧縮ファイルを作る.

function process_folder(writer, codec_info, folder_path)
{
    var folder = Fso.GetFolder(folder_path);
    var fc, input_file, input_ext;

    for (fc = new Enumerator(folder.Files); !fc.atEnd(); fc.moveNext()) {
        input_file = fc.item().Path;
        input_ext = Fso.GetExtensionName(get_link_target(input_file));
        if (InputExtRegExp.test(input_ext))
            process_file(writer, codec_info, input_file);
    }
}

// 開かれているファイル群のうち引数で渡されたものを圧縮する.

function process_doc(writer, codec_info, output_file, doc)
{
    echo(output_file);

    // ファイルが存在して作成時刻も元ファイルより新しければ, そのままにする.
    // ただし /force オプション指定時は上書きする.

    if (!Opt.ForceOverwrite)
    if (Fso.FileExists(output_file)) {
        echo(" ファイルが既に存在します。");
        return;
    }

    // 元ファイルを開く.

    doc.SelectionFrom = 0;
    doc.SelectionTo = doc.Length;

    var clip = doc.CreateSoundClip();

    // 圧縮フォーマット選択.
    // サンプリングレートはユーザーの指定, 元ファイルのそれ, 44 kHz の順で使えるものを選ぶ.
    // チャンネル数は元ファイルのそれ, 2 ch の順で使えるものを選ぶ.

    var fmt = null;
    if (Opt.CodecHint.indexOf("lossless") >= 0) {
        if (Opt.SampleRate)
            fmt = select_lossless_format(codec_info, Opt.SampleRate, clip.Channels, clip.BitsPerSample);

        if ((fmt == null) && (Opt.SampleRate != clip.SampleRate))
            fmt = select_lossless_format(codec_info, clip.SampleRate, clip.Channels, clip.BitsPerSample);

        if ((fmt == null) && (Opt.SampleRate != 44100) && (clip.SampleRate != 44100))
            fmt = select_lossless_format(codec_info, 44100, clip.Channels, 16);
    }
    else {  // lossy codec
        if (Opt.SampleRate)
            fmt = select_lossy_format(codec_info, Opt.SampleRate, clip.Channels, clip.BitsPerSample);

        if ((fmt == null) && (Opt.SampleRate != clip.SampleRate))
            fmt = select_lossy_format(codec_info, clip.SampleRate, clip.Channels, clip.BitsPerSample);

        if ((fmt == null) && (Opt.SampleRate != 44100) && (clip.SampleRate != 44100))
            fmt = select_lossy_format(codec_info, 44100, clip.Channels, clip.BitsPerSample);
    }

    if (fmt == null) {
        echo(" 圧縮フォーマットが見つかりません。");
        echo("(途中終了)");
        exit(@EXIT_FAILURE);
    }

    echo(" 圧縮フォーマット: " + fmt.Desc);

    // 実行
    var time;       // in milli seconds

    writer.CodecFormat = fmt;
    time = Application.System.GetTickCount();
    writer.Process(clip, output_file);
    time = Application.System.GetTickCount() - time;

    // かかった時間を表示
    var sec, min;

    sec = Math.round(time / 1000);
    min = Math.floor(sec / 60);     // 端数切捨て
    sec -= min * 60;
    echo(" 経過時間: " + min + " 分 " + sec + " 秒");

    // ファイルサイズの比率を表示

    var fi_size = (clip.Length * clip.SampleRate) * (clip.BitsPerSample / 8) * clip.Channels;
    var fo = Fso.GetFile(output_file);
    echo(" ファイル サイズ比: " + Math.round(fo.Size / fi_size * 100) + "%");
}

// 開かれているファイル群を圧縮する.

function process_documents(writer, codec_info, output_dir)
{
    var names = new Array();
    var i, j, s, t, n;

    // 出力ファイル名

    for (i = 0; i < Application.Documents.Count; ++i)
        names[i] = Fso.GetBaseName(Application.Documents(i).Title);

    // 出力ファイル名の重複回避

    for (i in names) {
        s = names[i];
        for (j = 0; j < i; ++j) {
            if (s.toUpperCase() == names[j].toUpperCase())
                break;
        }
        if (j < i) {
            for (n = 2; ; ++n) {
                t = s + " (" + n + ")";
                for (j = 0; j < names.length; ++j) {
                    if (t.toUpperCase() == names[j].toUpperCase())
                        break;
                }
                if (j == names.length)
                    break;
            }
            names[i] = t;
        }
    }

    // 圧縮ファイル作成

    for (i in names) {
        s = output_dir + "\\" + names[i] + "." + OutputExt;
        process_doc(writer, codec_info, s, Application.Documents(i));
    }
}

function main()
{
    // コマンドライン読み取り.
    read_options();

    var writer = Application.CreateFileWriter("wma");
    var codec_info = select_codec(writer);

    echo("コーデック: " + codec_info.Name);
    echo();

    // 処理フォルダ/ファイルの指定がなかったら圧縮フォーマットを列挙表示して終了.
    if (Opt.InputFiles.length == 0) {
        print_formats(codec_info);
        exit(@EXIT_FAILURE);
    }

    // 電源コントロール
    if (Opt.Suspend)
        Application.System.SetSuspendState(false, true, false);     // hibernate, force, immediate

    // 圧縮ファイル作成
    var i, str;

    if (Opt.ExportMode) {
        if (Opt.InputFiles.length != 1) {
            echo(" 出力先フォルダを一つだけ指定してください。");
            exit(@EXIT_FAILURE);
        }
        str = Opt.InputFiles[0];
        if ((str.length > 0) && (str.charAt(str.length - 1) == "\\"))
            str = str.substr(0, str.length - 1);
        if (Fso.FileExists(str)) {
            echo(str);
            echo(" パスが無効です。");
            exit(@EXIT_FAILURE);
        }
        process_documents(writer, codec_info, str);
    }
    else {
        for (i in Opt.InputFiles) {
            str = Opt.InputFiles[i];
            if (Fso.FolderExists(str))
                process_folder(writer, codec_info, str);
            else if (Fso.FileExists(str))
                process_file(writer, codec_info, str);
            else {
                echo(str);
                echo(" パスが無効です。");
                echo("(途中終了)");
                exit(@EXIT_FAILURE);
            }
        }
    }

    exit(@EXIT_SUCCESS);
}