#include <windows.h>
#include <wchar.h>
#include <psapi.h>
#include <wincrypt.h>
#include <uuids.h>

#include "kpi.h"
#include "kbmov_config.h"

//#define ENABLE_TEST_MODE //動作確認モード

#ifdef _DEBUG
#define ENABLE_TEST_MODE
#endif

//kbdshow.kpi の対応拡張子から音楽系を除いたものにする
#define SZ_SUPPORTEXT_DS_DEFAULT SZ_SUPPORTEXT_DS_DEFAULT1
#define SZ_SUPPORTEXT_DS_DEFAULT1 L".avi/.mpg/.m1v/.mpe/.mpeg/.asf/.wmv/.mpv2/.mov/.qt/.3gp/.3g2/.m4v/.mp4/"
#define SZ_SUPPORTEXT_DS_DEFAULT2 L".avi/.mpg/.m1v/.mpe/.mpeg/.asf/.wmv/.mpv2/.mov/.qt/.3gp/.3g2/.m4v/.mp4/.mkv/.flv/.ogm/.ogv/.rm/.webm/.vob"
#define SZ_SUPPORTEXT_DS_LIST  SZ_SUPPORTEXT_DS_DEFAULT1 "\t" SZ_SUPPORTEXT_DS_DEFAULT2

//MF で .mpg を再生すると冒頭の数秒間がカクつくので対応拡張子(MF)の既定値は最小限にしておく
//#define SZ_SUPPORTEXT_MF_DEFAULT  L".avi/.mpg/.m1v/.mpe/.mpeg/.asf/.wmv/.mpv2/.3gp/.3g2/.m4v/.mp4" 
#define SZ_SUPPORTEXT_MF_DEFAULT  L".3gp/.3g2/.m4v/.mp4"  

#ifdef ENABLE_TEST_MODE
static const wchar_t SZ_SUPPORTEXT_FOR_TEST_DEFAULT[] =
L".wav/.aif/.aiff/.aifc/.au/.snd/.avi/.mpg/.m1v/.mpe/.mpeg/.asf/.wmv/.mpv2/.mpa/.mov/.qt/.3gp/.3g2/.mka/.m4v/.mp4/.ac3/.mkv/.flv/.ogm/.ogv/.rm/.webm/"
L".mp3/.aac/.m4a/.ape/.ogg/.flac/.wv/.tta/.mpc/.opus/.vqf/.wma/.tak/.ofr" //audio(専用プラグイン同梱)
L"";
#endif

//General
static const wchar_t SEC_GENERAL[] = L"General";
//General->Key
static const wchar_t KEY_SUPPORTEXT[] = L"SupportExt";
static const wchar_t KEY_SUPPORTEXT_DS[] = L"SupportExtDS";
static const wchar_t KEY_SUPPORTEXT_MF[] = L"SupportExtMF";
#ifdef ENABLE_TEST_MODE
static const wchar_t KEY_SUPPORTEXT_FOR_TEST[] = L"SupportExtTest";
#endif

static const wchar_t KEY_USE_DS[] = L"UseDS";
static const bool    USE_DS_DEFAULT = true;
static const wchar_t SZ_USE_DS_DEFAULT[] = L"1";

static const wchar_t KEY_USE_MF[] = L"UseMF";
static const bool    USE_MF_DEFAULT = 1;
static const wchar_t SZ_USE_MF_DEFAULT[] = L"1";

static const wchar_t KEY_PRIOR_DS_MF[] = L"PriorDSMF";
static const int     PRIOR_DS_MF_DEFAULT = 0;
static const wchar_t SZ_PRIOR_DS_MF_DEFAULT[] = L"0";

static const wchar_t KEY_DISABLE_QT[] = L"DisableQT";
static const bool    DISABLE_QT_DEFAULT = false;
static const wchar_t SZ_DISABLE_QT_DEFAULT[] = L"0";

#ifdef ENABLE_TEST_MODE
static const wchar_t KEY_TEST[] = L"TestMode";
static const bool    TEST_DEFAULT = false;
static const wchar_t SZ_TEST_DEFAULT[] = L"0";
#endif
static const wchar_t KEY_OPEN_FROM_STREAM[] = L"OpenFromStream";
static const bool    OPEN_FROM_STREAM_DEFAULT = false;
static const wchar_t SZ_OPEN_FROM_STREAM_DEFAULT[] = L"0";

static const wchar_t KEY_VIDEO[] = L"Video";
static const bool    VIDEO_DEFAULT = true;
static const wchar_t SZ_VIDEO_DEFAULT[] = L"1";

static const wchar_t KEY_FULLSCREEN[] = L"FullScreen";
static const wchar_t FULLSCREEN_DEFAULT = false;
static const wchar_t SZ_FULLSCREEN_DEFAULT[] = L"0";

static const struct{
    const wchar_t *cszGuid;
    const wchar_t *cszName;
}VIDEO_RENDERERS[] = {
    {L"{FA10746C-9B63-4B6C-BC49-FC300EA5F256}", L"Enhanced Video Renderer(EVR)"},  //EVR
    {L"{51B4ABF3-748F-4E3B-A276-C828330E926A}", L"Video Mixing Renderer 9(VMR9)"}, //VMR9
    {L"{B87BEB7B-8D29-423F-AE4D-6582C10175AC}", L"Video Mixing Renderer 7(VMR7)"}, //VMR7
    {L"{6BC1CFFA-8FC1-4261-AC22-CFB4CC38DB50}", L"Default Video Renderer"},//VMR7?
    {L"{70E102B0-5556-11CE-97C0-00AA0055595A}", L"Old Video Renderer"}, //
    {L"{E1A8B82A-32CE-4B0D-BE0D-AA68C772E423}", L"madVR"}, //madVR
    //{L"{71F080AA-8661-4093-B15E-4F6903E77D0A}",  L"MPC Video Renderer"}, //MPC Video Renderer(未対応)
    {NULL, NULL}
};

static const wchar_t* VIDEO_RENDRER_DEFAULT_VISTA_OR_LATER = VIDEO_RENDERERS[0].cszGuid; //EVR
static const wchar_t* VIDEO_RENDRER_DEFAULT_XP = VIDEO_RENDERERS[2].cszGuid;             //VMR7

static const wchar_t KEY_VIDEO_RENDERER[] = L"VideoRenderer";
static const wchar_t* VIDEO_RENDERER_DEFAULT = VIDEO_RENDRER_DEFAULT_VISTA_OR_LATER;

bool __fastcall CheckVistaOrLater(void)
{
    static int nVistaOrLater = -1;
    const int nDefIndexVistaOrLater = 0;
    const int nDefIndexXP = 1;
    if(nVistaOrLater < 0){
        OSVERSIONINFOEX osv = { 0 };
        osv.dwOSVersionInfoSize = sizeof(osv);
        osv.dwMajorVersion = 6;//Vista
        ULONGLONG condition = 0;
        VER_SET_CONDITION(condition, VER_MAJORVERSION, VER_GREATER_EQUAL);
        nVistaOrLater = VerifyVersionInfo(&osv, VER_MAJORVERSION, condition) != 0;
        if(nVistaOrLater){
            VIDEO_RENDERER_DEFAULT = VIDEO_RENDRER_DEFAULT_VISTA_OR_LATER;
        }
        else{
            VIDEO_RENDERER_DEFAULT = VIDEO_RENDRER_DEFAULT_XP;
        }
    }
    return nVistaOrLater != 0;
}
//////////////////////////////////////////////////////////////////////////////
static wchar_t* __fastcall AddExt(wchar_t* pszExts, const wchar_t* cszExt)
{//pszExts に cszExt を追加して追加後の拡張子文字列を返す
 //元の pszExts は free され、戻り値は呼び出し側が free する
 //pszExts は正しい文字列であることが前提( .aaa/.bbb/.ccc etc...)(空文字列可、NULL不可)
 //cszExt も正しい文字列であることが前提( .xxx etc...)(空文字列可、NULL不可)
    wchar_t* pszNewExts = _wcsdup(pszExts);
    size_t len = wcslen(pszNewExts) + wcslen(cszExt) + 2;
    wchar_t* context;
    wchar_t* p = wcstok_s(pszNewExts, L"/", &context);
    while (p) {
        if (_wcsicmp(p, cszExt) == 0) {//重複
            free(pszNewExts);
            return pszExts;
        }
        p = wcstok_s(NULL, L"/", &context);
    }
    pszNewExts = (wchar_t*)realloc(pszNewExts, sizeof(wchar_t) * len);
    wcsncpy_s(pszNewExts, len, pszExts, _TRUNCATE);
    if (pszNewExts[0]) {
        wcsncat_s(pszNewExts, len, L"/", _TRUNCATE);
    }
    wcsncat_s(pszNewExts, len, cszExt, _TRUNCATE);
    free(pszExts);
    return pszNewExts;
}
//////////////////////////////////////////////////////////////////////////////
static wchar_t* __fastcall RemoveExt(wchar_t* pszExts, const wchar_t* cszExt)
{//pszExts から cszExt を除去して除去後の拡張子文字列を返す
 //元の pszExts は free され、戻り値は呼び出し側が free する
 //pszExts は正しい文字列であることが前提( .aaa/.bbb/.ccc etc...)(空文字列可、NULL不可)
 //cszExt も正しい文字列であることが前提( .xxx etc...)(空文字列可、NULL不可)
    wchar_t* pszNewExts = _wcsdup(L"");
    wchar_t* context;
    wchar_t* p = wcstok_s(pszExts, L"/", &context);
    while (p) {
        if (_wcsicmp(p, cszExt) != 0) {
            const size_t len_new_exts = wcslen(pszNewExts) + wcslen(p) + 1 + 1;// '/' の分だけ追加
            pszNewExts = (wchar_t*)realloc(pszNewExts, len_new_exts * sizeof(wchar_t));
            if (pszNewExts[0]) {
                wcsncat_s(pszNewExts, len_new_exts, L"/", _TRUNCATE);
            }
            wcsncat_s(pszNewExts, len_new_exts, p, _TRUNCATE);
        }
        p = wcstok_s(NULL, L"/", &context);
    }
    free(pszExts);
    return pszNewExts;
}
//////////////////////////////////////////////////////////////////////////////
static wchar_t* __fastcall RemoveExts(wchar_t* pszExts, const wchar_t* cszExts)
{//pszExts から cszExts を除去
 //元の pszExts は free され、戻り値は呼び出し側が free する
    wchar_t* pszTmpExts = _wcsdup(cszExts);
    wchar_t* context;
    wchar_t* p = wcstok_s(pszTmpExts, L"/", &context);
    while (p) {
        pszExts = RemoveExt(pszExts, p);
        p = wcstok_s(NULL, L"/", &context);
    }
    free(pszTmpExts);
    return pszExts;
}
//////////////////////////////////////////////////////////////////////////////
static wchar_t* __fastcall GetSupportExts(const wchar_t* cszExtsDS, const wchar_t* cszExtsMF)
{//cszExtsDS に cszExtsMF を重複なしで連結
 //戻り値は呼び出し側が free する
    wchar_t* pszSupportExts = _wcsdup(cszExtsDS);
    wchar_t* pszExtsMF = _wcsdup(cszExtsMF);
    wchar_t* context;
    wchar_t* p = wcstok_s(pszExtsMF, L"/", &context);
    while (p) {
        pszSupportExts = AddExt(pszSupportExts, p);
        p = wcstok_s(NULL, L"/", &context);
    }
    //pszSupportExts = AddExt(pszSupportExts, L".wav");//kbdshow.kpi と異なり wav は含めない
    free(pszExtsMF);
    return pszSupportExts;
}
//////////////////////////////////////////////////////////////////////////////
static bool __fastcall IsSupportExt(const wchar_t* cszExts, const wchar_t* cszExt)
{//cszExts に cszExt を含めば true を返す
    wchar_t* pszExts = _wcsdup(cszExts);
    wchar_t* context;
    wchar_t* p = wcstok_s(pszExts, L"/", &context);
    while (p) {
        if (_wcsicmp(p, cszExt) == 0) {
            free(pszExts);
            return true;
        }
        p = wcstok_s(NULL, L"/", &context);
    }
    free(pszExts);
    return false;
}
//////////////////////////////////////////////////////////////////////////////
BOOL __fastcall kbmov_EnumConfig(IKpiConfig* pConfig, IKpiConfigEnumerator* pEnumerator)
{
    CheckVistaOrLater();
    KPI_CFG_KEY key[] = {
        //   type, section, key, keydesc, default, min, max, list, listdesc, help
        {KPI_CFG_TYPE_EXT, SEC_GENERAL, KEY_SUPPORTEXT, L"対応拡張子",
            NULL, NULL, L"1024", //default, min, max
            NULL, //list
            NULL, //listdesc
            L"本プラグインの対応拡張子です。\r\n"
            L"「対応拡張子(DS)」,「対応拡張子(MF)」,「DirectShowを使う」,「MediaFoundationを使う」\r\n"
            L"の設定に基づいて決定されます。\r\n"
#ifdef ENABLE_TEST_MODE
            L"「動作確認モード」が true のときは「対応拡張子(動作確認用)」になります。\r\n"
#endif
            L"\r\n"
            L"この設定を編集することは出来ません。\r\n"
            L"拡張子は「対応拡張子(DS)」「対応拡張子(MF)」の方で指定して下さい。\r\n"
            L""},//help
        {KPI_CFG_TYPE_EXT, SEC_GENERAL, KEY_SUPPORTEXT_DS, L"対応拡張子(DS)",
            SZ_SUPPORTEXT_DS_DEFAULT, NULL, L"1024", //default, min, max
            SZ_SUPPORTEXT_DS_LIST, //list
            NULL, //listdesc
            L"「DirectShowを使う」が true のとき、指定した拡張子を DirectShow で再生します。\r\n"
            L"\r\n"
            L"K-Lite Codec Pack や LAV Filters 等の DirectShow フィルタを導入することにより\r\n"
            L"再生可能な形式を増やすことが出来ます。\r\n"
            L""},//help
        {KPI_CFG_TYPE_EXT, SEC_GENERAL, KEY_SUPPORTEXT_MF, L"対応拡張子(MF)",
            SZ_SUPPORTEXT_MF_DEFAULT, NULL, L"1024", //default, min, max
            SZ_SUPPORTEXT_MF_DEFAULT, //list
            NULL, //listdesc
            L"「MediaFoundationを使う」が true のとき、指定した拡張子を MediaFoundation で再生します。\r\n"
            L"\r\n"
            L"DirectShow フィルタを導入しても対応形式が増えることはないようです。\r\n"
            L"OS のバージョンアップ等により対応形式が増えるかもしれません。\r\n"
            L"\r\n"
            L"MediaFoundation は Windows XP では使用出来ません。\r\n"
            L""},//help
#ifdef ENABLE_TEST_MODE
        {KPI_CFG_TYPE_EXT, SEC_GENERAL, KEY_SUPPORTEXT_FOR_TEST, L"対応拡張子(動作確認用)",
            SZ_SUPPORTEXT_FOR_TEST_DEFAULT, NULL, L"1024", //default, min, max
            NULL, //SUPPORTEXT_DEFAULT "\t" SUPPORTEXT_DEFAULT "/.ac3", //list
            NULL, //listdesc
            L"「動作確認モード」が true のとき、指定した拡張子を「対応拡張子」に設定します。\r\n"
            L"既定では標準同梱のプラグインが対応する拡張子も含めています。\r\n"
            L"通常はこの設定を変更する必要はありません。\r\n"
            L""},//help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_TEST, L"動作確認モード",
            SZ_TEST_DEFAULT, NULL, NULL, //default, min, max
            NULL, //list
            NULL, //listdesc
            L"true にすると「対応拡張子(動作確認用)」を対応拡張子とします。\r\n"
            L"通常は false に設定します。\r\n"
            L"\r\n"
            L"true にするとプラグイン使用優先度が最も高くなるようにプラグインの\r\n"
            L"バージョン番号を設定し直します。\r\n"
            L"「最新の情報に更新」することで使用優先度が確実に反映されます。\r\n"
            L"主に DirectShow フィルタを使った再生が出来るかの動作確認用です。\r\n"
            L"\r\n"
            L"例えば .mp4 を DS/MF どちらで再生するか判断したいときは以下の手順となります。\r\n"
            L"\r\n"
            L"・「対応拡張子(動作確認用)」に .mp4 を追加\r\n"
            L"・「動作確認モード」を true にする\r\n"
            L"・「DirectShowを使う」を true, 「MediaFoundationを使う」を false にして再生\r\n"
            L"・「DirectShowを使う」を false, 「MediaFoundationを使う」を true にして再生\r\n"
            L"・再生出来る場合は再生品質が気に入った方を「対応拡張子(DS/MF)」に追加\r\n"
            L"・再生出来ない場合は再生出来ない方の「対応拡張子(DS/MF)」から除外\r\n"
            L"・動作確認モードを false に戻す\r\n"
            L""
        },//help
#endif
        { KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_DISABLE_QT, L"QuickTime無効化",
            SZ_DISABLE_QT_DEFAULT, NULL, NULL, //default, min, max
            NULL, //list
            NULL, //listdesc
            L"true にすると QuickTime 関係の拡張子(.qt/.mov)を対応拡張子から外します。\r\n"
            L"QuickTime 形式を kbqt.kpi で再生したいときは true にします。\r\n"
            L"対応拡張子(DS/MF) に .qt/.mov を追加していない場合、この設定は無視されます。\r\n"
            L"\r\n"
            L"kbdshow.kpi にも同じ設定があります。QuickTime 形式を kbqt.kpi で再生するには\r\n"
            L"kbdshow.kpi の方でも true にして下さい。\r\n"
            L""
        },//help
        { KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_USE_DS, L"DirectShowを使う",
            SZ_USE_DS_DEFAULT, NULL, NULL, //default, min, max
            NULL, //list
            NULL, //listdesc
            L"true にすると「対応拡張子(DS)」の再生に DirectShow を使用します。\r\n"
            L"\r\n"
            L"「DirectShowを使う」と「MediaFoundationを使う」がともに true で\r\n"
            L"「対応拡張子(DS/MF)」の両方で指定している拡張子を再生する場合は\r\n"
            L"「DS/MF優先」の設定に従います。\r\n"
            L"\r\n"
            L"MP4 関係(.3gp/.3g2/.m4v/.mp4)は別途 K-Lite Codec Pack や LAV Filters \r\n"
            L"等の DirectShow フィルタを導入しないと再生出来ないようです。\r\n"
            L"\r\n"
            L"MP4 関係を DS で再生出来ない場合は MF で再生して下さい。\r\n"
            L""
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_USE_MF, L"MediaFoundationを使う",
            SZ_USE_MF_DEFAULT, NULL, NULL, //default, min, max
            NULL, //list
            NULL, //listdesc
            L"true にすると「対応拡張子(MF)」の再生に Media Foundation を使用します。\r\n"
            L"\r\n"
            L"「DirectShowを使う」と「MediaFoundationを使う」がともに true で\r\n"
            L"「対応拡張子(DS/MF)」の両方で指定している拡張子を再生する場合は\r\n"
            L"「DS/MF優先」の設定に従います。\r\n"
            L"\r\n"
            L"MediaFoundation は Windows XP では使用出来ません。\r\n"
            L""
            },//help
        { KPI_CFG_TYPE_INT, SEC_GENERAL, KEY_PRIOR_DS_MF, L"DS/MF優先",
            SZ_PRIOR_DS_MF_DEFAULT, NULL, NULL, //default, min, max
            L"0\t1\t2\t3", //list
            L"MF=>DS\tDS=>MF\tMF only\tDS only", //listdesc
            L"「DirectShowを使う」と「MediaFoundationを使う」がともに true で\r\n"
            L"「対応拡張子(DS/MF)」の両方で指定している拡張子を DS/MF どちらで\r\n"
            L"再生するかの設定です。\r\n"
            L"\r\n"
            L"0: MF で開いて失敗したら DS で開きます。(MF 優先)\r\n"
            L"1: DS で開いて失敗したら MF で開きます。(DS 優先)\r\n"
            L"2: MF で開きます。失敗しても DS で開きません。\r\n"
            L"3: DS で開きます。失敗しても MF で開きません。\r\n"
            L"\r\n"
            L"DS/MF どちらで再生しているかは動画ウィンドウのタイトルバーの表示(末尾のDS/MF)\r\n"
            L"で判別することが出来ます。(動画でない場合は判別出来ません)\r\n"
            L""
        },//help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_OPEN_FROM_STREAM, L"ストリームから開く",
            SZ_OPEN_FROM_STREAM_DEFAULT, NULL, NULL, //default, min, max
            NULL, //list
            NULL, //listdesc
            L"true にすると DirectShow 使用時にファイルをストリームから開きます。\r\n"
            L"MediaFoundation 使用時は常にストリームから開きます。\r\n"
            L"\r\n"
            L"アーカイブ内の曲を再生時、アーカイブプラグインが対応していれば\r\n"
            L"一時ファイルを作らずにアーカイブを展開しながら、またはメモリ上に展開\r\n"
            L"して再生することが出来るようになります。\r\n"
            L"\r\n"
            L"false にすると DirectShow 使用時に実ファイルから開きます。\r\n"
            L"アーカイブ内の曲を再生時は常に一時ファイルが作成されます。\r\n"
            L"\r\n"
            L"true にすると対象データが実ファイル(アーカイブでない)でも影響します。\r\n"
            L"ストリームから開く処理は複雑な為、true にすると再生出来ない形式がある\r\n"
            L"かもしれません。その場合は false にして下さい。\r\n"
            L""
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_VIDEO, L"動画の映像を再生",
            SZ_VIDEO_DEFAULT, NULL, NULL, //default, min, max
            NULL, //list
            NULL, //listdesc
            L"true にすると動画を再生時に映像を再生します。\r\n"
            L"\r\n"
            L"false にすると動画を再生時に音声だけを再生します。映像の処理は\r\n"
            L"行わないので再生時の負荷が低くなります。\r\n"
            L"\r\n"
            L""
            },//help
        {KPI_CFG_TYPE_BOOL, SEC_GENERAL, KEY_FULLSCREEN, L"最大化時フルスクリーン",
            SZ_FULLSCREEN_DEFAULT, NULL, NULL, //default, min, max
            NULL, //list
            NULL, //listdesc
            L"true にすると動画再生時にウィンドウを最大化したらフルスクリーンで表示します。\r\n"
            L"\r\n"
            L"何かキーを押すかクリックすると元のサイズに戻ります。\r\n"
            L""
            },//help
        {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
    };
    for (int i = 0; key[i].cszSection; i++) {
        pEnumerator->EnumKey(&key[i]);
    }
    KPI_CFG_KEY key_video_renderer = 
    { KPI_CFG_TYPE_STR, SEC_GENERAL, KEY_VIDEO_RENDERER, L"ビデオレンダラー",
            VIDEO_RENDERER_DEFAULT, NULL, L"128", //default, min, max //min か max が非NULL だと選択肢以外も入力できる
            NULL, //list
            NULL, //listdesc
            L"DirectShow 使用時のビデオレンダラーを指定します。\r\n"
            L"MediaFoundation 使用時は常に「Enhanced Video Renderer(EVR)」が使用されます。\r\n"
            L"\r\n"
            L"Windows Vista 以降では Video Mixing Renderer 9(VMR9) か Enhanced Video Renderer(EVR) "
            L"を使用することをお勧めします。VMR7 や Default Video Renderer を使用すると画面が"
            L"真っ黒になって映像が表示されなくなることがあるため、お勧めしません。\r\n"
            L"\r\n"
            L"madVR をインストールしている場合は madVR がお勧めです。\r\n"
            L"動画を高品質な画質で再生出来るようになります。\r\n"
            L"\r\n"
            L"Windows XP では VMR9/EVR は使えないので Video Mixing Renderer 7(VMR7) か Default Video Renderer を選択して下さい。"
            L"Default Video Renderer は通常は VMR7 と同等です。\r\n"
            L"\r\n"
            L"\r\n"
            L"選択肢にないレンダラーは CLSID を直接記述することで使用出来る可能性があります。\r\n"
            L"システムにインストールされてるが対応しないビデオレンダラーを使用した場合は"
            L"ウィンドウは表示されるが画面には映像が映らないような状況になります。\r\n"
            L"\r\n"
            L"システムにインストールされていないビデオレンダラーを指定した場合、動画の"
            L"映像部分は再生されません。\r\n"
            L"" //help
    };
    wchar_t szVR_List[1024];
    wchar_t szVR_ListDesc[1024];
    szVR_List[0] = szVR_ListDesc[0] = 0;
    for(int i = 0; VIDEO_RENDERERS[i].cszGuid; i++){
        if(i != 0){
            wcsncat_s(szVR_List, L"\t", _TRUNCATE);
            wcsncat_s(szVR_ListDesc, L"\t", _TRUNCATE);
        }
        wcsncat_s(szVR_List, VIDEO_RENDERERS[i].cszGuid, _TRUNCATE);
        wcsncat_s(szVR_ListDesc, VIDEO_RENDERERS[i].cszName, _TRUNCATE);
    }
    key_video_renderer.cszList = szVR_List;
    key_video_renderer.cszListDesc = szVR_ListDesc;
    pEnumerator->EnumKey(&key_video_renderer);
    return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
DWORD __fastcall kbmov_ApplyConfig(IKpiConfig* pConfig,
    const wchar_t* cszSection, //セクション名
    const wchar_t* cszKey, //キー名
    INT64  nValue, //変更後の値(BOOL/INT の場合)
    double dValue, //変更後の値(FLOAT の場合)
    const wchar_t* cszValue)//変更後の値(文字列)
{
    if (wcscmp(cszSection, SEC_GENERAL) == 0) {
        if (wcscmp(cszKey, KEY_SUPPORTEXT) == 0) {//編集不可
            return KPI_CFGRET_OK;
        }
        if (wcscmp(cszKey, KEY_SUPPORTEXT_DS) == 0 ||
            wcscmp(cszKey, KEY_SUPPORTEXT_MF) == 0
#ifdef ENABLE_TEST_MODE
            || wcscmp(cszKey, KEY_SUPPORTEXT_FOR_TEST) == 0
#endif
            ) {
            if (cszValue[0]) {
                pConfig->SetStr(cszSection, cszKey, cszValue);
            }
            else if (wcscmp(cszKey, KEY_SUPPORTEXT_DS) == 0) {
                pConfig->SetStr(cszSection, cszKey, SZ_SUPPORTEXT_DS_DEFAULT1);
            }
            else if (wcscmp(cszKey, KEY_SUPPORTEXT_MF) == 0) {
                pConfig->SetStr(cszSection, cszKey, SZ_SUPPORTEXT_MF_DEFAULT);
            }
#ifdef ENABLE_TEST_MODE
            else if (wcscmp(cszKey, KEY_SUPPORTEXT_FOR_TEST) == 0) {
                pConfig->SetStr(cszSection, cszKey, SZ_SUPPORTEXT_FOR_TEST_DEFAULT);
            }
#endif
            return KPI_CFGRET_RELOAD_KPI;//対応拡張子に関わるのでプラグインの再ロードが必要
        }
        else if (wcscmp(cszKey, KEY_USE_DS) == 0 ||
                 wcscmp(cszKey, KEY_USE_MF) == 0 ||
                 wcscmp(cszKey, KEY_DISABLE_QT) == 0 ||
                 wcscmp(cszKey, KEY_PRIOR_DS_MF) == 0
#ifdef ENABLE_TEST_MODE
            || wcscmp(cszKey, KEY_TEST) == 0
#endif
            ) {
            pConfig->SetStr(cszSection, cszKey, cszValue);
            return KPI_CFGRET_RELOAD_KPI;//対応拡張子に関わるのでプラグインの再ロードが必要
        }
        else if (wcscmp(cszKey, KEY_OPEN_FROM_STREAM) == 0 ||
            wcscmp(cszKey, KEY_FULLSCREEN) == 0 ||
            wcscmp(cszKey, KEY_VIDEO) == 0) {
            pConfig->SetStr(cszSection, cszKey, cszValue);
            return KPI_CFGRET_RELOAD_DATA;
        }
        else if (wcscmp(cszKey, KEY_VIDEO_RENDERER) == 0) {
            GUID guid = GUID_NULL;
            CLSIDFromString(cszValue, &guid);
            wchar_t szGuid[128];
            StringFromGUID2(guid, szGuid, _countof(szGuid));
            pConfig->SetStr(cszSection, cszKey, szGuid);
            return KPI_CFGRET_RELOAD_DATA;
        }
        else if (!cszKey[0]) {
            //General セクション内の全てのキーに対して ApplyConfig が呼ばれた後に呼ばれる
            //ここで対応拡張子を決定する
            bool bTestMode;
            wchar_t* pszSupportExts = kbmov_GetSupportExts(pConfig, &bTestMode);
            //pConfig->SetStr(cszSection, KEY_SUPPORTEXT, pszSupportExts);//kbmov_GetSupportExts で設定される
            free(pszSupportExts);
        }
    }
    return KPI_CFGRET_OK;
}
//////////////////////////////////////////////////////////////////////////////
void __fastcall kbmov_GetSettings(IKpiConfig* pConfig, KbMov_Settings* pSettings)
{
    CheckVistaOrLater();//VIDEO_RENDERER_DEFAULT を設定
    pSettings->bOpenFromStream = pConfig->GetInt(SEC_GENERAL, KEY_OPEN_FROM_STREAM, OPEN_FROM_STREAM_DEFAULT) != 0;
    pSettings->bVideo = pConfig->GetInt(SEC_GENERAL, KEY_VIDEO, VIDEO_DEFAULT) != 0;
    pSettings->bFullScreen = pConfig->GetInt(SEC_GENERAL, KEY_FULLSCREEN, FULLSCREEN_DEFAULT) != 0;
    GUID guid = GUID_NULL;
    wchar_t szGuid[128];
    pConfig->GetStr(SEC_GENERAL, KEY_VIDEO_RENDERER, szGuid, sizeof(szGuid), VIDEO_RENDERER_DEFAULT);
    CLSIDFromString(szGuid, &pSettings->guidVideoRenderer);
}
//////////////////////////////////////////////////////////////////////////////
wchar_t* __fastcall kbmov_GetSupportExts(IKpiConfig* pConfig, bool* pbTest)
{//対応拡張子を取得
 //KEY_SUPPPORTEXT の内容は読まない
    wchar_t* pszSupportExts;
#ifdef ENABLE_TEST_MODE
    bool bTestMode = pConfig->GetInt(SEC_GENERAL, KEY_TEST, TEST_DEFAULT) != 0;
    if (bTestMode) {//動作確認モード
        *pbTest = true;
        wchar_t szSupportExts[1024];
        pConfig->GetStr(SEC_GENERAL, KEY_SUPPORTEXT_FOR_TEST, szSupportExts, sizeof(szSupportExts), SZ_SUPPORTEXT_FOR_TEST_DEFAULT);
        pszSupportExts = GetSupportExts(szSupportExts, L"");//常に .wav を含める為に必要
    }
    else
#endif
    {//対応拡張子DS/MF 及び UseDS と UseMF に基づいて決定
        *pbTest = false;
        bool bUseDS = pConfig->GetInt(SEC_GENERAL, KEY_USE_DS, USE_DS_DEFAULT) != 0;
        bool bUseMF = pConfig->GetInt(SEC_GENERAL, KEY_USE_MF, USE_MF_DEFAULT) != 0;
        wchar_t szExtsDS[1024];
        wchar_t szExtsMF[1024];
        szExtsDS[0] = szExtsMF[0] = 0;
        if (bUseDS) {
            pConfig->GetStr(SEC_GENERAL, KEY_SUPPORTEXT_DS, szExtsDS, sizeof(szExtsDS), SZ_SUPPORTEXT_DS_DEFAULT);
        }
        if (bUseMF) {
            pConfig->GetStr(SEC_GENERAL, KEY_SUPPORTEXT_MF, szExtsMF, sizeof(szExtsMF), SZ_SUPPORTEXT_MF_DEFAULT);
        }
        pszSupportExts = GetSupportExts(szExtsDS, szExtsMF);
    }
    bool bDisableQT = pConfig->GetInt(SEC_GENERAL, KEY_DISABLE_QT, DISABLE_QT_DEFAULT) != 0;
    if(bDisableQT){
        pszSupportExts = RemoveExts(pszSupportExts, L".qt/.mov");
    }
    pConfig->SetStr(SEC_GENERAL, KEY_SUPPORTEXT, pszSupportExts);//本体の表示の為に設定は書き込む
    return pszSupportExts;
}
//////////////////////////////////////////////////////////////////////////////
void __fastcall kbmov_IsSupport(IKpiConfig *pConfig, const wchar_t *cszExt, bool *pbDS, bool *pbMF, int *pPriorDS_MF)
{
    wchar_t szExts[1024];
    *pbDS = *pbMF = false;
    bool bUseDS = pConfig->GetInt(SEC_GENERAL, KEY_USE_DS, USE_DS_DEFAULT) != 0;
    bool bUseMF = pConfig->GetInt(SEC_GENERAL, KEY_USE_MF, USE_MF_DEFAULT) != 0;
#ifdef ENABLE_TEST_MODE
    bool bTestMode = pConfig->GetInt(SEC_GENERAL, KEY_TEST, TEST_DEFAULT) != 0;
    if (bTestMode) {
        if (bUseDS || bUseMF) {
            pConfig->GetStr(SEC_GENERAL, KEY_SUPPORTEXT_FOR_TEST, szExts, sizeof(szExts), SZ_SUPPORTEXT_FOR_TEST_DEFAULT);
            bool bSupport = IsSupportExt(szExts, cszExt);
            *pbDS = bUseDS && bSupport;
            *pbMF = bUseMF && bSupport;
        }
        return;
    }
#endif
    int nPriorDS_MF = pConfig->GetInt(SEC_GENERAL, KEY_PRIOR_DS_MF, PRIOR_DS_MF_DEFAULT);
    switch (nPriorDS_MF) {
    case 0: case 1: case 2: case 3:break;
    default: nPriorDS_MF = PRIOR_DS_MF_DEFAULT;
    }
    *pPriorDS_MF = nPriorDS_MF;
    if (bUseDS) {
        pConfig->GetStr(SEC_GENERAL, KEY_SUPPORTEXT_DS, szExts, sizeof(szExts), SZ_SUPPORTEXT_DS_DEFAULT);
        *pbDS = IsSupportExt(szExts, cszExt);
    }
    if (bUseMF) {
        pConfig->GetStr(SEC_GENERAL, KEY_SUPPORTEXT_MF, szExts, sizeof(szExts), SZ_SUPPORTEXT_MF_DEFAULT);
        *pbMF = IsSupportExt(szExts, cszExt);
    }
    if (*pbDS && *pbMF) {
        if (nPriorDS_MF == 2) {//MF only
            *pbDS = false;
        }
        else if (nPriorDS_MF == 3) {//DS only
            *pbMF = false;
        }
    }
}
//////////////////////////////////////////////////////////////////////////////
