#include <windows.h>
#include <stdio.h>
#include <wchar.h>

extern HINSTANCE g_hKpi;

#include "kbvgm_decoder.h"
#include "kbvgm_convert.h"
#include "kbvgm_dataloader.h"
#include "player/s98player.hpp"
#include "player/droplayer.hpp"
#include "player/vgmplayer.hpp"
#include "player/gymplayer.hpp"
#include "emu/SoundDevs.h"
#include "emu/EmuCores.h"
#include "emu/SoundEmu.h"

static void GetVersionFromPlayer(IKpiTagInfo *pTagInfo, PlayerBase *pPlayer)
{//参考：in_vgm.dll::FileInfoStorage.cpp
    wchar_t verStr[0x20] = { 0 };
    if (pPlayer->GetPlayerType() == FCC_VGM)
    {
        VGMPlayer* vgmplay = dynamic_cast<VGMPlayer*>(pPlayer);
        const VGM_HEADER* vgmhdr = vgmplay->GetFileHeader();
        _snwprintf_s(verStr, _TRUNCATE, L"VGM %X.%02X", (vgmhdr->fileVer >> 8) & 0xFF, (vgmhdr->fileVer >> 0) & 0xFF);
    }
    else if (pPlayer->GetPlayerType() == FCC_S98)
    {
        S98Player* s98play = dynamic_cast<S98Player*>(pPlayer);
        const S98_HEADER* s98hdr = s98play->GetFileHeader();

        _snwprintf_s(verStr, _TRUNCATE, L"S98 v%u", s98hdr->fileVer);
    }
    else if (pPlayer->GetPlayerType() == FCC_DRO)
    {
        DROPlayer* droplay = dynamic_cast<DROPlayer*>(pPlayer);
        const DRO_HEADER* drohdr = droplay->GetFileHeader();

        _snwprintf_s(verStr, _TRUNCATE, L"DRO v%u", drohdr->verMajor);	// DRO has a "verMinor" field, but it's always 0
    }
    else if (pPlayer->GetPlayerType() == FCC_GYM)
    {
        GYMPlayer* gymplay = dynamic_cast<GYMPlayer*>(pPlayer);
        const GYM_HEADER* gymhdr = gymplay->GetFileHeader();

        if (!gymhdr->hasHeader)
            wcsncpy_s(verStr, L"GYM", _TRUNCATE);	// raw GYM
        else if (gymhdr->uncomprSize == 0)
            wcsncpy_s(verStr, L"GYMX", _TRUNCATE);	// GYM with GYMX header, uncompressed
        else
            wcsncpy_s(verStr, L"GYMX (z)", _TRUNCATE);	// GYMX, compressed
    }
    if (verStr[0]) {
        pTagInfo->wSetValueW(L"Version", -1, verStr, -1);
    }
}
static void GetChipListFromPlayer(IKpiTagInfo *pTagInfo, PlayerBase *pPlayer)
{// FileInfoStorage::GenerateChipList
    std::vector<PLR_DEV_INFO> chipList;
    std::vector<std::string> chipLstStr;
    pPlayer->GetSongDeviceInfo(chipList);
    for (size_t curDev = 0; curDev < chipList.size(); curDev++)
    {
        const PLR_DEV_INFO& pdi = chipList[curDev];
        const char* chipName;
        unsigned int devCnt = 1;

        chipName = SndEmu_GetDevName(pdi.type, 0x01, pdi.devCfg);
        for (devCnt = 1; curDev + 1 < chipList.size(); curDev++, devCnt++)
        {
            const PLR_DEV_INFO& pdi1 = chipList[curDev + 1];
            const char* chipName1 = SndEmu_GetDevName(pdi1.type, 0x01, pdi1.devCfg);
            bool sameChip = (chipName == chipName1);	// we assume static pointers to chip names here
            if (!sameChip)
                break;
        }
        if (pdi.type == DEVID_SN76496)
        {
            if (pdi.devCfg->flags && devCnt > 1)
                devCnt /= 2;	// the T6W28 consists of two "half" chips in VGMs
        }
        if (devCnt > 1)
        {
            char numStr[0x10];
            sprintf(numStr, "%ux", devCnt);
            chipLstStr.push_back(std::string(numStr) + chipName);
        }
        else
        {
            chipLstStr.push_back(chipName);
        }
        /*
        wchar_t keyW[128];
        char valueA[128];
        //if(devCnt>1){//Chip #2 を使うデータ(ほとんどない)
        //    _snprintf_s(valueA, _TRUNCATE, "%ux%s", devCnt, chipName);
        //}
        //else{
            strncpy_s(valueA, chipName, _TRUNCATE);
        //}
        if(chipList.size() > 1){//curDev >= 0){
            _snwprintf_s(keyW, _TRUNCATE, L"Chip%u", curDev+1);
        }
        else{
            wcsncpy_s(keyW, L"Chip", _TRUNCATE);
        }
        pTagInfo->wSetValueA(keyW, -1, valueA, -1);
        */
    }
    char valueA[256];
    valueA[0] = 0;
    for(int i = 0; i < chipLstStr.size(); i++){
        if (i > 0) {
            strncat_s(valueA, ", ", _TRUNCATE);
        }
        strncat_s(valueA, chipLstStr[i].c_str(), _TRUNCATE);
    }
    pTagInfo->wSetValueA(L"Chip", -1, valueA, -1);
}

static UINT32 EmuTypeNum2CoreFCC(UINT8 chipType, UINT8 emuType, bool* isSubType)
{//playcfg.cpp より(in_vgm / VGMPlay ともに同じ)
 //修正ここから
 //ハードコーディングしないで SndEmu_GetDevDefList で取得した情報を
 //元に FCC を返す
    *isSubType = false;
    switch(chipType){
    case DEVID_YM2203: case DEVID_YM2608: case DEVID_YM2610:
        *isSubType = true;
        if (emuType == 0)
            return FCC_EMU_; //defDevDefList には MAME しかないけど 
        else if (emuType == 1)
            return FCC_MAME;
        break;
    default: break;
    }
    const DEV_DEF** defDevDefList = SndEmu_GetDevDefList(chipType);
    if(!defDevDefList){
        return 0;
    }
    int i;
    for(i = 0; defDevDefList[i]; i++){
        if(emuType == i){
            return defDevDefList[i]->coreID;
        }
    }
    return 0;
 //修正ここまで
 #if 0 //del
    if (isSubType != NULL)
        *isSubType = false;
    switch (chipType)
    {
    case DEVID_SN76496:
        if (emuType == 0)
            return FCC_MAME;
        else if (emuType == 1)
            return FCC_MAXM;
        break;
    case DEVID_YM2413:
        if (emuType == 0)
            return FCC_EMU_;
        else if (emuType == 1)
            return FCC_MAME;
        else if (emuType == 2)
            return FCC_NUKE;
        break;
    case DEVID_YM2612:
        if (emuType == 0)
            return FCC_GPGX;
        else if (emuType == 1)
            return FCC_NUKE;
        else if (emuType == 2)
            return FCC_GENS;
        break;
    case DEVID_YM2151:
        if (emuType == 0)
            return FCC_MAME;
        else if (emuType == 1)
            return FCC_NUKE;
        break;
    case DEVID_YM2203:
    case DEVID_YM2608:
    case DEVID_YM2610:
        if (isSubType != NULL)
            *isSubType = true;
        if (emuType == 0)
            return FCC_EMU_;
        else if (emuType == 1)
            return FCC_MAME;
        break;
    case DEVID_YM3812:
        if (emuType == 0)
            return FCC_ADLE;
        else if (emuType == 1)
            return FCC_MAME;
        else if (emuType == 2)
            return FCC_NUKE;
        break;
    case DEVID_YMF262:
        if (emuType == 0)
            return FCC_ADLE;
        else if (emuType == 1)
            return FCC_MAME;
        else if (emuType == 2)
            return FCC_NUKE;
        break;
    case DEVID_AY8910:
        if (emuType == 0)
            return FCC_EMU_;
        else if (emuType == 1)
            return FCC_MAME;
        break;
    case DEVID_NES_APU:
        if (emuType == 0)
            return FCC_NSFP;
        else if (emuType == 1)
            return FCC_MAME;
        break;
    case DEVID_C6280:
        if (emuType == 0)
            return FCC_OOTK;
        else if (emuType == 1)
            return FCC_MAME;
        break;
    case DEVID_QSOUND:
        if (emuType == 0)
            return FCC_CTR_;
        else if (emuType == 1)
            return FCC_MAME;
        break;
    case DEVID_SAA1099:
        if (emuType == 0)
            return FCC_VBEL;
        else if (emuType == 1)
            return FCC_MAME;
        break;
    }	// end switch(chipType) for emuCore

    return 0x00;
#endif
}
static UINT8 ConvertChipSmplModeOption(UINT8 devID, UINT8 option)
{//playcfg.cpp より(in_vgm / VGMPlay ともに同じ)
    switch (option)
    {
    case 0x00:	// native
        return DEVRI_SRMODE_NATIVE;
    case 0x01:	// highest sampling rate (native/custom)
        return DEVRI_SRMODE_HIGHEST;
    case 0x02:	// custom sample rate
        return DEVRI_SRMODE_CUSTOM;
    case 0x03:	// native for FM, highest for others
    {
        bool isFM = (devID == DEVID_YM3526 || devID == DEVID_Y8950 || devID == DEVID_YM3812 ||
            devID == DEVID_YM2413 || devID == DEVID_YMF262 || devID == DEVID_YM2151 ||
            devID == DEVID_YM2203 || devID == DEVID_YM2608 || devID == DEVID_YM2610 || devID == DEVID_YM2612);
        return isFM ? DEVRI_SRMODE_NATIVE : DEVRI_SRMODE_HIGHEST;
    }
    default:
        return 0x00;
    }
}
///////////////////////////////////////////////////////////////////////////
KbVgmDecoder::KbVgmDecoder(void)
{
    kpi_InitMediaInfo(&m_MediaInfo);
    m_pPlayer = NULL;
    m_pLoader = NULL;
    m_pFolder = NULL;
    m_pFile = NULL;
    m_pConfig = NULL;
    ZeroMemory(&m_Settings, sizeof(m_Settings));
    m_bEndPlay = 1;
}
///////////////////////////////////////////////////////////////////////////
KbVgmDecoder::~KbVgmDecoder(void)
{
    delete m_pPlayer;
    if(m_pLoader){
        DataLoader_Deinit(m_pLoader);
    }
    if(m_pFolder){
        m_pFolder->Release();
    }
    if(m_pFile){
        m_pFile->Release();
    }
    if(m_pConfig){
        m_pConfig->Release();
    }
}
///////////////////////////////////////////////////////////////////////////
DWORD __fastcall KbVgmDecoder::Open(const KPI_MEDIAINFO* cpRequest,
                                    IKpiConfig *pConfig,
                                    IKpiFile   *pFile,
                                    IKpiFolder *pFolder)
{
    kbvgm_Settings settings;
    kbvgm_GetSettings(pConfig, &settings);
    int SampleRate = 44100;
    if(settings.nSampleRate < 0){//要求無視
        SampleRate = settings.nSampleRate * -1;
    }
    else if(cpRequest && cpRequest->dwSampleRate){//要求優先
        SampleRate = cpRequest->dwSampleRate;
    }
    else{//要求なし
        SampleRate = settings.nSampleRate;
    }
    IKpiFile *pFileExtracted = ExtractGZ(pFile);
    IKpiFile *file = pFileExtracted ? pFileExtracted : pFile;
    DWORD dwSizeConverted;
    BYTE* pBufConverted = ConvertToS98(file, &dwSizeConverted);
    if (pBufConverted) {
        m_pLoader = kbvgm_dataloader_Init(pBufConverted, dwSizeConverted);
    }
    else{
        m_pLoader = kbvgm_dataloader_Init(file);
    }
    if(DataLoader_Load(m_pLoader) != 0){
        if (pFileExtracted)pFileExtracted->Release();
        return 0;
    }
    if (pFileExtracted)pFileExtracted->Release();
    m_pPlayer = new PlayerA;
    m_pPlayer->RegisterPlayerEngine(new VGMPlayer);
    m_pPlayer->RegisterPlayerEngine(new S98Player);
    m_pPlayer->RegisterPlayerEngine(new DROPlayer);
    m_pPlayer->RegisterPlayerEngine(new GYMPlayer);

    if(m_pPlayer->LoadFile(m_pLoader) != 0){//成功時0
        return 0;
    }
    m_Settings = settings;
    m_pFolder = pFolder; pFolder->AddRef();
    m_pFile = pFile; pFile->AddRef();
    m_pConfig = pConfig; pConfig->AddRef();

    const int nUnitSample = 576;//in_vgm.dll と同じにしておく
    m_pPlayer->SetFileReqCallback(KbVgmDecoder::FileRequestCallback, this);
    m_pPlayer->SetEventCallback(KbVgmDecoder::PlayerEventCallback, this);
    m_pPlayer->SetOutputSettings(SampleRate, 2, 32, nUnitSample);
    m_pPlayer->SetLoopCount(1);//曲長計算用(後の ApplyCfg_General で無限ループに設定する)
    double total_time_sec = m_pPlayer->GetTotalTime(1);
    double loop_time_sec = m_pPlayer->GetLoopTime();
    m_MediaInfo.dwSampleRate = SampleRate;
    m_MediaInfo.dwChannels = 2;
    m_MediaInfo.nBitsPerSample = 32;
    m_MediaInfo.dwSeekableFlags = KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
    m_MediaInfo.dwUnitSample = nUnitSample;//SetOutputSettings の第4引数で渡したのと同じ値
    m_MediaInfo.dwNumber = 1;
    m_MediaInfo.dwCount = 1;
    m_MediaInfo.qwLength = total_time_sec * (1000 * 10000);
    m_MediaInfo.qwLoop = loop_time_sec * (1000 * 10000);
    if (m_MediaInfo.qwLoop != 0) {
        m_MediaInfo.qwFadeOut = (UINT64)-1;
    }
    ApplyCfg_General();
    ApplyCfg_Chip();
    return 1;
}
///////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbVgmDecoder::Select(DWORD dwNumber,
                                  const KPI_MEDIAINFO **ppMediaInfo,
                                  IKpiTagInfo *pTagInfo,
                                  DWORD dwTagGetFlags)
{
    if(ppMediaInfo){
        *ppMediaInfo = NULL;
    }
    if(dwNumber > m_MediaInfo.dwCount){
        return 0;
    }
    GetTagInfo(pTagInfo, dwTagGetFlags);
    if(!ppMediaInfo){//タグ取得のみ(タイトル/アーティストのみ)
        return 1;
    }
    *ppMediaInfo = &m_MediaInfo;
    return 1;
}
///////////////////////////////////////////////////////////////////////////
UINT64 WINAPI KbVgmDecoder::Seek(UINT64 qwPosSample, DWORD dwFlag)
{
    UINT8 ret = m_pPlayer->Seek(PLAYPOS_SAMPLE, qwPosSample);
    if(m_bEndPlay)m_pPlayer->Start();
    if(ret == 0)return qwPosSample;
    return 0;
}
///////////////////////////////////////////////////////////////////////////
DWORD WINAPI KbVgmDecoder::Render(BYTE *pBuffer, DWORD dwSizeSample)
{
    const int block_align = sizeof(INT32)*2;
    DWORD dwRet = 0;
    while(!m_bEndPlay && dwSizeSample > 0){
        DWORD ret = m_pPlayer->Render(dwSizeSample * block_align, pBuffer) / block_align;
        if (ret == 0)break;
        //if(ret < dwSizeSample){//ret < dwSizeSample でも演奏終了とは限らない
        //    int t = 0;
        //}
        dwRet += ret;
        dwSizeSample -= ret;
        pBuffer += ret * block_align;
    }
    return dwRet;
}
///////////////////////////////////////////////////////////////////////////
void __fastcall KbVgmDecoder::GetTagInfo(IKpiTagInfo *pTagInfo,
                                         DWORD dwTagGetFlags)
{
    //return ;
    static const struct{
        const char *key;
        const char *key_jpn;
    }KEY_JPN[] = {
        {"TITLE",  "TITLE-JPN"},  //-JPN があればそちらを優先
        {"GAME",   "GAME-JPN"},
        {"SYSTEM", "SYSTEM-JPN"},
        {"ARTIST", "ARTIST-JPN"},
        {NULL, NULL},
    };
    static const struct{
        const char *key_from;
        const char *key_to;
    }KEY_REPLACE[] = {
        {"SYSTEM",     "Machine"}, //SYSTEM => Machine に置換
        {"ENCODED_BY", "Dumper"},  //ENCODED_BY => Dumper に置換
        {NULL, NULL}
    };
    PlayerBase *pPlayer = m_pPlayer->GetPlayer();

    const char* const* ppTags = pPlayer->GetTags();
    if (!ppTags || !ppTags[0]) {
        //libvgm(vgmplayer.cpp) では取得出来ないが本体だと取得出来るデータが存在する
        //対象データは VGM_HEADER::eofOfs と gdsOfs が一致しているので本来はデータの方が問題
        //(本体は実データのサイズは調べるが eofOfs は見ない)
        if(dwTagGetFlags & KPI_TAGGET_FLAG_GENERAL){
            GetVersionFromPlayer(pTagInfo, pPlayer);
            GetChipListFromPlayer(pTagInfo, pPlayer);
        }
        return;
    }
    pTagInfo->GetTagInfo(m_pFile, NULL, KPI_TAGTYPE_NONE, dwTagGetFlags);//本体のタグ取得ルーチンを使わない
    int i = 0;
    while(ppTags[i]){
        const char *key = ppTags[i];
        const char *value = ppTags[i+1];
        if(!value){
            break;
        }
        int j;
        //-JPN があればそちらを優先
        for(j = 0; KEY_JPN[j].key; j++){
            if(strcmp(key, KEY_JPN[j].key) != 0){
                continue;
            }
            const char* key_jpn = ppTags[i + 2];
            if (!key_jpn || strcmp(key_jpn, KEY_JPN[j].key_jpn) != 0) {
                break;
            }
            //-JPN あり
            const char* value_jpn = ppTags[i + 3];
            i += 2;
            if(value_jpn && value_jpn[0]){
                value = value_jpn;
            }
            break;
        }
        for(j = 0; KEY_REPLACE[j].key_from; j++){
            if(strcmp(key, KEY_REPLACE[j].key_from) == 0){
                key = KEY_REPLACE[j].key_to; //キー名を置き換え
                break;
            }
        }
        pTagInfo->u8SetValueU8(key, -1, value, -1);
        i += 2;
    }
    if (dwTagGetFlags & KPI_TAGGET_FLAG_GENERAL) {
        GetVersionFromPlayer(pTagInfo, pPlayer);
        GetChipListFromPlayer(pTagInfo, pPlayer);
    }
    return;
}
///////////////////////////////////////////////////////////////////////////
DATA_LOADER* KbVgmDecoder::FileRequestCallback(PlayerBase* player, const char* fileName)
{
    if(_stricmp(fileName, "yrw801.rom") == 0){
        if(!m_Settings.szYRW801_ROM[0]){
            return NULL;
        }
#if 1
        //実ファイル名から IKpiFile の取得を実装しても良いが少し面倒なので 
        //Open 時に渡される IKpiFolder に任せる
        IKpiFile* pFile = NULL;
        if(!m_pFolder->OpenFile(m_Settings.szYRW801_ROM, &pFile)){
            return NULL;
        }
        //yrw801.rom が GZ 圧縮されることは通常はないと思うが in_vgm.dll は対応しているので
        //一応対応させる
        IKpiFile *pFileExtracted = ExtractGZ(pFile);
        if(pFileExtracted){
            pFile->Release();//pGzFile 内で AddRef されている
            pFile = pFileExtracted;
        }
        DATA_LOADER* pLoader = kbvgm_dataloader_Init(pFile);
        DataLoader_Load(pLoader);
        pFile->Release();
        return pLoader;
#else
        //utils/FileLoader を使うと gzopen 等に依存して kbzlib.dll をそのままでは使えない
        //utils/MemoryLoader なら gzopen 等には依存しないが渡したメモリを自分で解放しなければならない
        //(KbVgmDecoder のインスタンスが解放されるまで保持しておかなければならない)
        //kbvgm_dataloader は GZ を直接は扱わない
        HANDLE hFile = CreateFile(m_Settings.szYRW801_ROM, GENERIC_READ,
                                  FILE_SHARE_READ | FILE_SHARE_WRITE, 
                                  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if(hFile == INVALID_HANDLE_VALUE)return NULL;
        LARGE_INTEGER liSize;
        GetFileSizeEx(hFile, &liSize);
        if(liSize.QuadPart > 1024*1024*1000){//大き過ぎ
            CloseHandle(hFile);
            return NULL;
        }
        BYTE *buf = (BYTE*)malloc(liSize.LowPart);
        DWORD dwRead = 0;
        ReadFile(hFile, buf, liSize.LowPart, &dwRead, NULL);
        CloseHandle(hFile);
        DATA_LOADER *pLoader = kbvgm_dataloader_Init(buf, dwRead);//buf は loader 内で不要になった時点で解放される
        DataLoader_Load(pLoader);
        return pLoader;
#endif
    }
    return NULL;
}
///////////////////////////////////////////////////////////////////////////
UINT8  KbVgmDecoder::PlayerEventCallback(PlayerBase* player, UINT8 evtType, void* evtParam)
{
    switch(evtType){
        case PLREVT_START: m_bEndPlay = 0;break;
        case PLREVT_END: m_bEndPlay = 1;break;
        default: break;
    }
    return 0;
}
///////////////////////////////////////////////////////////////////////////
void KbVgmDecoder::ApplyCfg_General(void)
{//playcfg.cpp の ApplyCfg_General (in_vgm)(VGMPlay には実装されていない) に相当
    const std::vector<PlayerBase*>& plrs = m_pPlayer->GetRegisteredPlayers();
    size_t curPlr;
    UINT8 retVal;
    PlayerA::Config pwCfg;

    pwCfg = m_pPlayer->GetConfiguration();
    pwCfg.masterVol = (UINT32)(0x10000 * m_Settings.dVolume  + 0.5);
    pwCfg.chnInvert = m_Settings.bSurroundSnd ? 0x02 : 0x00;
    pwCfg.loopCount = 0;//無限ループ(ループ曲の演奏終了は本体が処理)
    pwCfg.endSilenceSmpls = m_MediaInfo.dwSampleRate;//1000ms (in_vgm.dll の既定値)(設定しておかないと一部の曲の終端が途切れる)
    pwCfg.pbSpeed = 1.0;
    m_pPlayer->SetConfiguration(pwCfg);
    for (curPlr = 0; curPlr < plrs.size(); curPlr++)
    {
        PlayerBase* pBase = plrs[curPlr];
        switch (pBase->GetPlayerType())
        {
        case FCC_VGM:
        {
            VGMPlayer* vgmplay = dynamic_cast<VGMPlayer*>(pBase);
            if(!vgmplay)break;
            VGM_PLAY_OPTIONS vpOpts;
            retVal = vgmplay->GetPlayerOptions(vpOpts);
            if (retVal){
                break;
            }
            vpOpts.playbackHz = m_Settings.dwPlaybackRate;
            vpOpts.hardStopOld = false;//opts.hardStopOld;//VGMPlay.ini では初期値 false
            vgmplay->SetPlayerOptions(vpOpts);
        }
        break;
        }
    }
}
///////////////////////////////////////////////////////////////////////////
void KbVgmDecoder::ApplyCfg_Chip(void)
{//playcfg.cpp (in_vgm)(VGMPlay ApplyCfg_Chip に相当
#if 0
    const std::vector<PlayerBase*>& plrs = m_pPlayer->GetRegisteredPlayers();
    size_t curPlr;
    UINT8 retVal;
    UINT32 devID;

    if (cOpts.chipInstance != 0xFF)
        devID = PLR_DEV_ID(cOpts.chipType, cOpts.chipInstance);
    else
        devID = PLR_DEV_ID(cOpts.chipType, 0x00);
    for (curPlr = 0; curPlr < plrs.size(); curPlr++)
    {
        PlayerBase* pBase = plrs[curPlr];
        PLR_DEV_OPTS devOpts;
        UINT8 curInst;
        UINT8 curChn;

        retVal = pBase->GetDeviceOptions(devID, devOpts);
        if (retVal)
            continue;	// this player doesn't support this chip

        devOpts.emuCore[0] = cOpts.emuCore;
        devOpts.emuCore[1] = cOpts.emuCoreSub;
        devOpts.srMode = ConvertChipSmplModeOption(cOpts.chipType, gOpts.chipSmplMode);
        devOpts.resmplMode = gOpts.resmplMode;
        devOpts.smplRate = gOpts.chipSmplRate;
        devOpts.coreOpts = cOpts.addOpts;
        devOpts.muteOpts.disable = cOpts.chipDisable;
        for (curInst = 0; curInst < 2; curInst++)
        {
            devOpts.muteOpts.chnMute[curInst] = cOpts.muteMask[curInst];
            for (curChn = 0; curChn < 32; curChn++)
                devOpts.panOpts.chnPan[curInst][curChn] = cOpts.panMask[curInst][curChn];
        }
        //printf("Player %s: Setting chip options for chip 0x%02X, instance 0x%02X\n",
        //		pBase->GetPlayerName(), cOpts.chipType, cOpts.chipInstance);
        if (cOpts.chipInstance != 0xFF)
        {
            pBase->SetDeviceOptions(devID, devOpts);
        }
        else
        {
            for (curInst = 0; curInst < 2; curInst++)
                pBase->SetDeviceOptions(PLR_DEV_ID(cOpts.chipType, curInst), devOpts);
        }
    }
#endif
    //return;
    const std::vector<PlayerBase*>& plrs = m_pPlayer->GetRegisteredPlayers();

    for (size_t curChp = 0; curChp < CHIP_LIST_COUNT; curChp++)
    {
        UINT32 chip_type = CHIP_LIST[curChp].chip_type;
        UINT32 devID;// = PLR_DEV_ID(chip_type, 0); 

        size_t curPlr;
        for (curPlr = 0; curPlr < plrs.size(); curPlr++)
        {
            PlayerBase* pBase = plrs[curPlr];
            for(int curInst = 0; curInst < 2; curInst++)
            {//in_vgm.dll で言うところの Chip #1 と Chip #2 のそれぞれに対して適用
                devID = PLR_DEV_ID(chip_type, curInst);//curInst==0: Chip#1, curInst==1: Chip#2
                PLR_DEV_OPTS devOpts;
                UINT8 retVal;

                retVal = pBase->GetDeviceOptions(devID, devOpts);
                if (retVal)
                    continue;	// this player doesn't support this chip
                bool subType;
                if (m_Settings.nChipInstance == 1 && curInst == 1) {
                    devOpts.muteOpts.disable = 0xFF;
                }
                else if (m_Settings.nChipInstance == 2 && curInst == 0) {
                    devOpts.muteOpts.disable = 0xFF;
                }
                else if(m_Settings.dwEmuCore[curChp] > 0){
                    UINT32 coreFCC = EmuTypeNum2CoreFCC(devID, m_Settings.dwEmuCore[curChp]-1, &subType);
                    if(subType){//YM2203, YM2608, YM2610
                        devOpts.emuCore[1] = coreFCC;
                    }
                    else{
                        devOpts.emuCore[0] = coreFCC;
                    }
                }
                else{
                    devOpts.muteOpts.disable = 0xFF;
                }
                devOpts.srMode = ConvertChipSmplModeOption(chip_type, m_Settings.dwChipSmplMode);
                devOpts.resmplMode = m_Settings.dwResamplMode;
                devOpts.smplRate = m_Settings.dwChipSmplRate;
                //devOpts.coreOpts = cOpts.addOpts;
                //devOpts.muteOpts.disable = cOpts.chipDisable;
                /*
                for (curInst = 0; curInst < 2; curInst++)
                {
                    devOpts.muteOpts.chnMute[curInst] = cOpts.muteMask[curInst];
                    for (curChn = 0; curChn < 32; curChn++)
                        devOpts.panOpts.chnPan[curInst][curChn] = cOpts.panMask[curInst][curChn];
                }
                //printf("Player %s: Setting chip options for chip 0x%02X, instance 0x%02X\n",
                //		pBase->GetPlayerName(), cOpts.chipType, cOpts.chipInstance);
                if (cOpts.chipInstance != 0xFF)
                {
                    pBase->SetDeviceOptions(devID, devOpts);
                }
                else
                {
                    for (curInst = 0; curInst < 2; curInst++)
                        pBase->SetDeviceOptions(PLR_DEV_ID(cOpts.chipType, curInst), devOpts);
                }
                */
                pBase->SetDeviceOptions(devID, devOpts);

            }
        }
    }


}
///////////////////////////////////////////////////////////////////////////
