﻿人柱版10.69からの改変説明

■概要■
HTTPサーバ機能は後述「CivetWebの組み込みについて」を参照してください。デフォルト
ではlocalhost以外からのアクセスを拒否するので注意してください。
Unicode対応のため予約情報(Reserve.txtなど)の既定の文字コードを変更しました。後述
「予約ファイル等のByteOrderMark付きUTF-8対応について」を参照してください。
Windowsサービスとして使用しない場合、EpgTimer.exeやEpgTimerTask.exeは必須ではあ
りません。EpgTimerSrv.exeがタスクトレイアイコンを表示し、スリープ確認ダイアログ
やチューナー・バッチを直接起動します。
EpgTimerTaskはEpgTimerSrvに統合しました。EpgTimerSrv.exeをコピーしてリネームする
か、EpgTimerSrv.exeに/taskオプションをつけるとEpgTimerTaskとして動作します。

Readme.txt、Readme_EpgDataCap_Bon.txt、Readme_EpgTimer.txtは基本的に人柱版10.69
のままです。更新履歴はHistory.txtに移されていますが、すでに内容を更新していませ
ん。正確な履歴はコミットログを参照してください。ファイルプロパティ等のバージョン
情報は(独断で)10.70としていますが、おおむね2020年以降に更新したこのフォークやそ
れに近いフォークに付けた大まかな値です。
このファイルでは上述のReadmeから改変された部分だけを説明します(他者の改変部分も
原則能動態で説明します)。なお、仕様に影響しないバグ修正や細かいデザイン改変は省
略します。追加機能については【追加】マークを付けています。

■Readme.txt■
◇動作環境
  必要なランタイムはビルド環境次第になります。
  OSは、おそらくXP SP3以降なら動くでしょう(XPはすでに終了しているので未確認)。
◇twitter.dllの取り扱いについて
  twitter.dllは削除しました。無視してください。
◇基本的な使用準備
  手順に大きな変更はないですが、チューナー数の設定はEpgTimerSrv.exeの起動だけで
  もできます。タスクトレイに表示される時計アイコンを右クリック→「システム→Srv
  設定」で設定してください。既定でEpgTimerSrvはEpgTimerに連動して終了しなくなっ
  たので、設定を反映させるには右クリック→「Srv終了」で直接EpgTimerSrvを終了して
  ください。
  Program Files等のOSが特別に管理するフォルダへの配置は避けてください。書き込み
  保護や仮想化、大規模アップデート時の退避処理によりトラブルを引き起こします。

■Readme_EpgDataCap_Bon.txt■
◇設定
  ●基本設定タブ
    Readmeで触れられていませんが、相対パスは使用不可です(仕様変更ではない)。安定
    性に問題が出るので絶対パスを使用してください。
    ドライブのルート("D:\"など)を指定する場合は「\」を削除しない("D:"にしない)で
    ください。原作挙動では自動的に削除されてしまいますが、一般に不適切です。
  ●動作設定タブ
    ・ドロップ/スクランブル数が～以上でドロップログを出力する【追加】
      EpgTimerSrv設定の「ドロップログを出力する」を有効にしている場合でも、この
      条件を満たしたときだけ.errファイルを出力します。
    ・スクランブル値の変化をドロップログに記録しない【追加】
      スクランブル値の変化した時刻を.errファイルに記録しないようにします。
    ・ドロップログをUTF-8で出力する【追加】
       .errファイルをShift_JISではなくUTF-8で出力します。
    ・デバッグ出力をファイルに保存する【追加】
      デバッグ出力(OutputDebugStringW)をEpgDataCap_Bon.exeの起動数に応じて
      EpgDataCap_Bon_DebugLog-{番号}.txtに保存します。
    ・BonDriverについてのデバッグ出力を増やす【追加】
      BonDriverの呼び出しが10秒以上ブロックされた場合などにデバッグ出力します。
      ストリームの統計も1分ごとに出力するようになりますが、これを抑制したいとき
      は、EpgDataCap_Bon.iniのTraceBonDriverLevelを2→1に変更してください。
    ・TS入力/ファイル出力バッファ上限【追加】
      EpgDataCap_Bon.iniのTsBuffMaxCount/WriteBuffMaxCountを設定します。値の意味
      は「回数」ではなく「48128バイトのn倍」になりました。
    ・録画ファイル名【追加】
      即時録画のファイル名を指定します。開始日時($DYYYY$～$TH28$)とサービス名
      ($ServiceName$)を使用できます。
  ●EPG取得設定タブ
    ・基本情報のみ取得するネットワーク(視聴・録画中)【追加】
      動作設定タブにある、視聴中、録画中のEPGデータ取得について、基本情報のみ取
      得するかどうか設定します。
    ・TS解析後に取得する【追加】
      EPGデータの取得・解析を行うタイミングを遅らせます。
  ●ネットワーク設定タブ
    IPv6アドレスも使用できます【追加】。
    ・TCP送信
      ポート番号が22000～22999の範囲では送信形式がplain(BonDriver_TCP.dllのため
      の特別なヘッダをつけない)になります【追加】。
      送信先を0.0.0.1(SrvPipe)にすると、TCPではなく名前付きパイプ
      "\\.\pipe\SendTSTCP_{ポート番号}_{プロセスID}"として外部ツールなどの接続を
      待ち受けます【追加】。
      送信先を0.0.0.2(Pipe)にすると、BonDriver_Pipe.dllに接続します【追加】。ポ
      ート番号はPipe番号になります。

◇例外発生時のスタックトレース出力について【追加】
EpgDataCap_Bon.exeがなんらかの不具合で異常終了するとき、スタックトレースを
EpgDataCap_Bon.exe.errというテキストファイルに出力します。また、ビルド時に生成さ
れるEpgDataCap_Bon.pdbが同じフォルダにあれば出力内容が詳細になります。
EpgTimerSrv.exeも同様です。

■Readme_EpgTimer.txt■
"EpgTimer.exe"を"EpgTimerNW～.exe"にファイル名をリネームすることで、EpgTimerNW相
当の動作になります。～には任意の文字列を指定可能で、この文字列が異なるEpgTimerNW
は多重起動できます。通常のEpgTimerも"EpgTimer～.exe"にリネームして多重起動できま
すが、必ずEpgTimerSrvのあるフォルダに置いてください。また、リネームしたEpgTimer
はEpgTimerSrvを連動して起動しません。
録画済み番組情報"RecInfo2Data.bin"は"RecInfo2.txt"に移動しました。
OSのタイムゾーンの影響を受けなくなりました。予約管理や画面表示等すべて日本標準時
(UTC+9時間)で行います。

◇主な機能
  サーバー連携機能は廃止しました。

◇使い方
  ◇各タブ
    タブの移動はショートカットキー(Ctrl+1～5)でも可能です。ちなみに(Ctrl+F)は検
    索ダイアログを開きます。【追加】
    ●自動予約登録
      ・予約ごと削除ボタン【追加】
        選択されている項目を、その検索条件にマッチする予約ごと削除します。

◇検索条件
  ・検索キーワード
    OR検索( | 前後スペース必須)もできます【追加】。改行入力機能は廃止しました。
    検索対象をキーワード単位で指定できます(NOTキーワードも同様)【追加】。
      :title:タイトル(「番組名のみ検索対象にする」がオンのときのデフォルト)
      :event:タイトルと番組内容(同じくオフのときのデフォルト)
      :genre:ジャンル(EpgTimerの番組詳細タブの「ジャンル:」に続く文字列)
      :video:映像情報(「映像:」について同上)
      :audio:音声情報(「音声:」について同上)
    例えばタイトルか映像情報に"[字]"が含まれるものを検索する場合
      :title:[字] | :video:[字]
    とします。先頭の:を::にする(::title:など)と個別に正規表現モードになります。
  ・正規表現モード
    ConvertText.txtは廃止しました(後述)。全体を1つの正規表現とみなします。上述の
    OR検索や検索対象の指定はできません。
  ・あいまい検索モード
    検索アルゴリズムを編集距離に基づくものに変更しました。編集距離がキーワード長
    の25%以下(1-3文字=距離0(普通の検索)、4-7文字=距離1、8-11文字=距離2...)になる
    文字列が検索対象に含まれているかを、全てのキーワードについて調べます。
  ・サービス絞込み
    Readmeで触れられている通り、とくに自動予約登録では余分なサービスを省いてくだ
    さい。検索負荷はサービス数に概ね比例するので、例えば、サービスを全チェックし
    た自動予約が大量にあると予約管理に影響するほどの負荷になります。自動予約の検
    索にかかった時間はデバッグ出力の"Done PostLoad EpgData"で確認できます。数秒
    単位の時間がかかっているときは検索条件を工夫してください。
  ・大小文字区別【追加】
    検索キーワードやNOTキーワードの大文字小文字(Aとaなど)を区別して検索します。
  ・自動登録を無効にする【追加】
    自動予約登録条件に追加するとき、その条件を無効にする(予約されなくなる)かどう
    かです。通常の検索で使用する意味はありません。
  ・同一番組名の録画結果があれば無効で登録する
    ・全てのサービスで無効にする【追加】
      同一サービスかどうかのチェックを省略し、同一番組名のみで判断します。

◇録画設定
  ・有効【追加】
    以前の録画モード「無効」がチェックボックスとして分離しました。
  ・追従
    プログラム予約に切り替えるのと実質的な違いがほとんど無いため、「イベントリレ
    ー追従」に意味を変更しました。
  ・録画フォルダ
    録画フォルダを空欄のままにすると既定の録画保存フォルダになります【追加】。
    ファイル名PlugInにオプションの文字列を指定できます【追加】。オプションの意味
    はPlugIn次第ですが、RecName_Macro.dllではマクロを指定します。オプションを指
    定しなければ従来動作です。対応していないPlugInではオプションは無視されます。
  ・録画後実行bat
    「*」以降の文字列を$BatFileTag$マクロとして参照できます【追加】。「"」は全角
    「”」に置換されます。意図しない展開を防ぐため、バッチでは"$BatFileTag$"のよ
    うに引用符で囲うなどしてください。
  ・録画マージン
    完全な録画を重視する場合、開始マージンは十分に確保してください。予約管理やPC
    の状態によって数秒から十数秒程度の遅延は起こり得ます。デフォルトの開始マージ
    ン5秒は一般にやや不十分です。

◇設定
  EpgTimerSrv.exe(=予約管理を担当)についての設定画面を分離しました。「設定→動作
  設定→全般→EpgTimerSrv設定」から起動できます。以下は分離前のタブ構造での説明
  なので適宜読みかえてください。「動作設定→その他」は「動作設定→全般」に変更し
  ました。

  ●基本設定タブ
    ●保存フォルダ
      ・コマンドライン引数【追加】
        録画用アプリの引数をカスタマイズします。EpgDataCap_Bon.exeの場合は弄る必
        要ありません。
        ・最小化: EPG取得や「最小化で起動する」設定の録画時に付加される
        ・非視聴時: 視聴時以外や「視聴時はViewを起動する」にチェックしていないと
                    きに付加される
      ・録画情報保存フォルダ【追加】
        .program.txt/.errの保存先を指定します(Common.iniのRecInfoFolderに相当)。
    ●チューナー
      利用可能なチューナー数のうちEPG取得に使用するチューナー数を設定できるよう
      になりました。【追加】
    ●EPG取得設定タブ
      EPG取得時間に曜日と取得種別を指定できます【追加】。種別はすぐ上にある「基
      本情報のみ取得するネットワーク」のチェックボックスで指定してください。
  ●動作設定タブ
    ●録画動作
      ・bat実行条件
        廃止しました(バッチごとに指定)。
      ・対象データのデフォルト【追加】
        予約の「指定サービス対象データ」のデフォルト値です。以前はEpgDataCap_Bon
        の設定項目を共用していましたが、独立しました。
      ・録画ファイルの容量確保を行う【追加】
        Bitrate.iniをもとに録画容量を予想してファイルにあらかじめブランク領域を
        確保します(EpgTimerSrv.iniのKeepDiskに相当)。並列録画時の断片化を抑制で
        きる可能性が高いですが、このようなファイルを追っかけ再生できるソフトは比
        較的少ないです。
      ・番組情報を出力する
        ・UTF-8で出力する【追加】
          .program.txtをShift_JISではなくUTF-8で出力します。
    ●予約情報管理
      ・イベントリレーによる追従を行う
        廃止しました(予約ごとに指定)。
      ・EPGデータ読み込み時、予約時と番組名が変わっていれば番組名を変更する
        廃止しました(常にオン)。
      ・EPGデータ読み込み時、EventIDの変更を開始、終了時間のみで処理する
        廃止しました(常にオフ)。
      ・同一物理チャンネルで連続となるチューナーの使用を優先する
        廃止しました(常にオン)。
      ・優先度が同じ場合、チューナー強制指定された予約を先に割り当てする【追加】
        概ね、原作の「デフォルトアルゴリズム」と「アルゴリズム2」の違いと考えて
        ください(現アルゴリズムについては後述Q&A参照)。
      ・チューナーの起動に失敗したとき、ほかのチューナーで再試行する【追加】
        録画結果が「チューナーのオープンに失敗」となる場合、そのチューナーを除い
        て予約を再割り当てします(このとき使用チューナー強制指定は考慮しません)。
        再割り当てできるチューナーがない場合、録画結果は「チューナー不足のため失
        敗」になります。
      ・チューナー強制指定の値が異なるものを重複予約できるようにする【追加】
        EPG自動予約とイベントリレー追従について、同一イベントIDの番組は重複予約
        されませんが、チューナー強制指定が異なる予約に限ってこれを可能にします。
      ・EPG自動予約をプログラム化したとき、再び追加されないようにする【追加】
        次のEPG再読み込みで同じ番組が追加されないよう予約状況に注釈を入れます。
      ・予約を無効にするとき、録画モードを「指定サービス」にする【追加】
        無効時の録画モードは、録画モードを表す数値の拡張で実現しているため、外部
        ツールに不具合がでる場合は有効にしてください。
      ・録画情報保存フォルダ指定時は録画ファイルと同じ場所を参照しない【追加】
        録画済み一覧で使用される録画情報(.program.txt/.err)は、録画ファイルと同
        じ場所→録画情報保存フォルダの順に参照しますが、この挙動を変更します。
      ・録画済み一覧から削除するときに録画ファイルも削除する【追加】
        Common.iniのRecInfoDelFileに相当します。
        ・同時に削除するファイルの拡張子は削除設定に従う【追加】
          デフォルトは.program.txtと.errが対象ですが、これをカスタマイズします。
      ・ファイル名の禁則文字の変換対象から「\」を除外する【追加】
        PlugInが返すファイル名の禁則文字の変換対象から「\」を除外して、フォルダ
        階層を表現できるようにします(EpgTimerSrv.iniのNoChkYenに相当)。有効の場
        合でも、ひとつ前の文字が「.」の場合(「.\」)は変換対象です。
      ・録画中の予約削除を【追加】
        ・削除のみ(従来動作)
        ・録画済みに追加
          「録画終了」として録画済み一覧に追加します。録画後実行batが実行され、
          成功として扱われる点に注意してください。
        ・キャンセルとして録画済みに追加
          「録画中にキャンセルされた可能性があります」として追加します。
    ●ボタン表示
      ・タブの位置に表示【追加】
        上部表示ボタンを上部タブと並列に配置します。
    ●その他/全般
      ・ネットワーク接続を許可する
        ・IPv6【追加】
          IPv6サーバにします。IPv4IPv6デュアルスタックではありません。
          アクセス制御もIPv6で指定してください("+::1,+fe80::/64"など)。
        ・アクセス制御【追加】
          EpgTimerSrv.exeが接続を許可するクライアントのIPアドレスを
            {許可+|拒否-}{ネットワーク}/{ネットマスク},...
          の形式で複数指定します。指定は左から順にクライアントと比較され、最後に
          マッチした指定の+-で可否が決まります(HTTPサーバ機能と同じルール)。記述
          に誤りがあればすべて拒否します。拒否されたIPはデバッグ出力されます。
        ・無通信タイムアウト(秒)【追加】
          EpgTimerNWのネットワーク接続で「クライアント側に待ち受けポートを作る」
          をオフにしたときに使われるロングポーリングの再接続の間隔を指定します。
      ・EPG取得時に放送波時間でPC時計を同期する
        同期の信頼性を確保するため、150秒の放送波時間の観測を行います。EPG取得時
        間が延べ150(複数時は150÷チューナ数)秒に満たない場合は同期しません。
        特権については後述「EpgTimerAdminProxy.exeについて」も参照してください。
      ・EPG取得後も番組情報をX日前まで保存する【追加】
        EPG取得後は過去の番組情報が消えますが、これを"EpgArc.dat"というファイル
        に保存して、番組表などに利用できるようにします。∞を指定すると、さらに過
        去の番組情報をEpgArc2フォルダに一週間単位で保存し続けます。
      ・EpgTimerSrvを常駐させる【追加】
        ・タスクトレイアイコンを表示する【追加】
        ・開始準備で点滅させる【追加】
          「予約録画開始準備」(使用BonDriver通知を除く)の通知で点滅させます。
        ・バルーンチップ/トーストでの動作通知を抑制する【追加】
          ・リアルタイムで表示できなかった通知を捨てる【追加】
            過去の通知が遅れてバルーンチップ/トースト表示されないようにします。
          ※これらはEpgTimerSrv.exeが直接表示するものについての設定です
      ・情報通知ログをファイルに保存する【追加】
        情報通知ログをEpgTimerSrvのあるフォルダのEpgTimerSrvNotify.logに保存しま
        す。情報通知ログのウィンドウの表示にも利用されます。
      ・デバッグ出力をファイルに保存する【追加】
        EpgTimerSrvのデバッグ出力(OutputDebugStringW)をEpgTimerSrvのあるフォルダ
        のEpgTimerSrvDebugLog.txtに保存します。
      ・EpgTimerSrvの応答をtkntrec版互換にする【追加】
        ※変更はEpgTimerSrv再起動後に適用されます。
        EpgTimerに対するEpgTimerSrvのふるまいをtkntrec版のEpgTimerSrvと同じにし
        ます。つまりtkntrec版のEpgTimerも利用できるようにします。以前のEpgTimer
        や一部の外部ツールとの通信は非互換になる点に注意してください。
      ・TSファイルの拡張子【追加】
        デフォルトの録画ファイル名や、設定項目で「TSファイル」と表現している機能
        の判定に使用する拡張子を指定します。5文字以下の英数字のみ使用できます。
      ・サーバー間連携
        廃止しました。
      ・タスクトレイアイコンを表示する【追加】
        タスクトレイアイコンの表示・非表示を切り替えます。
      ・EPG取得対象サービスのみ表示する【追加】
        一覧や番組表に表示するサービスをEpgTimerSrv設定の「EPG取得対象サービス」
        でチェックされたサービスに限定します。
      ・リストボックスなどのサービス一覧をIDでソートする【追加】
        オフにするとChSet5.txtの記述順で表示します。
      ・引数つきで起動したとき(iEPG予約追加など)はすぐに終了する【追加】
        ・EpgTimer.exeにiEPGファイルを与えて起動したときの動作を変更します。
      ・テーマを適用する(要再起動)【追加】
        EpgTimer.exe.xmlの<NoStyle>に相当します。オフでOSデフォルト、オンでVista
        テーマになります。オンのときEpgTimerのあるフォルダにEpgTimer.exe.rd.xaml
        があれば、そこに定義されたリソースを適用します【追加】。iniフォルダに簡
        単なサンプルを用意したので参考にしてください。
      ・右クリックメニューにテーマを適用する(要再起動)【追加】
        上述機能の右クリックメニュー限定版です。リソースを定義したいときは同様に
        EpgTimer.exe.rdcm.xamlを作成してください。
      ・EPGデータを常に更新する(旧「EPGデータを自動的に読み込まない」相当)
        EpgTimerSrvのEPGデータをEpgTimerにダウンロードするのを、番組表を表示する
        タイミング(≒番組表タブを開くタイミング)まで遅らせるかどうか指定します。
    ●Windowsサービス
      このタブは廃止しました。サービス登録、解除はiniフォルダにある以下のバッチ
      ファイルを管理者権限で起動してください。予めEpgTimerは閉じてください。
        ・EpgTimerSrv_Install.bat : サービス登録と開始
        ・EpgTimerSrv_Remove.bat : サービス停止と解除
      EpgTimerを管理者権限で起動する必要はありません(むしろ避けてください)。
  ●番組表タブ
    ●基本
      ・フォント
        ,(コンマ)で区切ってフォールバックフォント(主フォントに文字がないときに使
        われるフォント)を1つだけ指定できます【追加】。
      ・表示
        ・最低表示行数【追加】
          短時間の番組でも最低この行数(小数点使用可)だけ高さを確保します。
        ・タイトル(以外)の文字列置換リスト【追加】
          置換前文字列が長いものを優先します。例えば全角記号を半角にしたいときは
          /！/!/？/? のように指定します。特定文字列の絵文字化などにも使えます。
    ●表示項目
      ・カスタマイズ表示
        表示条件→表示サービスで、同一TSのサービス(全サービス録画でまとめて録画
        されるもの)を逆順に並べると、これらを結合表示できます【追加】。例：
          アフリカ中央テレビ3
          アフリカ中央テレビ2
          アフリカ中央テレビ1、の順に追加すると番組表上は1サービス分の幅で表示
  ●外部アプリケーション
    ●TVTest連携
      ・Pipe方式【追加】
        起動したTVTestがBonDriver_TCP.dllではなくBonDriver_NetworkPipe.dll(
        BonDriver_Pipe.dllを単にリネームしたもの)を選択するようにします。ローカ
        ル通信専用ですが、BonDriver_TCP.dllなどと同様に扱えます。
    ●ファイル再生
      ・追っかけ再生にも使用する【追加】
        追っかけ再生にも(NetworkTVモードではなく)このアプリを使います。
    ●Twitter設定タブ
      廃止しました。

◇録画ファイル自動削除の仕様
  削除対象は前述「TSファイルの拡張子」をもつファイルです。作成日時ではなく更新日
  時でソートし、開始2時間前までの予約に必要と予想される容量を確保します。

◇スタンバイ、休止状態への移行
  次の予約録画またはEPG取得に対して、動作設定で指定した"復帰処理開始時間"+8分、
  かつ抑制条件で指定した時間以上の開きがある場合に移行します。

◇録画後のバッチファイル実行の仕様
  バッチのプロセス優先度は"通常以下"(BELOW_NORMAL_PRIORITY_CLASS)で実行します。
  PowerShellスクリプト(.ps1)またはLuaスクリプト(.lua)(lua52.dllがある場合)も使用
  できます【追加】。LuaスクリプトはEpgTimerSrv内部のスレッドで実行します。短時間
  の処理への使用を想定していますが、処理時間が長くなる場合は後述「Lua edcbグロー
  バル変数の仕様」のSleepの説明を確認してください。
  以下の拡張命令を利用できます【追加】。拡張命令はバッチファイル内のどこかに直接
  記述してください(remコメント等どんな形式でもOK)。
  _EDCBX_BATMARGIN_={bat実行条件(分)}
    このマージン以上録画予定がないときに実行開始します。デフォルトは0です。
  _EDCBX_HIDE_
    ウィンドウを非表示にします。Luaスクリプトでは無意味(ウィンドウがない)です。
  _EDCBX_NORMAL_
    ウィンドウを最小化しません。
  _EDCBX_DIRECT_
    マクロを置換ではなく環境変数で渡して直接実行します。マクロを囲う$が%になるだ
    けですが、start /waitを使って別のスクリプトに処理を引き継ぐときに便利です。
    マクロにUnicode文字を含む場合にも対応できます。
    また、以下を保証します:
      ・EpgTimerSrv.exeのあるフォルダに"EpgTimer_Bon_RecEnd.bat"を作らない
      ・EpgTimer.exeを経由する間接実行はしない
      ・カレントディレクトリはバッチのあるフォルダになる(Luaスクリプトを除く)
    PowerShellとLuaスクリプトでは常に有効です。
  _EDCBX_FORMATTIME_
    日時についてのマクロ($SDYY$など)をISO8601形式の$StartTime$と$DurationSecond$
    に単純化します。
    PowerShellとLuaスクリプトでは常に有効です(簡単に整形できるため)。
  取得できるマクロについては以下の3行のコマンドで確認すると手っ取り早いです。
    >rem _EDCBX_DIRECT_
    >set
    >pause
  (PowerShellの場合)
    >ls env:
    >Read-Host "Press enter to continue"
  (Luaの場合。マクロはenvグローバル変数に格納)
    >for k,v in pairs(env) do s=(s or '')..k..', ' end
    >edcb.os.execute('start echo '..s)

◇マクロ
  RecName_Macro.dllについて、マクロを追加しました。【追加】
    $BonDriverName$ BonDriverの名前
    $BonDriverID$   BonDriverのID(優先度)
    $TunerID$       BonDriverごとのチューナID
    $ReserveID$     予約ID
    $FreeCAFlag$    ノンスクランブルフラグ。不明のとき-1
    $ExtEventInfo$  詳細情報。未取得(空)の場合あり。OSのファイル名の制約を容易に
                    超えるので多くとも200文字程度で足切りすべき
  RecName_Macro.dllに限り、以下の関数機能を利用できます。【追加】
  ※関数部に$,&,(を含めるときは数値文字参照(&文字コード;)を使う
  ・【文字置換】Tr/置換文字リスト/置換後/
    ・例：番組名のA→a、$→B: $Tr/A&36;/aB/(Title)$
    ・サロゲートペアで表現される文字には使えない
  ・【半角⇔全角】HtoZ,ZtoH
  ・【英数半角⇔全角】HtoZ<alnum>,ZtoH<alnum>
  ・【Shift_JISにない文字を?に置換】ToSJIS
  ・【文字列置換】S/置換文字列/置換後/.../
  ・【文字削除】Rm/削除文字リスト/
    ・例：番組内容から/を削除: $Rm!/!(SubTitle)$
    ・サロゲートペアで表現される文字には使えない
  ・【足切り】Head文字数[省略記号]
    ・例：番組内容を半角にして最長15文字に: $Head15(ZtoH(SubTitle))$
    ・例：番組名を省略記号つき最長15文字に: $Head15~(Title)$
  ・【頭切り】Tail文字数[省略記号]
    ・例：予約IDを4桁に: $Tail4((0000$ReserveID$))$
  ※2重括弧にするとその中身を展開した結果が関数に渡される。入れ子にする場合は、
    閉じ括弧))$の数が内側より多くなるように何もしない関数(Rm..など)で調整する。
    例: $Rm..(Head60~(($Head30~(($Title$ $SubTitle$))$ $ExtEventInfo$)))$

◇追従の仕様(この項上書き)
  ※あいまい検索処理によるEventID変更に対する追従処理は一切行いません。
  追従処理には大きく2種類あります。
  1.EPGデータ読み込み時に、読み込んだEPGデータから追従を行う
    ※このEPGデータはEpgTimerの番組表と同じものです
    プログラム予約、および2.で一度でも変更された予約は対象外です。追従するパラメ
    ータは開始時間、終了時間、およびイベント名です。

  2.起動中のEpgDataCap_Bon.exeの蓄積しているEPGデータから追従を行う
    起動中のチャンネルと異なるチャンネルの予約、6時間以上先の予約、プログラム予
    約、および無効予約は対象外です。追従するパラメータは開始時間、終了時間、イベ
    ント名、およびイベントリレーです。イベントリレーは現在番組(present)の情報の
    みを利用して行います。
    現在番組の終了時間が未定になった場合：
      現在番組の予約が録画終了(マージン含む)まで5分を切るタイミングで、5分ずつ予
      約を延長していきます。終了時間未定のまま番組が終わった場合、番組終了から5
      ～10分後に録画が終わることになります。終了時間が再度決定すれば、決定時間に
      変更します。

    次番組(following)の終了時間が未定になった場合：
      次番組の予約が録画終了(マージン含む)まで5分を切るタイミングで、5分ずつ予約
      を延長していきます。終了時間未定のまま次番組が切り替わった場合、後述の「現
      在でも次でもない番組」として扱います。終了時間が再度決定すれば、決定時間に
      変更します。
      また、このとき現在でも次でもない番組についても時間が定まらないので、これら
      の予約が録画終了(マージン含む)まで5分を切るタイミングで、録画総時間が
      TuijyuHourに達するまで、5分ずつ予約を延長していきます。次番組の終了時間が
      再度決定すれば、延長を停止します。この番組が現在または次番組になれば、その
      時間に変更します。
      一部の放送局で、未定解消直後の番組のイベントIDが変更されることがあります。
      これに対処するため、現在または次番組に未定追従中の予約とイベント名が完全一
      致するイベントが存在する場合に限り、その予約を番組終了時間まで延長します。

◇Twitter機能
  Twitter機能は廃止しました。代わりにEpgTimerSrv.exeのあるフォルダに置かれた以下
  のバッチファイルを実行します【追加】。取得できるマクロは従来とだいたい同じです
  (NEW系マクロは名前からNEWを取り除いています)。$BatFileTag$、およびPostRecEndに
  限り$RecInfoID$、これ以外に限り$ReserveID$(予約ID)、$RecMode$(録画モード0=全サ
  ービス～4=視聴)、$ReserveComment$(コメント)も取得できます。
  ・PostAddReserve(.bat|.ps1|.lua) : 予約を追加したとき(無効を除く)
    ・EPG自動予約のとき$ReserveComment$は"EPG自動予約"という文字列で始まります
  ・PostChgReserve(.bat|.ps1|.lua) : 予約を変更したとき(無効を除く)
    ・$SYMDHMNEW$～$SEYMDHM28NEW$は取得できません
  ・PostRecStart(.bat|.ps1|.lua) : 録画を開始したとき
  ・PostRecEnd(.bat|.ps1|.lua) : 録画を終了したとき
    ・取得できるマクロは録画後バッチと完全に同じです
  また、イベント発生時に以下のバッチファイルを実行します。
  ・PostNotify(.bat|.ps1|.lua) : 更新通知が送られたとき
    ・取得できるマクロは$NotifyID$のみです
      ・$NotifyID$(1=EPGデータ更新, 2=予約情報更新, 3=録画結果情報更新)
    ・ほかのバッチと異なり、実行中でもスリープなどの録画後動作を抑制しません
    ・以下の拡張命令により、$NotifyID$を0とした通知を定期的に送ることができます
      _EDCBX_NOTIFY_INTERVAL_={通知間隔(秒)}
  バッチ仕様は録画後バッチと同じですが、_EDCBX_BATMARGIN_は無効です。また、
  _EDCBX_DIRECT_でないときの一時ファイル名は"EpgTimer_Bon_Post.bat"です。
  実行は直列に行います(互いに並列実行しない)。また、実行時点で各々の動作が完了し
  ている(予約ファイル等更新済みである)ことを保証します。
  iniフォルダに簡単な参考用バッチファイルを用意しました。

◇Q&A
  ・予約割り振りの仕方を詳しく
    全予約を開始時間でソートし、予約のない時間帯ごとに組分け、組ごとの予約に対し
    "予約優先度>開始時間(終ろ優先時逆順)>予約ID"の一意な優先度をつけ、優先度順に
    予約をチューナに割り当てます。ここで、優先度をもとに実際の録画時間を計算し、
    最長となるチューナを選びます(同じ長さならBonDriverの優先度順)。録画時間を減
    らすことなく割り当てられるチューナが複数ある場合、より近くに同一チャンネルの
    予約があるチューナを選びます。チューナ強制指定時はここで選べるチューナが限定
    されることになります。録画時間が0分ならチューナ不足となります。
  ・開始と終了時間重なっているときの動作を詳しく
    別チャンネルの場合、前番組の優先度が高いときはそれが終わるまで後番組の録画は
    始まりません。後番組の優先度が高いときは、その開始20秒前に前番組の録画を終了
    します。マージンを含めて録画時間です。マージン無視等の仕様は廃止しました。
  ・プログラム的に登録する方法は？
    Reserve.txtにID 0で項目を直接追加する機能は廃止しました。
  ・EPG取得でBS、CS1、CS2の基本情報のみ取得て何？
    CS3(NetworkID=10)を追加しました【追加】。
  ・追従できるだけ失敗しないための対策とかある？
    仕様変更ではなく注意ですが、デジタル放送の仕様上、午前0時を跨いでのEPG取得は
    避けるのが無難です。

◇キーワード検索の置き換え文字を変更する
  ConvertText.txtは廃止しました。常に以下のテーブルを使います。
  <置換前>
  ０１２３４５６７８９
  ＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ
  ａｂｃｄｅｆｇｈｉｊｋｌｍｎｏｐｑｒｓｔｕｖｗｘｙｚ
  ’”{全角空白}！＃＄％＆（）＊＋，－．／：；＜＝＞？＠［］＾＿｀｛｜｝～｡｢｣､･
  ｦｧｨｩｪｫｬｭｮｯｰｱｲｳｴｵ
  ｶﾞ ｷﾞ ｸﾞ ｹﾞ ｺﾞ ｶｷｸｹｺ
  ｻﾞ ｼﾞ ｽﾞ ｾﾞ ｿﾞ ｻｼｽｾｿ
  ﾀﾞ ﾁﾞ ﾂﾞ ﾃﾞ ﾄﾞ ﾀﾁﾂﾃﾄﾅﾆﾇﾈﾉ
  ﾊﾞ ﾋﾞ ﾌﾞ ﾍﾞ ﾎﾞ ﾊﾟ ﾋﾟ ﾌﾟ ﾍﾟ ﾎﾟ ﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖﾗﾘﾙﾚﾛﾜﾝﾞﾟ￥
  <置換後>
  0123456789
  ABCDEFGHIJKLMNOPQRSTUVWXYZ
  abcdefghijklmnopqrstuvwxyz
  '"{半角空白}!#$%&()*+,-./:;<=>?@[]^_`{|}~。「」、・
  ヲァィゥェォャュョッーアイウエオ
  ガギグゲゴカキクケコ
  ザジズゼゾサシスセソ
  ダヂヅデドタチツテトナニヌネノ
  バビブベボパピプペポハヒフヘホマミムメモヤユヨラリルレロワン゛゜\

◇追従動作のカスタマイズ
  ・NoEpgTuijyuMinは廃止しました(常に0)
  ・DuraChgMarginMinは廃止しました(常に0)
  ・TuijyuHourのデフォルトは3時間になりました

◇予約割り振りのアルゴリズムの変更
  廃止しました。

◇録画後実行batの実行条件の変更
  仕様変更ではありませんが補足します。"正常に予約録画の行えた物"とは、録画済み一
  覧の結果が「(録画)終了」または「開始時間が変更されました」または「次の予約開始
  のためにキャンセルされました」となっているものです。"エラーが発生した予約録画"
  とはそれ以外のすべての結果です。加えて、録画ファイル名が取得できなかったもの(
  視聴予約含む)、「後ろの予約を同一ファイルで出力」中のものは実行されません。

◇正常に録画を行えた番組情報の蓄積数を変更する
  RecInfo2Maxのデフォルトは1000です(仕様変更でなく誤表記)。

◇正常に録画を行えた番組情報として判断するためのドロップ数を変更する
  ドロップカウントの仕様変更により、RecInfo2DropChkのデフォルトは2になりました。

◇ブラウザから表示できるようにする / DLNAのDMSぽい機能を使う
  「CivetWebの組み込みについて」を参照。

◇録画後bat起動時の形式を変える
  非サービス時は<ExecBat>に従いません。上述の拡張命令でバッチごとに指定します。

◇情報通知ログを自動的にファイルに保存する
  廃止しました。EpgTimerSrv側の保存機能を利用してください。

◇同一番組無効登録で番組名の比較の際に無視する文字列を指定する【追加】
  [無]や[生]などのついた番組名も同一番組として扱いたい場合に利用することを想定し
  たもの。EpgTimerSrv.iniのSETにRecInfo2RegExpを追加することで指定可能です。文字
  列は正規表現として扱われ、動作としては、番組名から正規表現にマッチする部分を削
  除したものが同一番組名判定に使われます。
  例：RecInfo2RegExp=\[[再無生]\]
      （[再]と[無]と[生]に対応）

◇EpgDataCap3.dllの字幕属性情報への対応【追加】
  番組名に字幕記号([字])がなく番組情報に字幕属性があるときは、映像情報の文字列
  (:video:で検索できるもの)に[字]または[二字](外国語字幕)を追加します。反対に、
  番組名に字幕記号があり、番組情報に字幕属性がないときは、[字無]を追加します。

◇スリープ抑止拡張【追加】
  設定→動作設定→録画動作→抑止条件→[PCを使用中の場合]にチェックを入れ、録画終
  了時や番組表取得完了時にPCを使用(マウスやキーボード操作)していた場合、スリープ
  へのカウントダウンが表示されなくなります。また、その下にある[X分以内にユーザー
  操作があれば使用中とみなす]で使用中かどうかの判定時間を調整できます。ここを0に
  した場合は常に使用中であるとみなします(スリープしなくなります）。

◇バルーンチップを強制的に閉じるまでの時間(秒数)を指定する【追加】
  (Vista以降？の)バルーンチップはマウス操作等がなければ表示されつづけますが、
  EpgTimer.exe.xmlの<ForceHideBalloonTipSec>で表示タイムアウトを指定できます。

◇EpgTimerSrvのタスクトレイ左クリック動作を変更する【追加】
  デフォルトではEpgTimerSrv.exeと同じ場所のEpgTimer.exeを実行しますが、EpgTimer
  という名前のショートカットファイルがあればこちらを実行します。

◇Write_DefaultのTeeコマンド機能について【追加】
Write_Defaultの通常のファイル出力に平行して、出力と同じデータをPlugIn設定で指定
されたコマンドの標準入力に渡します。コマンドには$FilePath$(出力ファイルパス)を指
定できます。コマンドのカレントディレクトリは親プロセス(EpgDataCap_Bon.exeなど)の
あるフォルダになります。プロセス優先度は"通常以下"で実行します。
録画終了後、標準入力は速やかに閉じられます(入力完了まで待機することはない)。コマ
ンドの処理速度が録画速度を下回る場合は終了後の処理について(引数で受け取った出力
ファイルパスをもとに処理を継続するなど)コマンド側で工夫してください。
・Teeバッファサイズ(byte) : コマンドにデータを入力する単位
・Tee読み込み遅延(byte) : コマンドへの入力をファイル出力からこの値だけ遅らせる

◇予約ファイル等のByteOrderMark付きUTF-8対応について【追加】
予約ファイル等(EpgAutoAdd.txt, ManualAutoAdd.txt, RecInfo.txt, RecInfo2.txt,
Reserve.txt)をメモ帳などを使って「文字コード:UTF-8(BOM付き)」に変換すると、以後
この形式で読み書きします。変換はEpgTimerSrv.exeを終了させてから行ってください。
番組名などにUnicode文字を含めても文字化けせずに扱えるようになります。
※これらのファイルを直接参照する外部ツールは利用できない可能性が高くなります。
※ファイル新規作成時の既定をUTF-8にしました。外部ツールに問題が出る場合は上記の
手順で「文字コード:ANSI」に戻してください。

◇予約ファイル等の";;NextID="について【追加】
IDの再使用を防ぐため予約ファイル等の内容に";;NextID="コメントがつきます。必須な
ものではないので、外部ツールに問題が出る場合はEpgTimerSrv.exeを終了させてからメ
モ帳などで削除できます。Reserve.txtのソートは予約日時順に、RecInfo.txtの最終フィ
ールドは空欄に戻ります。

◇EpgTimerAdminProxy.exeについて【追加】
特権が必要な機能を使わない場合は不要です。無視してください。
「抑制条件->共有フォルダのTSファイルにアクセスがある場合」と「EPG取得時に放送波
時間でPC時計を同期する」ではEpgTimerSrv.exeにこれらを処理する特権が必要ですが、
サービスとして使用しない場合、たとえ管理者ユーザーでログオンしたとしても、普通は
UAC(ユーザーアカウント制御)によって特権が制限されます。一方、これらの処理のため
だけにEpgTimerSrv.exeを管理者権限で実行するのは安全ではありません(と思います)。
EpgTimerAdminProxy.exeを常駐させることで、EpgTimerSrv.exeに代わってこれらの処理
を行うことができます。
利用方法は、EpgTimerAdminProxy.exeを「ログオン時」に「最上位の特権で実行」するよ
うにタスクスケジューラ登録するのが手軽です(「タスクを停止するまでの時間」はオフ
に)。EpgTimerAdminProxy.exeは保護された場所("C:\Windows"など)に配置すべきです。

◇EpgTimer.exeをモジュールとして使う【追加】
EpgTimer.exeがEpgTimerSrv.exeとの通信に使用しているEpgTimer.CtrlCmdUtilというク
ラスの機能は、PowerShellを使って外部からも利用することができます。仕様は
EpgTimer/EpgTimer/CommonフォルダのソースコードCtrlCmd.csとCtrlCmdDef.csを参照し
てください。利用例がini/PostBatExamples/EdcbSchUploader.ps1にあります。

■CivetWebの組み込みについて■
HTTPサーバ機能の簡単化とディレクトリトラバーサル等々のバグ修正を目的に、EpgTimerSrv.exeにCivetWebを組み込みました。
有効にする場合はEpgTimerSrv.exeと同じ場所にlua52.dllが必要です。対応するものをDLしてください。
https://sourceforge.net/projects/luabinaries/files/5.2.4/Windows%20Libraries/Dynamic/
CivetWebについては本家のドキュメント↓を参照してください(英語) ※組み込みバージョンはv1.12
https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md
SSL/TLSを利用する場合はEpgTimerSrv.exeと同じ場所にlibssl-1_1(-x64).dllとlibcrypto-1_1(-x64).dllが必要です。自ビルドするか信頼できるどこかから入手してください。
https://www.openssl.org/community/binaries.html から辿った https://bintray.com/vszakats/generic/openssl (curlメンテナによるバイナリ)のopenssl-1.1.1*で動作を確認しています。
EpgTimerSrv.iniのSETセクションを編集し、EpgTimerSrv.exeを再起動してください。
以下のキー[=デフォルト]を利用できます:

EnableHttpSrv[=0]
  HTTPサーバ機能を有効にするかどうか
  # [=1]または[=2]で有効
  # [=2]にするとEpgTimerSrv.exeと同じ場所にログファイルも出力
HttpAccessControlList[=+127.0.0.1,+::1,+::ffff:127.0.0.1]
  アクセス制御
  # CivetWebのaccess_control_listに相当("deny all accesses"からスタート)
  # LANを越えないなら+127.0.0.1,+192.168.0.0/16
  # 従来通りすべてのアクセスを許可する場合は+0.0.0.0/0とする
  # ※+0.0.0.0/0は最終手段。キャリアの技術情報やプロキシを活用して接続元をできるだけ限定すべき
HttpPort[=5510]
  ポート番号
  # CivetWebのlistening_portsに相当
  # IPv4IPv6デュアルスタックは[=+5510]
  # SSL/TLSは[=5510s]
  # 複数ポート指定方法などは本家ドキュメント参照
HttpPublicFolder[=EpgTimerSrv.exeと同じ場所の"HttpPublic"]
  公開フォルダの場所
  # CivetWebのdocument_rootに相当
  # フォルダパスに日本語(マルチバイト)文字を含まないこと
  # ※EDCBを日本語を含むフォルダに入れている場合は要注意
HttpAuthenticationDomain[=mydomain.com]
  認証領域
  # CivetWebのauthentication_domainに相当
  # パスワード確認画面の文字列ぐらいの役割しかない
HttpNumThreads[=5]
  ワーカスレッド数
  # CivetWebのnum_threadsに相当
  # 最大50
HttpRequestTimeoutSec[=120]
  リクエストタイムアウト(秒)
  # CivetWebのrequest_timeout_msに相当
HttpSslCipherList[=HIGH:!aNULL:!MD5]
  使用するSSL/TLSの暗号スイートのリスト
  # CivetWebのssl_cipher_listに相当
HttpSslProtocolVersion[=4]
  受け入れるSSL/TLSプロトコルのバージョン
  # CivetWebのssl_protocol_versionに相当
  # 値が大きいほど安全。TLS1.0が必要なら2にする。ガラケーなどでSSL3.0が必要なら1にする
HttpKeepAlive[=0]
  Keep-Aliveを有効にするかどうか
  # CivetWebのenable_keep_aliveに相当
  # 有効にする[=1]ときは以下に注意:
  # ・mg.keep_alive(true)メソッドを呼んだLuaスクリプトは持続的接続になるかもしれない。
  #   このメソッドがtrueを返したときは"Content-Length"を必ず送り、"Connection: close"しない
EnableDMS[=0]
  「DLNAのDMSぽい機能」を有効にするかどうか
  # [=1]で有効
  # UDPポート1900を使用し、UPnPのM-SEARCH応答と約16分間隔のNOTIFY通知をおこなう
  # 有効になるのはDMSのUDP部分のみ。別途iniフォルダにあるdlna以下を公開フォルダに置く必要がある
  # カスタマイズは/dlna/dms/cds/soap.luaを参照
  # 通知する自機のUUIDは、公開フォルダの/dlna/dms/ddd.xmlから"<UDN>uuid:{UUID}</UDN>"を探してこれを使う
  # 通知する自機のHTTPポート番号は、HttpPortキーの最後にみつかった':'より後ろか、キーの先頭にある数字を使う
DmsIfTypes[=3]
  上述の応答と通知に使うネットワークインタフェース(NIC)のタイプ
  # +1=127.0.0.1、+2=192.168.0.0/16、+4=172.16.0.0/12、+8=10.0.0.0/8、+16=169.254.0.0/16、+32=その他
DmsInitialWaitSec[=20]
  上述の応答と通知の初期ディレイ(秒)

加えて、以下の設定をCivetWebのデフォルトから変更しています:
  ssi_pattern: "" (SSIは無効)
  extra_mime_types: "ContentTypeText.txt"に追加したMIMEタイプ(従来通り)
  lua_script_pattern: "**.lua$|**.html$|*/api/*$" (つまり.htmlファイルはLuaスクリプト扱いになる)
    ※REST APIとの互換のため、公開フォルダ直下のapiフォルダにあるファイルもLuaスクリプト扱いになる
  ssl_certificate: HttpPortに文字's'を含むとき、EpgTimerSrv.exeと同じ場所の"ssl_cert.pem"
  ssl_ca_file: EpgTimerSrv.exeと同じ場所の"ssl_peer.pem"
  ssl_default_verify_paths: "no" (既定の証明書フォルダを参照しない)
  ssl_verify_peer: "ssl_peer.pem"が存在するとき"yes"
  global_auth_file: EpgTimerSrv.exeと同じ場所の"glpasswd"
  access_control_allow_origin: "" (クロスドメイン通信は拒否)

公開フォルダ以下のフォルダやファイルが公開対象です(色々遊べる)。公開フォルダを用意しないと何もできません。
iniフォルダに原作っぽい動作をするLuaスクリプトを追加したので参考にしてください。

LANを越える場合は以下を参考に"ssl_peer.pem"または"glpasswd"を作成し(フォルダごとの.htpasswdは不確実)、SSL/TLSを利用してください。
不正アクセス耐性は"ssl_peer.pem"がおそらく最善(OpenSSLの信頼性とほぼ等価)です。
CivetWebでセキュリティが確保されているだろうと判断できたのは認証処理までの不正アクセス耐性のみです。
パス無しの公開サーバとしての利用はお勧めしません。

"ssl_cert.pem"(秘密鍵+自己署名証明書)の作成手順例
※鵜呑みにしないこと。bashの場合はtype→cat
※最近のブラウザはsubjectAltNameが必須のため、以下OpenSSLコマンドはバージョン1.1.1以降を使う
> openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.crt -x509 -days 3650 -sha256 -addext "subjectAltName = IP:127.0.0.1,IP:192.168.0.2,DNS:example.com"
  (subjectAltNameは一例。自機のIPやドメインを並べる)
  (入力項目はデフォルトでOK。ブラウザの証明書例外追加時に"server.crt"と拇印が等しいかだけ注意する)
> type server.crt >ssl_cert.pem
> type server.key >>ssl_cert.pem

"ssl_peer.pem"(信頼済みクライアント証明書リスト)の作成手順例
> openssl req -new -newkey rsa:2048 -nodes -keyout client.key -out client.crt -x509 -days 3650 -sha256
  (入力項目はデフォルトでOK)
> type client.crt >ssl_peer.pem
> openssl pkcs12 -export -inkey client.key -in client.crt -out edcb_key.p12 -name "edcb_key"
  (↑"edcb_key.p12"(クライアントの秘密鍵)はブラウザ等にインポートする)
  (パスワードを入力するために"winpty openssl～"とする必要があるかもしれない)

"glpasswd"(ダイジェスト認証ファイル)の簡単な作り方(ユーザ名root、認証領域mydomain.com、パスワードtest)
> set <nul /p "x=root:mydomain.com:test" | openssl md5
  (出力される32文字のハッシュ値でパスワードを上書き↓)
> set <nul /p "x=root:mydomain.com:351eee77bbb11db9fef4870b0d78b061" >glpasswd

Luaのmg.write()について、成否のブーリアンを返すよう拡張しています(本家に取り込まれました)。

■Lua edcbグローバル変数の仕様■
機能はEpgTimerSrv本体にあるメソッドとほぼ同じなので
C++を読める人はEpgTimerSrvMain.cppにある実装を眺めると良いかもしれない。

ANSI系のLua標準ライブラリを補完するため、以下の関数を用意している。
edcb.os.execute
edcb.os.remove
edcb.os.rename
edcb.io.open
edcb.io.popen
引数にUTF-8をそのまま渡せる以外の挙動はLua標準ライブラリと同じ。
ただし、edcb.io.*が返すファイルハンドルは簡略化のため行読み込みの機能(linesなど)を省略している。
また、execute/popenのプロンプト画面は出ないが、最後の引数にtrueを指定すれば表示できる。
例：edcb.os.execute('ping localhost',true)

[略語の定義]
B:ブーリアン
I:整数
S:文字列
TIME:標準ライブラリos.date('*t')が返すテーブルと同じ
<テーブル名>:後述で定義するテーブル
～のリスト:添え字1からNまでN個の～を添え字順に格納したテーブル

htmlEscape:I
  文字列返却値の実体参照変換を指示するフラグ(+1=amp,+2=lt,+4=gt,+8=quot,+16=apos)
  初期値は0。
  例えばedcb.htmlEscape=15とすると'<&"テスト>'は'&lt;&amp;&quot;テスト&gt;'のように変換される。
  edcb.os.*/edcb.io.*の挙動には影響しない。

serverRandom:S
  EpgTimerSrv.exeの起動毎に変化する256bitの暗号論的乱数
  バッチ実行時は存在しない

S GetGenreName( 大分類*256+中分類:I )
  STD-B10のジャンル指定の文字列を取得する
  中分類を0xFFとすると大分類の文字列が返る。
  0xFFFFとすると自動予約検索条件の'なし'が返る。
  存在しないとき空文字列。
  例えばedcb.GetGenreName(0x0205)は'グルメ・料理'が返る。
  大分類に0x60を加えるとTR-B14の「番組付属情報」の文字列が返る。
  大分類に0x70を加えるとTR-B15の「広帯域CSデジタル放送拡張用情報」の文字列が返る。
  (これらの特別扱いは自動予約検索条件でも同様)
  例えばedcb.GetGenreName(0x7205)は'ホラー／スリラー'が返る。

S GetComponentTypeName( コンポーネント内容*256+コンポーネント種別:I )
  STD-B10のコンポーネント記述子の文字列を取得する
  存在しないとき空文字列。
  例えばedcb.GetComponentTypeName(0x01B1)は'映像1080i(1125i)、アスペクト比4:3'が返る。

S|nil Convert( to文字コード:S, from文字コード:S, 変換対象:S )
  文字コード変換する
  利用できる文字コードは'utf-8'または'cp932'のみ。
  変換に失敗すると空文字列、利用できない文字コードを指定するとnilが返る。
  例：os.execute(edcb.Convert('cp932','utf-8','echo 表が怖い & pause'))

B Sleep( ミリ秒:I )
  スレッドの実行を中断する
  バッチ実行時：EpgTimerSrvが終了しようとしているとき、このメソッドはtrueで速やかに返る。
                バッチを長時間実行させたい場合は処理を分割し、このメソッドを利用して終了を妨げないようにする。
                例：
                repeat
                  {なにか短時間で終わる処理}
                until Sleep(0)

S GetPrivateProfile( セクション:S, キー:S, 既定値:S|I|B, ファイル名:S )
  Win32APIのGetPrivateProfileStringを呼ぶ
  既定値がBのときはtrue=1、false=0に変換される。
  ファイル名はEDCBフォルダ配下に置かれたiniファイルを相対指定する。
  例：v=0+edcb.GetPrivateProfile('SET','HttpPort',5510,'EpgTimerSrv.ini')
  ファイル名が'Setting\\'で始まるときは「設定関係保存フォルダ」にリダイレクトされる。
  特例的に以下の引数でEDCBフォルダのパスが返る。
  edcb.GetPrivateProfile('SET','ModulePath','','Common.ini')

B WritePrivateProfile( セクション:S, キー:S|nil, 値:S|I|B|nil, ファイル名:S )
  Win32APIのWritePrivateProfileStringを呼ぶ
  値がBのときはtrue=1、false=0に変換される。
  キーまたは値がnilのときの挙動はWritePrivateProfileStringと同じ。
  ファイル名についてはGetPrivateProfile()と同じ。
  書き込めたときtrueが返る。

B ReloadEpg()
  EPG再読み込みを開始する
  開始できたときtrueが返る。

ReloadSetting( ネットワーク設定を読み込むか:B )
  設定を再読み込みする
  引数をtrueにするとEpgTimerSrv.iniの以下のキーも読み込まれる(HTTPサーバは再起動する)。
  EnableTCPSrv, TCP*, EnableHttpSrv, Http*, EnableDMS

B EpgCapNow()
  EPG取得開始を要求する
  要求が受け入れられたときtrueが返る。

<チャンネル情報>のリスト GetChDataList()
  チャンネルスキャンで得たチャンネル情報のリストを取得する(onid>tsid>sidソート)
  つまり"Setting\ChSet5.txt"の内容。

<サービス情報>のリスト|nil GetServiceList()
  EPG取得で得た全サービス情報を取得する(onid>tsid>sidソート)
  プロセス起動直後はnil(失敗)。

{minTime:TIME, maxTime:TIME}|nil GetEventMinMaxTime( ネットワークID:I, TSID:I, サービスID:I )
{minTime:TIME, maxTime:TIME}|nil GetEventMinMaxTimeArchive( ネットワークID:I, TSID:I, サービスID:I )
  指定サービスの全イベントについて最小開始時間と最大開始時間を取得する
  開始時間未定でないイベントが1つもなければnil。
  *Archive()は過去イベントが対象。
  (SearchEpgArchiveが存在するバージョン以降)IDの上位16bitにマッチのORマスクを付加できる。

<イベント情報>のリスト EnumEventInfo( {onid:I|nil, tsid:I|nil, sid:I|nil}のリスト [, {startTime:TIME|nil, durationSecond:I|nil} ] )
<イベント情報>のリスト EnumEventInfoArchive( {onid:I|nil, tsid:I|nil, sid:I|nil}のリスト [, {startTime:TIME|nil, durationSecond:I|nil} ] )
  指定サービスの全イベント情報を取得する(onid>tsid>sid>eidソート、*Archive()は未ソート)
  リストのいずれかにマッチしたサービスについて取得する。
  ・onid,tsid,sidフィールドを各々nilとすると、各々すべてのIDにマッチする。
  ・(SearchEpgArchiveが存在するバージョン以降)IDの上位16bitにマッチのORマスクを付加できる。
  第2引数にイベントの開始時間の範囲を指定できる。
  ・開始時間がstartTime以上～startTime+durationSecond未満のイベントにマッチ
  ・空テーブルのときは開始時間未定のイベントにマッチ
  *Archive()は過去イベントが対象。※再利用の可能性があるため、過去イベントのeidをIDとして扱うべきでない。
                                  ※取得対象が月～年単位になりうるので、サービス指定も時間指定もない呼び出しは控えるべき。
  以前はnilが返る場合があったが(SearchEpgArchiveが存在するバージョン以降)常にリストが返る。

<イベント情報>のリスト SearchEpg( <自動予約検索条件> [, {startTime:TIME|nil, durationSecond:I|nil} ] )
<イベント情報>のリスト SearchEpgArchive( <自動予約検索条件> [, {startTime:TIME|nil, durationSecond:I|nil} ] )
<イベント情報>|nil SearchEpg( ネットワークID:I, TSID:I, サービスID:I, イベントID:I )
  イベント情報を検索する
  1～2引数のときは<自動予約検索条件>にマッチしたイベントを取得する。(onid>tsid>sid>eidソート、SearchEpgArchiveは未ソート)
  (SearchEpgArchiveが存在するバージョン以降)第2引数にイベントの開始時間の範囲を指定できる。
  SearchEpgArchive()は過去イベントが対象。※取得対象が月～年単位になりうるので、時間指定のない呼び出しは控えるべき。
  以前はnilが返る場合があったが(SearchEpgArchiveが存在するバージョン以降)常にリストが返る。
  4引数のときは指定イベントを取得する。なければnilが返る。

B AddReserveData( <予約情報> )
  予約を追加する
  失敗時はfalse。

B ChgReserveData( <予約情報> )
  予約を変更する
  失敗時はfalse。

DelReserveData( 予約ID:I )
  予約を削除する

<予約情報>のリスト GetReserveData()
<予約情報>|nil GetReserveData( 予約ID:I )
  予約を取得する(reserveIDソート)
  無引数のときは全予約を取得する。
  1引数のときは指定予約を取得する。なければnilが返る。
  予約IDが0x7FFFFFFFのときは録画マージンなどのデフォルト値を取得する。未対応バージョンではnilが返る。recSetting.batFilePath
  について、「*」以降をBatFileTagとして扱うバージョンでは'*'が返る。

S|nil GetRecFilePath( 予約ID:I )
  予約の録画ファイルパスを取得する
  録画中でなかったり視聴予約などで取得できなければnilが返る。

<録画済み情報>のリスト GetRecFileInfo()
<録画済み情報>|nil GetRecFileInfo( 情報ID:I )
  録画済み情報を取得する(idソート)
  無引数のときは全情報を取得する。
  1引数のときは指定情報を取得する。なければnilが返る。

<録画済み情報>のリスト GetRecFileInfoBasic()
<録画済み情報>|nil GetRecFileInfoBasic( 情報ID:I )
  基本的な録画済み情報を取得する(idソート)
  programInfoとerrInfoが常に空文字列になる以外はGetRecFileInfo()と同じ。
  programInfoとerrInfoを取得するためのファイルアクセスのコストが無い。
  例：このメソッドが存在するならこれを使ってリストを取得する
      a=edcb.GetRecFileInfoBasic and edcb.GetRecFileInfoBasic() or edcb.GetRecFileInfo()

ChgPathRecFileInfo( 情報ID:I, 録画ファイルパス:S )
  録画済み情報の録画ファイルパスを変更する

ChgProtectRecFileInfo( 情報ID:I, プロテクト:B )
  録画済み情報のプロテクトを変更する

DelRecFileInfo( 情報ID:I )
  録画済み情報を削除する

<チューナ予約情報>のリスト GetTunerReserveAll()
  チューナごとの予約の割り当て情報を取得する(tunerIDソート)
  ただし、リストの最終要素はチューナ不足の予約を表す。

<録画プリセット>のリスト EnumRecPresetInfo()
  録画プリセット情報を取得する(idソート)
  少なくともデフォルトプリセット(id==0)は必ず返る。
  実装は下記[※1]。プリセットの提供方法は任意なので、必ずしもこれを使う必要はない。

<自動予約登録情報>のリスト EnumAutoAdd()
  自動予約登録情報を取得する(dataIDソート)

<自動予約(プログラム)登録情報>のリスト EnumManuAdd()
  自動予約(プログラム)登録情報を取得する(dataIDソート)

DelAutoAdd( 登録ID:I )
  自動予約登録情報を削除する

DelManuAdd( 登録ID:I )
  自動予約(プログラム)登録情報を削除する

B AddOrChgAutoAdd( <自動予約登録情報> )
  自動予約登録情報を追加/変更する
  dataIDフィールドが0のとき追加の動作になる。
  失敗時はfalse。

B AddOrChgManuAdd( <自動予約(プログラム)登録情報> )
  自動予約(プログラム)登録情報を追加/変更する
  dataIDフィールドが0のとき追加の動作になる。
  失敗時はfalse。

I GetNotifyUpdateCount( 通知ID:I )
  更新通知のカウンタを取得する
  0から始まってイベントが発生するたびに1だけ増える数値が返る。
  取得できない通知IDを指定すると-1が返る。
  通知ID:
    1=EPGデータが更新された
    2=予約情報が更新された
    3=録画済み情報が更新された
    4=自動予約登録情報が更新された
    5=自動予約(プログラム)登録情報が更新された

<ファイル情報>のリスト ListDmsPublicFile()
  廃止

<ファイル情報>のリスト|nil FindFile( 検索パターン:S, 取得数:I )
  Win32APIのFindFirstFileを呼ぶ
  取得数を0とするとすべての検索結果を取得する。
  1つ以上の検索結果があるときはリスト、それ以外はnilが返る。

B[,I] OpenNetworkTV( 送信モード:I, ネットワークID:I, TSID:I, サービスID:I [, NetworkTVID:I ] )
  NetworkTVモードを開始する、またはサービスを変更する
  EpgTimerSrv設定の「視聴に使用するBonDriver」から使われていないチューナを優先度逆順に探して起動する。
  NetworkTVモードの優先度は予約より低くEPG取得より高い。
  送信モードにチューナのUDP(+1)/TCP(+2)オプションを指定する。開始済みチューナの送信モードは変化しない。
  (IsOpenNetworkTVが存在するバージョン以降)NetworkTVIDを指定できる。省略時は0を指定したものとみなす。
  NetworkTVIDは任意の整数で、大小に優劣はない。EpgTimerのNetworkTVモードでは0が使われる。
  NetworkTVIDの異なるNetworkTVモードは干渉なく扱える。
  失敗時はfalse。成功時はtrueと(IsOpenNetworkTVが存在するバージョン以降)チューナのプロセスIDを返す。

B[,I] IsOpenNetworkTV( NetworkTVID:I )
  NetworkTVモードが開始しているか
  戻り値はOpenNetworkTVと同じ。

CloseNetworkTV( [ NetworkTVID:I ] )
  NetworkTVモードを終了する
  (IsOpenNetworkTVが存在するバージョン以降)NetworkTVIDを指定できる。省略時は0を指定したものとみなす。

[※1]
edcb.EnumRecPresetInfo=function()
  local gp,p,d,r=edcb.GetPrivateProfile,'EpgTimerSrv.ini',{0},{}
  --【プリセットIDはカンマ区切りでPresetID=1,2,3,のように(0～3の4つある場合。0は不要)保存されている】
  for v in gp('SET','PresetID','',p):gmatch('[0-9]+') do
    d[#d+1]=0+v
  end
  table.sort(d)
  for i=1,#d do
    if i==1 or d[i]~=d[i-1] then
      --【REC_DEF{プリセットID}セクション】
      local n='REC_DEF'..(d[i]==0 and '' or d[i])
      local m=gp(n,'UseMargineFlag',0,p)~='0'
      r[#r+1]={
        id=d[i],
        name=d[i]==0 and 'Default' or gp(n,'SetName','',p),
        recSetting={
          recMode=tonumber(gp(n,'RecMode',1,p)) or 1,
          noRecMode=tonumber(gp(n,'NoRecMode',1,p)) or 1,
          priority=tonumber(gp(n,'Priority',2,p)) or 2,
          tuijyuuFlag=gp(n,'TuijyuuFlag',1,p)~='0',
          serviceMode=tonumber(gp(n,'ServiceMode',0,p)) or 0,
          pittariFlag=gp(n,'PittariFlag',0,p)~='0',
          batFilePath=gp(n,'BatFilePath','',p),
          suspendMode=tonumber(gp(n,'SuspendMode',0,p)) or 0,
          rebootFlag=gp(n,'RebootFlag',0,p)~='0',
          startMargin=m and (tonumber(gp(n,'StartMargine',0,p)) or 0) or nil,
          endMargin=m and (tonumber(gp(n,'EndMargine',0,p)) or 0) or nil,
          continueRecFlag=gp(n,'ContinueRec',0,p)~='0',
          partialRecFlag=tonumber(gp(n,'PartialRec',0,p)) or 0,
          tunerID=tonumber(gp(n,'TunerID',0,p)) or 0,
          recFolderList={},
          partialRecFolder={}
        }
      }
      --【REC_DEF_FOLDER{プリセットID}セクション】
      n=n:gsub('EF','EF_FOLDER')
      for j=1,tonumber(gp(n,'Count',0,p)) or 0 do
        r[#r].recSetting.recFolderList[j]={
          recFolder=gp(n,''..(j-1),'',p),
          writePlugIn=gp(n,'WritePlugIn'..(j-1),'Write_Default.dll',p),
          recNamePlugIn=gp(n,'RecNamePlugIn'..(j-1),'',p)
        }
      end
      --【REC_DEF_FOLDER_1SEG{プリセットID}セクション】
      n=n:gsub('ER','ER_1SEG')
      for j=1,tonumber(gp(n,'Count',0,p)) or 0 do
        r[#r].recSetting.partialRecFolder[j]={
          recFolder=gp(n,''..(j-1),'',p),
          writePlugIn=gp(n,'WritePlugIn'..(j-1),'Write_Default.dll',p),
          recNamePlugIn=gp(n,'RecNamePlugIn'..(j-1),'',p)
        }
      end
    end
  end
  return r
end

■テーブル定義■
<チャンネル情報>={
  onid:I=ネットワークID
  tsid:I=TSID
  sid:I=サービスID
  serviceType:I=サービスタイプ
  partialFlag:B=限定受信サービス(ワンセグ)かどうか
  serviceName:S=サービス名
  networkName:S=ネットワーク名
  epgCapFlag:B=EPGデータ取得対象かどうか
  searchFlag:B=検索時のデフォルト検索対象サービスかどうか
}

<サービス情報>={
  onid:I=ネットワークID
  tsid:I=TSID
  sid:I=サービスID
  service_type:I=サービス形式種別
  partialReceptionFlag:B=限定受信サービス(ワンセグ)かどうか
  service_provider_name:S=事業者名
  service_name:S=サービス名
  network_name:S=ネットワーク名
  ts_name:S=TS名
  remote_control_key_id:I=リモコンキー識別
}

<イベント情報>={
  onid:I=ネットワークID
  tsid:I=TSID
  sid:I=サービスID
  eid:I=イベントID
  startTime:TIME|nil=開始時間(不明のときnil)
  durationSecond:I|nil=総時間(不明のときnil)
  freeCAFlag:B=ノンスクランブルフラグ
  shortInfo:{
    event_name:S=イベント名
    text_char:S=情報
  }|nil=EPG基本情報(ないときnil、以下同様)
  extInfo:{
    text_char:S=詳細情報
  }|nil=EPG拡張情報
  contentInfoList:{
    content_nibble:I=大分類*256+中分類
    user_nibble:I=独自ジャンル大分類*256+独自ジャンル中分類
  }のリスト|nil=EPGジャンル情報
  componentInfo:{
    stream_content:I=コンポーネント内容
    component_type:I=コンポーネント種別
    component_tag:I=コンポーネントタグ
    text_char:S=コンポーネント記述
  }|nil=EPG映像情報
  audioInfoList:{
    stream_content:I=以下、STD-B10音声コンポーネント記述子を参照
    component_type:I=*
    component_tag:I=*
    stream_type:I=*
    simulcast_group_tag:I=*
    ES_multi_lingual_flag:B=*
    main_component_flag:B=*
    quality_indicator:I=*
    sampling_rate:I=*
    text_char:S=*
  }のリスト|nil=EPG音声情報
  eventGroupInfo:{
    group_type:I=グループ種別。必ず1(イベント共有)
    eventDataList:{onid:I, tsid:I, sid:I, eid:I}のリスト
  }|nil=EPGイベントグループ情報
  eventRelayInfo:{
    group_type:I=グループ種別
    eventDataList:{onid:I, tsid:I, sid:I, eid:I}のリスト
  }|nil=EPGイベントグループ(リレー)情報
}

<予約情報>={
  title:S=番組名
  startTime:TIME=録画開始時間
  durationSecond:I=録画総時間
  stationName:S=サービス名
  onid:I=ネットワークID
  tsid:I=TSID
  sid:I=サービスID
  eid:I=イベントID
  comment:S=コメント
  reserveID:I=予約ID
  overlapMode:I=かぶり状態
  startTimeEpg:TIME=予約時の開始時間
  recSetting:<録画設定>=録画設定
}

<録画設定>={
  recMode:I=録画モード(0～5)
  noRecMode:I|nil=無効時の録画モード(0～4。recModeが5でないとき無意味。recModeが5のとき、nilならば1とみなす)
  priority:I=優先度
  tuijyuuFlag:B=イベントリレー追従するかどうか
  serviceMode:I=処理対象データモード
  pittariFlag:B=ぴったり?録画
  batFilePath:S=録画後BATファイルパス
  suspendMode:I=休止モード
  rebootFlag:B=録画後再起動する
  startMargin:I|nil=録画開始時のマージン(デフォルトのときnil)
  endMargin:I|nil=録画終了時のマージン(デフォルトのときnil)
  continueRecFlag:B=後続同一サービス時、同一ファイルで録画
  partialRecFlag:I=物理CHに部分受信サービスがある場合、同時録画する(=1)か否(=0)か
  tunerID:I=強制的に使用Tunerを固定
  recFolderList={
    recFolder:S=録画フォルダ
    writePlugIn:S=出力PlugIn
    recNamePlugIn:S=ファイル名変換PlugIn
  }のリスト=録画フォルダパス
  partialRecFolder={
    (recFolderListと同じ)
  }のリスト=部分受信サービス録画のフォルダ
}

<録画済み情報>={
  id:I=情報ID
  recFilePath:S=録画ファイルパス
  title:S=番組名
  startTime:TIME=開始時間
  durationSecond:I=録画時間
  serviceName:S=サービス名
  onid:I=ネットワークID
  tsid:I=TSID
  sid:I=サービスID
  eid:I=イベントID
  drops:I=ドロップ数
  scrambles:I=スクランブル数
  recStatus:I=録画結果のステータス
  startTimeEpg:TIME=予約時の開始時間
  comment:S=コメント
  programInfo:S=.program.txtファイルの内容
  errInfo:S=.errファイルの内容
  protectFlag:B=プロテクト
}

<チューナ予約情報>={
  tunerID:I=チューナID
  tunerName:S=BonDriverファイル名
  reserveList:予約ID:Iのリスト=そのチューナに割り当てられた予約
}

<録画プリセット>={
  id:I=プリセットID
  name:S=プリセット名
  recSetting:<録画設定>=プリセット録画設定
}

<自動予約登録情報>={
  dataID:I=登録ID
  addCount:I=予約追加カウント(参考程度)。登録時はnilで良い
  searchInfo:<自動予約検索条件>=検索条件
  recSetting:<録画設定>=録画設定
}

<自動予約検索条件>={
  ### 以下のプロパティはEnumAutoAddなどの取得系メソッドでnilになることはない
  andKey:S=キーワード
  notKey:S=NOTキーワード
  regExpFlag:B|nil=正規表現モード(省略時false)
  titleOnlyFlag:B|nil=番組名のみ検索対象にする(省略時false)
  aimaiFlag:B|nil=あいまい検索モード(省略時false)
  notContetFlag:B|nil=対象ジャンルNOT扱い(省略時false)
  notDateFlag:B|nil=対象期間NOT扱い(省略時false)
  freeCAFlag:I|nil=スクランブル放送(0=限定なし(省略時),1=無料のみ,2=有料のみ)
  chkRecEnd:B|nil=録画済かのチェックあり(省略時false)
  chkRecDay:I|nil=録画済かのチェック対象期間(省略時0)
  chkRecNoService:B|nil=録画済チェックでサービスを無視する(省略時false)
  chkDurationMin:I|nil=番組最小長(分)(省略時0)
  chkDurationMax:I|nil=番組最大長(分)(省略時0)
  contentList:{
    content_nibble:I=大分類*256+中分類
    user_nibble:I|nil=独自ジャンル大分類*256+独自ジャンル中分類(省略時0)
  }のリスト=対象ジャンル
  dateList:{
    startDayOfWeek:I=検索開始曜日(0=日,1=月...)
    startHour:I=開始時
    startMin:I=開始分
    endDayOfWeek:I=検索終了曜日(0=日,1=月...)
    endHour:I=終了時
    endMin:I=終了分
  }のリスト|nil=対象期間(省略時空リスト)
  serviceList:{
    onid:I=ネットワークID
    tsid:I=TSID
    sid:I=サービスID
  }のリスト|nil=対象サービス(省略時空リスト)
  ### 以下のプロパティは取得系メソッドで常にnil
  network:I|nil=対象ネットワーク(+1=地デジ,+2=BS,+4=CS1+CS2,+8=その他。該当サービスがserviceListに追加される。互換用なので使用しないこと)
  days:I|nil=対象期間(現時刻からdays*24時間以内。SearchEpg以外では無視される)
}

<自動予約(プログラム)登録情報>={
  dataID:I=登録ID
  dayOfWeekFlag:I=対象曜日(+1=日,+2=月...)
  startTime:I=録画開始時間(00:00を0として秒単位)
  durationSecond:I=録画総時間
  title:S=番組名
  stationName:S=サービス名
  onid:I=ネットワークID
  tsid:I=TSID
  sid:I=サービスID
  recSetting:<録画設定>=録画設定
}

<ファイル情報>={
  name:S=ファイル名
  size:I=ファイルサイズ
  isdir:B=ディレクトリかどうか
  mtime:TIME=修正時間
}
