<!-- vim:set ft=markdown: -->

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

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

Windows以外の環境については【UNIX】をつけて注釈します。文中で拡張子が.dllや.exe
となっているものはWindows以外では.soや拡張子なしと読みかえてください。  
以下、EpgTimerSrv.exeやEpgTimerSrv.iniのあるフォルダを「EDCBのルートフォルダ」と
表現します。【UNIX】これは通常/var/local/edcbです。



Readme.txtの改変点
----------------------------------------
#### ■動作環境
  必要なランタイムはビルド環境次第になります。  
  OSは、おそらくXP SP3以降なら動くでしょう(XPはすでに終了しているので未確認)。

#### ■twitter.dllの取り扱いについて
  twitter.dllは削除しました。無視してください。

#### ■基本的な使用準備
  手順に大きな変更はないですが、チューナー数の設定はEpgTimerSrv.exeの起動だけで
  もできます。タスクトレイに表示される時計アイコンを右クリック→「システム→Srv
  設定」で設定してください。既定でEpgTimerSrvはEpgTimerに連動して終了しなくなっ
  たので、設定を反映させるには右クリック→「Srv終了」で直接EpgTimerSrvを終了して
  ください。  
  Program Files等のOSが特別に管理するフォルダへの配置は避けてください。書き込み
  保護や仮想化、大規模アップデート時の退避処理によりトラブルを引き起こします。

  【UNIX】EpgDataCap_BonとEpgTimerSrvは通常/usr/local/binにあるコマンドベースの
  実行ファイルです。BonDriver、ファイル名変換PlugIn、出力PlugIn、その他のモジュ
  ールは/usr/local/lib/edcbの直下にあり、ワイルドカードで`BonDriver*.so`に一致す
  るものをBonDriver、`RecName*.so`に一致するものをファイル名変換PlugIn、
  `Write*.so`に一致するものを出力PlugInと判断します。ファイル名の大文字小文字が
  区別されることに注意してください。設定ファイルなど使用中に変化するものは
  /var/local/edcb以下に置かれます。  
  BonDriverは https://github.com/u-n-k-n-o-w-n/BonDriverProxy_Linux の仕様に従う
  ものを利用できます。 https://github.com/xtne6f/BonDriverTunnel のものも利用で
  きます。B25Decoder関係についてはとくに説明しませんが、Windowsのものと特段違い
  はないです。  
  `EpgDataCap_Bon -d BonDriver_foo.so -chscan`でチャンネルスキャンできます。
  `EpgDataCap_Bon -d BonDriver_foo.so -nid {ネットワークID} -tsid {TSID} -sid {サービスID} -rec`
  で即時録画できます(保存先は「録画フォルダ」で指定した場所)。その他の引数は
  `EpgDataCap_Bon -h`で確認してください。  
  EpgDataCapBon、EpgTimerSrvとも一般的なシグナル(SIGHUPかSIGINTかSIGTERM、たとえ
  ばCtrl+C)で終了します。後処理が不十分になるので強制終了(SIGKILL)はやむをえない
  場合をのぞき送らないでください。  
  EpgTimerSrvはWindowsと同様にチューナー数の設定や設定後の再起動が必要です。ほと
  んどの設定はWebUIで可能です。



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データの取得・解析を行うタイミングを遅らせます。
    - ロゴデータを保存する【追加】  
      PNG形式のロゴデータをLogoDataフォルダに保存します。
      - 取得するロゴタイプ  
        サイズが異なるタイプ0～5の各ロゴのどれを取得するかフラグ(1+2+4+8+16+32)
        で指定します。既定値は32(タイプ5のみ取得)です。

  - ●ネットワーク設定タブ  
    IPv6アドレスも使用できます【追加】。
    - TCP送信  
      ポート番号が22000～22999の範囲では送信形式がplain(BonDriver_TCP.dllのため
      の特別なヘッダをつけない)になります【追加】。  
      送信先を0.0.0.1(SrvPipe)にすると、TCPではなく名前付きパイプ
      `\\.\pipe\SendTSTCP_{ポート番号}_{プロセスID}`として外部ツールなどの接続を
      待ち受けます【追加】。【UNIX】EDCBのルートフォルダにFIFOファイル
      `SendTSTCP_{ポート番号}_{プロセスID}_[01].fifo`を作ります。  
      送信先を0.0.0.2(Pipe)にすると、BonDriver_Pipe.dllに接続します【追加】。ポ
      ート番号はPipe番号になります。

  - ●外部アプリケーション設定タブ  
    コマンドラインオプションにUDP/TCP/Pipeで送信中のポート番号やEpgDataCap_Bonの
    プロセスIDをマクロで指定できます【追加】。`$PipePort$`は`$PipeNumber$`に1234
    を加えた値です。  
    既定で複数起動を抑止するようになりました。また、EpgDataCap_Bonの終了時に一緒
    に閉じる設定【追加】は視聴予約などに便利です。

#### ■例外発生時のスタックトレース出力について 【追加】

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



Readme_EpgTimer.txtの改変点
----------------------------------------
"EpgTimer.exe"を"EpgTimerNW～.exe"にファイル名をリネームすることで、EpgTimerNW相
当の動作になります。～には任意の文字列を指定可能で、この文字列が異なるEpgTimerNW
は多重起動できます。通常のEpgTimerも"EpgTimer～.exe"にリネームして多重起動できま
すが、必ずEDCBのルートフォルダに置いてください。また、リネームした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:など)と個別に正規表現モードになります。
  - メモ【追加】  
    メモ欄です。実装上は`:note:`で始まるNOTキーワードです。
  - 正規表現モード  
    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$"`の
    ように引用符で囲うなどしてください。【UNIX】「"」の置換は行いません。
  - 録画マージン  
    完全な録画を重視する場合、開始マージンは十分に確保してください。予約管理やPC
    の状態によって数秒から十数秒程度の遅延は起こり得ます。デフォルトの開始マージ
    ン5秒は一般にやや不十分です。

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

  - ●基本設定タブ
    - ●保存フォルダ
      - コマンドライン引数【追加】  
        録画用アプリの引数をカスタマイズします。EpgDataCap_Bon.exeの場合は弄る必
        要ありません。
        - 最小化: EPG取得や「最小化で起動する」設定の録画時に付加される
        - 非視聴時: 視聴時以外や、視聴時またはEPG取得を除く起動時の「Viewを起動
                    する」にチェックしていないときに付加される
        - ONID/TSID/SID: 録画や視聴時のチャンネルの初期値として付加され、チャン
                         ネル変更が余分に発生するのを回避できる。設定値のいずれ
                         かを空にすれば原作挙動に戻る
      - 録画情報保存フォルダ【追加】  
        .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に相当)。有効の場
        合でも、ひとつ前の文字が「.」の場合(「.\」)は変換対象です。  
        【UNIX】「\」ではなく「/」を除外します。「/」と制御文字(NUL～US、DEL)以
        外の禁則文字はありません。
      - 録画中の予約削除を【追加】
        - 削除のみ(従来動作)
        - 録画済みに追加  
          「録画終了」として録画済み一覧に追加します。録画後実行batが実行され、
          成功として扱われる点に注意してください。
        - キャンセルとして録画済みに追加  
          「録画中にキャンセルされた可能性があります」として追加します。
    - ●ボタン表示
      - タブの位置に表示【追加】  
        上部表示ボタンを上部タブと並列に配置します。
    - ●その他/全般
      - ネットワーク接続を許可する
        - IPv6【追加】  
          IPv6サーバにします。IPv4IPv6デュアルスタックではありません。
          アクセス制御もIPv6で指定してください(`+::1,+fe80::/64`など)。
        - アクセス制御【追加】  
          EpgTimerSrv.exeが接続を許可するクライアントのIPアドレスを  
            `{許可+|拒否-}{ネットワーク}/{ネットマスク},...`  
          の形式で複数指定します。指定は左から順にクライアントと比較され、最後に
          マッチした指定の+-で可否が決まります(HTTPサーバ機能と同じルール)。記述
          に誤りがあればすべて拒否します。拒否されたIPはデバッグ出力されます。
        - 無通信タイムアウト(秒)【追加】  
          EpgTimerNWのネットワーク接続で「クライアント側に待ち受けポートを作る」
          をオフにしたときに使われるロングポーリングの再接続の間隔を指定します。
      - EPG取得時に放送波時間でPC時計を同期する  
        同期の信頼性を確保するため、150秒の放送波時間の観測を行います。EPG取得時
        間が延べ150(複数時は150÷チューナ数)秒に満たない場合は同期しません。
        特権については後述「EpgTimerAdminProxy.exeについて」も参照してください。
        【UNIX】`/bin/date -s 20xx-xx-xxTxx:xx:xxZ`をsudoで実行するので、例えば
        EpgTimerSrvの実行ユーザーがsudoグループ(環境によりwheelグループ)に所属し
        ているならvisudoコマンドを使って/etc/sudoersに以下を追記すればOKです。  
        `%sudo ALL=(ALL:ALL) NOPASSWD: /bin/date -s 20[0-9][0-9]-[01][0-9]-[0-3][0-9]T[0-2][0-9]\:[0-5][0-9]\:[0-5][0-9]Z`
      - EPG取得後も番組情報をX日前まで保存する【追加】  
        EPG取得後は過去の番組情報が消えますが、これを"EpgArc.dat"というファイル
        に保存して、番組表などに利用できるようにします。∞を指定すると、さらに過
        去の番組情報をEpgArc2フォルダに一週間単位で保存し続けます。
      - EpgTimerSrvを常駐させる【追加】
        - タスクトレイアイコンを表示する【追加】
        - 開始準備で点滅させる【追加】  
          「予約録画開始準備」(使用BonDriver通知を除く)の通知で点滅させます。
        - バルーンチップ/トーストでの動作通知を抑制する【追加】
          - リアルタイムで表示できなかった通知を捨てる【追加】  
            過去の通知が遅れてバルーンチップ/トースト表示されないようにします。

          ※これらはEpgTimerSrv.exeが直接表示するものについての設定です
      - 情報通知ログをファイルに保存する【追加】  
        情報通知ログをEDCBのルートフォルダのEpgTimerSrvNotify.logに保存します。
        情報通知ログのウィンドウの表示にも利用されます。
      - デバッグ出力をファイルに保存する【追加】  
        EpgTimerSrvのデバッグ出力(OutputDebugStringW)をEDCBのルートフォルダの
        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フォルダ](../ini)に簡単なサンプルを用意したので参考にしてくださ
        い。
      - 右クリックメニューにテーマを適用する(要再起動)【追加】  
        上述機能の右クリックメニュー限定版です。リソースを定義したいときは同様に
        EpgTimer.exe.rdcm.xamlを作成してください。
      - EPGデータを常に更新する(旧「EPGデータを自動的に読み込まない」相当)  
        EpgTimerSrvのEPGデータをEpgTimerにダウンロードするのを、番組表を表示する
        タイミング(≒番組表タブを開くタイミング)まで遅らせるかどうか指定します。
    - ●Windowsサービス  
      このタブは廃止しました。サービス登録、解除は[iniフォルダ](../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分、
  かつ抑制条件で指定した時間以上の開きがある場合に移行します。  
  【UNIX】スタンバイ、休止、シャットダウン、および復帰処理は未実装です。これらの
  タイミングでデバッグ出力に「Shutdown is not supported」を出力します。

#### ■録画後のバッチファイル実行の仕様
  バッチのプロセス優先度は"通常以下"(BELOW_NORMAL_PRIORITY_CLASS)で実行します。
  PowerShellスクリプト(.ps1)またはLuaスクリプト(.lua)(lua52.dllがある場合)も使用
  できます【追加】。【UNIX】シェルスクリプト(.sh)またはLuaスクリプトが使用できま
  す。シェルスクリプトには実行権限が必要です。プロセス優先度は変更しません。  
  LuaスクリプトはEpgTimerSrv内部のスレッドで実行します。短時間の処理への使用を想
  定していますが、処理時間が長くなる場合は後述
  [edcbグローバル変数の仕様](#Luaスクリプトのedcbグローバル変数の仕様)のSleepの
  説明を確認してください。  
  以下の拡張命令を利用できます【追加】。拡張命令はバッチファイル内のどこかに直接
  記述してください(remコメント等どんな形式でもOK)。
  - `_EDCBX_BATMARGIN_={bat実行条件(分)}`  
    このマージン以上録画予定がないときに実行開始します。デフォルトは0です。
  - `_EDCBX_HIDE_`  
    ウィンドウを非表示にします。Luaスクリプトでは無意味(ウィンドウがない)です。
  - `_EDCBX_NORMAL_`  
    ウィンドウを最小化しません。
  - `_EDCBX_DIRECT_`  
    マクロを置換ではなく環境変数で渡して直接実行します。マクロを囲う`$`が`%`にな
    るだけですが、`start /wait`を使って別のスクリプトに処理を引き継ぐときに便利
    です。マクロにUnicode文字を含む場合にも対応できます。  
    また、以下を保証します:
      - EDCBのルートフォルダに"EpgTimer_Bon_RecEnd.bat"を作らない
      - EpgTimer.exeを経由する間接実行はしない
      - カレントディレクトリはバッチのあるフォルダになる(Luaスクリプトを除く)

    PowerShellとLuaスクリプトでは常に有効です。【UNIX】常に有効です。
  - `_EDCBX_FORMATTIME_`  
    日時についてのマクロ(`$SDYY$`など)をISO8601形式の`$StartTime$`と
    `$DurationSecond$`に単純化します。  
    PowerShellとLuaスクリプトでは常に有効です。【UNIX】常に有効です。

  取得できるマクロについては以下の3行のコマンドで確認すると手っ取り早いです。
  ```batchfile
  rem _EDCBX_DIRECT_
  set
  pause
  ```

  (PowerShellの場合)
  ```powershell
  ls env:
  Read-Host "Press enter to continue"
  ```

  (Luaの場合。マクロはenvグローバル変数に格納)
  ```lua
  for k,v in pairs(env) do s=(s or '')..k..', ' end
  edcb.os.execute('start echo '..s)
  ```

  【UNIX】
  ```shell
  #!/bin/bash
  env >/var/local/edcb/env_test.txt
  ```

#### ■マクロ
  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
    - 【UNIX】この関数はなにもしない
  - 【文字列置換】S/置換文字列/置換後/.../
  - 【文字削除】Rm/削除文字リスト/
    - 例：番組内容から/を削除: `$Rm!/!(SubTitle)$`
    - サロゲートペアで表現される文字にも使える
  - 【足切り】Head[C]文字数[省略記号]
    - 例：番組内容を半角にして最長15文字に: `$Head15(ZtoH(SubTitle))$`
    - 例：番組名を省略記号つき最長15文字に: `$Head15~(Title)$`
    - 文字数はHeadがUTF-16換算、HeadCがUTF-8換算
  - 【頭切り】Tail[C]文字数[省略記号]
    - 例：予約IDを4桁に: `$Tail4((0000$ReserveID$))$`
    - 文字数はTailがUTF-16換算、TailCがUTF-8換算

  ※2重括弧にするとその中身を展開した結果が関数に渡される。入れ子にする場合は、
    閉じ括弧`))$`の数が内側より多くなるようになにもしない関数(`S.`など)で調整す
    る  
    例: `$S.(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機能は廃止しました。代わりにEDCBのルートフォルダに置かれた以下のバッチ
  ファイルを実行します【追加】。取得できるマクロは従来とだいたい同じです(NEW系マ
  クロは名前からNEWを取り除いています)。`$BatFileTag$`、およびPostRecEndに限り
  `$RecInfoID$`、これ以外に限り`$ReserveID$`(予約ID)、`$RecMode$`(録画モード0=全
  サービス～4=視聴)、`$ReserveComment$`(コメント)も取得できます。
  - PostAddReserve(.bat|.ps1|.lua|.sh) : 予約を追加したとき(無効を除く)
    - EPG自動予約のとき`$ReserveComment$`は"EPG自動予約"という文字列で始まります
  - PostChgReserve(.bat|.ps1|.lua|.sh) : 予約を変更したとき(無効を除く)
    - `$SYMDHMNEW$`～`$SEYMDHM28NEW$`は取得できません
  - PostRecStart(.bat|.ps1|.lua|.sh) : 録画を開始したとき
  - PostRecEnd(.bat|.ps1|.lua|.sh) : 録画を終了したとき
    - 取得できるマクロは録画後バッチと完全に同じです

  また、イベント発生時に以下のバッチファイルを実行します。
  - PostNotify(.bat|.ps1|.lua|.sh) : 更新通知が送られたとき
    - 取得できるマクロは`$NotifyID$`のみです
      - `$NotifyID$`(1=EPGデータ更新, 2=予約情報更新, 3=録画結果情報更新)
    - ほかのバッチと異なり、実行中でもスリープなどの録画後動作を抑制しません
    - 以下の拡張命令により`$NotifyID$`を0とした通知を定期的に送ることができます
      ```
      _EDCBX_NOTIFY_INTERVAL_={通知間隔(秒)}
      ```
  バッチ仕様は録画後バッチと同じですが、_EDCBX_BATMARGIN_は無効です。また、
  _EDCBX_DIRECT_でないときの一時ファイル名は"EpgTimer_Bon_Post.bat"です。
  実行は直列に行います(互いに並列実行しない)。また、実行時点で各々の動作が完了し
  ている(予約ファイル等更新済みである)ことを保証します。  
  [iniフォルダ](../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の組み込みについて](#CivetWebの組み込みについて)を参照。

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

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

#### ■しょぼいカレンダーに予約をアップロードする
  EpgTimerSrvに組み込みのしょぼいカレンダー連携機能はHTTPSを使わない(HTTPのみ)、
  アップロードタイミングがEPG再読み込み時のみ、など柔軟性に欠けるのでお勧めしま
  せん(そのうち廃止するかもしれません)。【UNIX】この機能は未実装です。  
  [ini/PostBatExamples](../ini/PostBatExamples)に更新通知を利用したスクリプト例
  があるのでそちらをお勧めします。

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

  例：`RecInfo2RegExp=\[[再無生]\]|🈞|🈚|🈢`  
      （[再]と[無]と[生]に対応）

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

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

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

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

#### ■EpgTimerSrvの起動オプション 【追加】
  - `/setting`  
    設定ダイアログを表示します
  - `/task`  
    EpgTimerTaskとして動作します
  - `/luapost {script}`  
    起動中のEpgTimerSrvにLuaスクリプトの実行をウィンドウメッセージで要求します

  【UNIX】起動オプションはありません。`/luapost`機能についてはEDCBのルートフォル
  ダのFIFOファイル`EpgTimerSrvLuaPost.fifo`にLuaスクリプトを書き込みます。

#### ■Write_DefaultのTeeコマンド機能について 【追加】

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

#### ■予約ファイル等のByteOrderMark付きUTF-8対応について 【追加】

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

##### 予約ファイル等の";;NextID="について 【追加】

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

#### ■番組の詳細情報についての追加仕様 【追加】

.program.txtなどの「詳細情報」のEPGデータはもともと項目名(出演者やスタッフなど)
とその内容に分かれていますが、以前はこれらをたんに改行で連結していました。追加仕
様では項目名に接頭辞として"- "(ハイフンとスペース)を置くことで、これらを分別でき
るようにします。(元のEPGデータの行頭が"- "の場合は"-- "にエスケープします)。  
加えて、.program.txtのフォーマットをすこし厳密にしています。具体的には1行目が日
付、2行目がサービス名、3行目から次の空行までが番組名、そこから次の空行までが番組
内容、その次の行が「詳細情報」という文字列であった場合そこから空行2行までが詳細
情報です。以前より概ねこのようなフォーマットでしたが、たまにこうならない場合があ
りました。

#### ■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フォルダ](../EpgTimer/EpgTimer/Common)のソースコード
CtrlCmd.csとCtrlCmdDef.csを参照してください。利用例が
[ini/PostBatExamples](../ini/PostBatExamples)/EdcbSchUploader.ps1にあります。

#### ■各種PlugInの設定ファイルについてのメタ情報の仕様 【追加】

Write_DefaultやRecName_Macroなどのプラグインの設定は、APIを使ってダイアログウィ
ンドウを呼び出すのが従来の仕組みですが、ウィンドウハンドルなど低水準な情報を必要
としたり応用が利かない面があります。そこで、設定ファイルについてのメタ情報をJSON
形式のUTF-8文字列としてプラグインファイルに埋め込みます。

メタ情報はJSONのサブ配列を格納した配列の形式になっていて  
`[["setting-59c4d329-ba81-4054-9f4d-d1900653338a",`  
という文字列で始まり  
`[""]]`  
で終わります。先頭のサブ配列はヘッダー情報で、その第1要素はプラグインの名前、第2
要素はフラグです。  
例: `[["setting-59c4d329-ba81-4054-9f4d-d1900653338a","サンプル PlugIn",0],`  
フラグが1のとき、設定ファイルを新規作成する際にUnicodeで保存するよう指示します。
フラグの最下位ビット以外は今のところ無視されます。

先頭と末尾を除くサブ配列は各項目の情報で、その第0要素はフラグになっています。フ
ラグの下位3ビット(0～7)は情報の種類を表し0のときは設定項目を表します。設定項目の
第1要素はセクション名、第2要素はキー名、第3要素はラベル、第4要素は既定値です。  
例: `[0,"SET","Name","名前","名無し"],`  
既定値の型が真偽値のときは、falseを0、trueを1とする例えばチェックボックスのよう
な項目です。  
例: `[0,"SET","Enabled","有効",false],`  
既定値の型が数値のときは、例えば数値入力ボックスのような項目です。第5要素は最小
値、第6要素は最大値です。  
例: `[0,"SET","Weight","重さ",100,-2147483648,2147483647],`  
フラグが1のときは単なるラベルになります。フラグが2～7の項目は今のところ無視され
ます。  
例: `[1,"※正しく入力してね"],`  
各項目のフラグに16を足すとサブ項目になり、典型的にはインデントされます。さらに8
を足すと、手前の項目の有効状態(true、非0、非空文字)に応じて典型的にはグレーアウ
トします。まとめると例えばこのような文字列になります。

```
[["setting-59c4d329-ba81-4054-9f4d-d1900653338a","サンプル PlugIn",0],
[0,"SET","Enabled","有効",false],
[24,"SET","Name","名前","名無し"],
[24,"SET","Weight","重さ",100,-2147483648,2147483647],
[25,"※正しく入力してね"],
[""]]
```

改行はサブ配列を区切る`],`の直後のみ可能です。これ以外の整形はできません。データ
型はfalse、true、整数(-2147483648～2147483647)、文字列(エスケープシーケンスは
`\\\\`と`\\"`のみ)のみ使えます。各サブ配列に余分な要素があれば無視されますが拡張
のために予約されています。



CivetWebの組み込みについて
----------------------------------------
HTTPサーバ機能の簡単化とディレクトリトラバーサル等々のバグ修正を目的に、EpgTimerSrv.exeにCivetWebを組み込みました。
有効にする場合はEDCBのルートフォルダにlua52.dllが必要です。対応するものをDLしてください。  
https://sourceforge.net/projects/luabinaries/files/5.2.4/Windows%20Libraries/Dynamic/

CivetWebについては本家のドキュメント↓を参照してください(英語) ※組み込みバージョンはv1.16  
https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md

SSL/TLSを利用する場合はEDCBのルートフォルダにlibssl-3(-x64).dllとlibcrypto-3(-x64).dllが必要です。自ビルドするか信頼できるどこかから入手してください。

EpgTimerSrv.iniのSETセクションを編集し、EpgTimerSrv.exeを再起動してください。  
以下のキー[=デフォルト]を利用できます:

- `EnableHttpSrv` [=0]  
  HTTPサーバ機能を有効にするかどうか
  - [=1]または[=2]で有効
  - [=2]にするとEDCBのルートフォルダにログファイルも出力
- `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` [=EDCBのルートフォルダの"HttpPublic"]  
  公開フォルダの場所
  - CivetWebのdocument_rootに相当
  - フォルダパスに日本語(マルチバイト)文字を含まないこと
    - EDCBを日本語を含むフォルダに入れている場合は要注意
  - 1つしか指定できないので、複数の場所を公開したいときはフォルダ内にmklinkコマンドでシンボリックリンクを作る
- `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フォルダ](../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'を含むとき、EDCBのルートフォルダの"ssl_cert.pem"
  - ssl_ca_file: EDCBのルートフォルダの"ssl_peer.pem"
  - ssl_default_verify_paths: "no" (既定の証明書フォルダを参照しない)
  - ssl_verify_peer: "ssl_peer.pem"が存在するとき"yes"
  - global_auth_file: EDCBのルートフォルダの"glpasswd"
  - access_control_allow_origin: "" (クロスドメイン通信は拒否)

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

リモートから利用したい場合はSSHポートフォワーディングをお勧めします。鍵の管理についての煩わしさが少なく応用が利き、設定ミスの危険も小さいと考えるからです。
Windows10以降SSHサーバーは標準のオプション機能でインストールでき、スマホ等のSSHクライアントアプリもConnectBotをはじめ多くあります。  
SSH以外の手段でLANを越える場合は以下を参考に"ssl_peer.pem"または"glpasswd"を作成し(フォルダごとの.htpasswdは不確実)、SSL/TLSを利用してください。
不正アクセス耐性は"ssl_peer.pem"がおそらく最善(OpenSSLの信頼性とほぼ等価)です。
CivetWebでセキュリティが確保されているだろうと判断できたのは認証処理までの不正アクセス耐性のみです。
パス無しの公開サーバとしての利用はお勧めしません。

#### "ssl_cert.pem"(秘密鍵+自己署名証明書)の作成手順例

※鵜呑みにしないこと。bashの場合はtype→cat  
※最近のブラウザはsubjectAltNameが必須のため、以下OpenSSLコマンドはバージョン1.1.1以降を使う  
※証明書のインポート方法は様々なのでブラウザやOSに合わせること
  - EdgeはOSの証明書ストアを参照するので"server.crt"を「信頼されたルート証明機関」としてインストールする
  - Firefoxは自己署名証明書を例外扱いする方針なので、証明書例外追加時に"server.crt"と拇印が等しいかだけ注意する

```batchfile
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)
type server.crt >ssl_cert.pem
type server.key >>ssl_cert.pem
```

#### "ssl_peer.pem"(信頼済みクライアント証明書リスト)の作成手順例

※証明書のインポート方法は様々なのでブラウザやOSに合わせること
  - EdgeはOSの証明書ストアを参照するので"edcb_key.p12"を「個人/証明書」としてインストールする
  - Firefoxは独自の証明書ストアを持つので"edcb_key.p12"を証明書マネージャーでインポートする

```batchfile
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～"とする必要があるかもしれない)
```

#### PowerShellを使った"glpasswd"(ダイジェスト認証ファイル)の簡単な作り方

```powershell
# ユーザ名root、認証領域mydomain.com、パスワードtest
"root:mydomain.com:test" | Out-File -Encoding ascii -NoNewline glpasswd
# ハッシュ値を計算
$hash=(Get-FileHash -Algorithm MD5 glpasswd).Hash.ToLowerInvariant()
# パスワード部分をハッシュ値で上書き
"root:mydomain.com:$hash" | Out-File -Encoding ascii -NoNewline glpasswd
```

#### Bashを使った"glpasswd"の簡単な作り方

```shell
# ユーザ名root、認証領域mydomain.com、パスワードtest
echo -n "root:mydomain.com:$(echo -n "root:mydomain.com:test" | md5sum | head -c 32)" >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のプロンプト画面は出ないが、第2(popenは第3)引数にtrueを指定すれば表示できる。
```lua
edcb.os.execute('ping localhost',true)
```

さらに、execute/popenの第3(第4)引数に追加の環境変数をテーブルで指定できる。テーブルの各要素の値は
文字列かfalse(継承される環境変数を打ち消す)とする。未対応バージョンでは第3(第4)引数は無視される。
```lua
edcb.os.execute('echo %hoge% %fuga% & pause', true, {hoge='ほげ♪',fuga='ふが☆'})
```

【UNIX】edcb.osとedcb.ioはLua標準ライブラリのosやioと基本的に同じものなので上記の追加機能はない。
ただし、edcb.ioはファイルクローズ(FD_CLOEXEC)についてのパッチを含むのでこちらの利用を推奨。追加で以下のメソッドを持つ。

`edcb.io._flock_nb( ファイルハンドル [, モード:'s'|'u'|'x' ] ) → boolean`
- ファイルのBSDロック(flock)を操作する  
  モード's'はLOCK_SH、'x'はLOCK_EX、'u'はLOCK_UNに相当。省略時のモードは'x'。
  ブロックしない(LOCK_NB)。
  成功時trueが返る。

[略語の定義]
- `B`: ブーリアン
- `I`: 整数
- `F`: 実数
- `S`: 文字列
- `TIME`: 標準ライブラリ`os.date('*t')`が返すテーブルと同じ
- `<テーブル名>`: [後述で定義](#Luaスクリプトのテーブル定義)するテーブル
- `～のリスト`: 添え字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の暗号論的乱数  
  バッチ実行時は存在しない

`CreateRandom( 生成するbyte数:I ) → S|nil`
- 暗号論的擬似乱数を生成する  
  1048576bytes(2097152文字)まで生成可能。
  16進数で表現した乱数が返る。失敗するとnilが返る(通常失敗しない)。

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

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

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

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

`GetPrivateProfile( セクション:S, キー:S, 既定値:S|I|B, ファイル名:S ) → S`
- Win32APIのGetPrivateProfileStringを呼ぶ  
  既定値が`B`のときはtrue=1、false=0に変換される。
  ファイル名はEDCBフォルダ配下に置かれたiniファイルを相対指定する。
  ```lua
  v=0+edcb.GetPrivateProfile('SET','HttpPort',5510,'EpgTimerSrv.ini')
  ```
  `\\`と`/`はどちらもフォルダ区切りとして扱われる。
  ファイル名が`'Setting\\'`で始まるときは「設定関係保存フォルダ」にリダイレクトされる。
  【UNIX】ファイル名が`'RecName\\'`または`'Write\\'`で始まるときはEDCBフォルダにリダイレクトされる。
  特例的に以下の引数でEDCBフォルダのパスが返る。
  ```lua
  edcb.GetPrivateProfile('SET','ModulePath','','Common.ini')
  ```
  【UNIX】以下の引数でEDCBのライブラリのディレクトリ(通常/usr/local/lib/edcb)のパスが返る。
  ```lua
  edcb.GetPrivateProfile('SET','ModuleLibPath','','Common.ini')
  ```

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

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

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

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

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

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

`GetEventMinMaxTime( ネットワークID:I, TSID:I, サービスID:I ) → {minTime:TIME, maxTime:TIME}|nil`  
`GetEventMinMaxTimeArchive( ネットワークID:I, TSID:I, サービスID:I ) → {minTime:TIME, maxTime:TIME}|nil`
- 指定サービスの全イベントについて最小開始時間と最大開始時間を取得する  
  開始時間未定でないイベントが1つもなければnil。
  GetEventMinMaxTimeArchive()は過去イベントが対象。
  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ソート、EnumEventInfoArchive()は未ソート)  
  リストのいずれかにマッチしたサービスについて取得する。
  - onid,tsid,sidフィールドを各々nilとすると、各々すべてのIDにマッチする。
  - IDの上位16bitにマッチのORマスクを付加できる。

  第2引数にイベントの開始時間の範囲を指定できる。
  - 開始時間がstartTime以上～startTime+durationSecond未満のイベントにマッチ
  - 空テーブルのときは開始時間未定のイベントにマッチ

  EnumEventInfoArchive()は過去イベントが対象。
  ※再利用の可能性があるため、過去イベントのeidをIDとして扱うべきでない。
  ※取得対象が月～年単位になりうるので、サービス指定も時間指定もない呼び出しは控えるべき。

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

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

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

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

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

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

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

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

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

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

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

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

`GetTunerProcessStatusAll() → <起動中のチューナ情報>のリスト`
- 起動中のチューナについて把握している情報の一覧を取得する(tunerIDソート)  
  各種統計情報についてはつねに把握しているとは限らないので参考程度。

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

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

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

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

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

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

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

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

`FindFile( 検索パターン:S, 取得数:I ) → <ファイル情報>のリスト|nil`
- Win32APIのFindFirstFileを呼ぶ  
  取得数を0とするとすべての検索結果を取得する。
  1つ以上の検索結果があるときはリスト、それ以外はnilが返る。
  【UNIX】検索パターンのワイルドカードのうち'*'は3個まで使用可能。

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

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

`CloseNetworkTV( [ NetworkTVID:I ] )`
- NetworkTVモードを終了する  
  NetworkTVIDを指定できる。省略時は0を指定したものとみなす。

#### EnumRecPresetInfo

```lua
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
```



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

#### `<サービス情報>` 定義
```
{
  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のリスト=そのチューナに割り当てられた予約
}
```

#### `<起動中のチューナ情報>` 定義
```
{
  tunerID:I=チューナID
  processID:I=プロセスID
  drop:I=ドロップ数
  scramble:I=スクランブル数
  signalLv:F=信号レベル
  space:I=チューナ空間、または不明(-1)
  ch:I=物理チャンネル、または不明(-1)
  onid:I=ネットワークID、または不明(-1)
  tsid:I=TSID、または不明(-1)
  recFlag:B=録画中かどうか
  epgCapFlag:B=EPGデータ取得中かどうか
}
```

#### `<録画プリセット>` 定義
```
{
  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=修正時間
}