
#include "pch.h"
#include "BKMessageBox.h"
#include "Config.h"
#include "Platform.h"
#include "resource.h"
#include "ScreenColors_Shared.h"
#include "vkeycodes.h"

#include <VersionHelpers.h> // нужен windows kit 8.1 в нём функция IsWindowsVistaOrGreater

#pragma warning(disable:4996)

static const std::array<BK_MODEL_PARAMETERS, static_cast<int>(CONF_BKMODEL::NUMBERS)> g_mstrConfigBKModelParameters =
{
	{
		{ _T("BK-0010"),             IDS_CONFNAME_BK_0010,             BK_DEV_MPI::BK0010,      BK_DEV_MPI::NONE },
		{ _T("BK-0010-01"),          IDS_CONFNAME_BK_0010_01,          BK_DEV_MPI::BK001001,    BK_DEV_MPI::NONE },
		{ _T("BK-0010-01_MSTD"),     IDS_CONFNAME_BK_0010_01_MSTD,     BK_DEV_MPI::BK001001,    BK_DEV_MPI::MSTD10 },
		{ _T("BK-0010-01_EXT32RAM"), IDS_CONFNAME_BK_0010_01_EXT32RAM, BK_DEV_MPI::BK001001,    BK_DEV_MPI::MSTD10_EXT32 },
		{ _T("BK-0010-01_FDD"),      IDS_CONFNAME_BK_0010_01_FDD,      BK_DEV_MPI::BK001001,    BK_DEV_MPI::STD_MEM },
		{ _T("BK-0010-01_A16M"),     IDS_CONFNAME_BK_0010_01_A16M,     BK_DEV_MPI::BK001001,    BK_DEV_MPI::A16M },
		{ _T("BK-0010-01_SMK512"),   IDS_CONFNAME_BK_0010_01_SMK512,   BK_DEV_MPI::BK001001,    BK_DEV_MPI::SMK512 },
		{ _T("BK-0010-01_SAMARA"),   IDS_CONFNAME_BK_0010_01_SAMARA,   BK_DEV_MPI::BK001001,    BK_DEV_MPI::SAMARA },
		{ _T("BK-0010-01_OPTOK"),    IDS_CONFNAME_BK_0010_01_OPTOK,    BK_DEV_MPI::BK001001,    BK_DEV_MPI::OPTOK },
		{ _T("BK-0010-01_AZBK"),     IDS_CONFNAME_BK_0010_01_AZBK,     BK_DEV_MPI::BK001001,    BK_DEV_MPI::AZBK },
		{ _T("BK-0011"),             IDS_CONFNAME_BK_0011,             BK_DEV_MPI::BK0011,      BK_DEV_MPI::MSTD11 },
		{ _T("BK-0011_FDD"),         IDS_CONFNAME_BK_0011_FDD,         BK_DEV_MPI::BK0011,      BK_DEV_MPI::STD_253 },
		{ _T("BK-0011_A16M"),        IDS_CONFNAME_BK_0011_A16M,        BK_DEV_MPI::BK0011,      BK_DEV_MPI::A16M },        // осторожно! А16М использует монитор БК11М
		{ _T("BK-0011_SMK512"),      IDS_CONFNAME_BK_0011_SMK512,      BK_DEV_MPI::BK0011,      BK_DEV_MPI::SMK512 },      // осторожно! СМК 512 использует монитор БК11М
		{ _T("BK-0011_SAMARA"),      IDS_CONFNAME_BK_0011_SAMARA,      BK_DEV_MPI::BK0011,      BK_DEV_MPI::SAMARA },
		{ _T("BK-0011M"),            IDS_CONFNAME_BK_0011M,            BK_DEV_MPI::BK0011M,     BK_DEV_MPI::MSTD11M },
		{ _T("BK-0011M_FDD"),        IDS_CONFNAME_BK_0011M_FDD,        BK_DEV_MPI::BK0011M,     BK_DEV_MPI::STD_326 },
		{ _T("BK-0011M_A16M"),       IDS_CONFNAME_BK_0011M_A16M,       BK_DEV_MPI::BK0011M,     BK_DEV_MPI::A16M },
		{ _T("BK-0011M_SMK512"),     IDS_CONFNAME_BK_0011M_SMK512,     BK_DEV_MPI::BK0011M,     BK_DEV_MPI::SMK512 },
		{ _T("BK-0011M_SAMARA"),     IDS_CONFNAME_BK_0011M_SAMARA,     BK_DEV_MPI::BK0011M,     BK_DEV_MPI::SAMARA },
		{ _T("BK-0011M_AZBK"),       IDS_CONFNAME_BK_0011M_AZBK,       BK_DEV_MPI::BK0011M,     BK_DEV_MPI::AZBK }
	}
};

static const std::array<int, SSR_LIST_SIZE> g_EnabledSSR =
{
	DEFAULT_SOUND_SAMPLE_RATE,
	48000,
	96000,
	192000
};

static const std::array<SoundChipModelList, SCHM_LIST_SIZE> g_EnabledSoundChipModels =
{
	{
		{ PSG_MODEL::AY_3_8910, IDS_CONFAY_AY38910 },
		{ PSG_MODEL::YM2149,    IDS_CONFAY_YM2149F }
	}
};

static const std::array<SoundChipFreqlList, SCHFRQ_LIST_SIZE> g_SoundChipFrequences =
{
	{
		{ 1774400,                          IDS_SNDCHPFRQ_1774400 },
		{ 1500000,                          IDS_SNDCHPFRQ_1500000 },
		{ DEFAULT_EMU_SOUNDCHIP_FREQUENCY,  IDS_SNDCHPFRQ_1714286 },
		{ 1789772,                          IDS_SNDCHPFRQ_1789772 }
	}
};

static const std::array<MultiSoundChipWorkMode, MSCHWM_LIST_SIZE> g_MultiSoundChipWorkModes =
{
	{
		{ MULTI_PSG_MODE::GID,         IDS_MSCHWM_GID },
		{ MULTI_PSG_MODE::GRYPHON,     IDS_MSCHWM_GRYPHON },
		{ MULTI_PSG_MODE::TURBOSOUND,  IDS_MSCHWM_TURBOSOUND }
	}
};


static const CString g_ConsoleName = _T("Консоль");
static const CString g_strEmptyUnit = _T("<empty>");
static const std::array<CString, FDD_DRIVE::NUM_FDD> g_mstrDrives =
{
	_T("A:"), _T("B:"), _T("C:"), _T("D:")
};
static const std::array<int, FDD_DRIVE::NUM_FDD> g_mnDrivesIndx =
{
	IDS_INI_DRIVEA, IDS_INI_DRIVEB, IDS_INI_DRIVEC, IDS_INI_DRIVED
};

static const std::array<UINT, NUMBER_VIEWS_MEM_DUMP> g_arStrDumpAddrID =
{
	IDS_INI_ADDR_DUMP_0, IDS_INI_ADDR_DUMP_1, IDS_INI_ADDR_DUMP_2, IDS_INI_ADDR_DUMP_3
};

static const std::array<UINT, NUMBER_VIEWS_MEM_DUMP> g_arStrDumpListID =
{
	IDS_INI_DMP_LIST_POS_0, IDS_INI_DMP_LIST_POS_1, IDS_INI_DMP_LIST_POS_2, IDS_INI_DMP_LIST_POS_3
};

static const std::array<CString, 16> g_arStrSMKPgNums =
{
	_T("0000"), _T("2000"), _T("0004"), _T("2004"),
	_T("0010"), _T("2010"), _T("0014"), _T("2014"),
	_T("0001"), _T("2001"), _T("0005"), _T("2005"),
	_T("0011"), _T("2011"), _T("0015"), _T("2015")
};

static const std::array<CString, 4> g_arStrMMVw1 =
{
	_T("нет"), _T("ОЗУ wo"), _T("ПЗУ"), _T("ОЗУ")
};

static const std::array<CString, 5> g_arStrMMVw_A16M =
{
	_T("нет"), _T("%d wo"), _T("ПЗУ"), _T("%d"), _T("%d ro")
};

static const std::array<CString, 5> g_arStrMMVw_SMK =
{
	_T("нет"), _T("%d.%d wo"), _T("ПЗУ"), _T("%d.%d"), _T("%d.%d ro")
};

static const std::array<CString, 4> g_arStrMMVw_AZ =
{
	_T("нет"), _T("%o wo"), _T("%o ro"), _T("%o"),
};


CConfig g_Config; // глобально доступный конфиг.


CConfig::CConfig()
{
	initVolPan();
}

CConfig::~CConfig()
{
	iniFile.Clear();
}

// инициализация конфига и всяких переменных, типа пути к проге
bool CConfig::InitConfig(const CString &strIniName)
{
	GetConfCurrPath(); // инициализация переменной m_strCurrentPath
	m_strIniFileName = m_strCurrentPath / strIniName.GetString();
	iniFile.SetIniFileName(m_strIniFileName);
	bool bNewConfig = false;

	if (!fs::exists(m_strIniFileName))
	{
		// если файла нету - создадим конфиг по умолчанию
		DefaultConfig();
		bNewConfig = true;
	}

	LoadConfig(true, false);
	return bNewConfig;
}

void CConfig::UnInitConfig()
{
	SaveConfig();
	iniFile.Clear();
}

const fs::path &CConfig::GetConfCurrPath()
{
	if (m_strCurrentPath.empty())
	{
		CString pModuleName;
		BOOL res = ::GetModuleFileName(AfxGetInstanceHandle(), pModuleName.GetBufferSetLength(_MAX_PATH), _MAX_PATH);
		pModuleName.ReleaseBuffer();

		if (res)
		{
			m_strCurrentPath = fs::path(pModuleName.GetString()).parent_path();
		}
		else
		{
			m_strCurrentPath = fs::current_path();
		}
	}

	return m_strCurrentPath;
}

// статические переменные и массивы переменных
// Rom модули
fs::path CConfig::m_strBK10_017_Monitor,
CConfig::m_strBK10_018_Focal,
CConfig::m_strBK10_084_Focal,
CConfig::m_strBK10_019_MSTD,
CConfig::m_strBK10_106_basic1,
CConfig::m_strBK10_107_basic2,
CConfig::m_strBK10_108_basic3,
CConfig::m_strBK11_201_bos,
CConfig::m_strBK11_202_ext,
CConfig::m_strBK11_203_mstd,
CConfig::m_strBK11_198_basic1,
CConfig::m_strBK11_199_basic2,
CConfig::m_strBK11_200_basic3,
CConfig::m_strBK11m_324_bos,
CConfig::m_strBK11m_325_ext,
CConfig::m_strBK11m_330_mstd,
CConfig::m_strBK11m_327_basic1,
CConfig::m_strBK11m_328_basic2,
CConfig::m_strBK11m_329_basic3,
CConfig::m_strFDD_Std,
CConfig::m_strFDD_Std253,
CConfig::m_strFDD_A16M,
CConfig::m_strFDD_SMK512,
CConfig::m_strFDD_Samara,
CConfig::m_strFDD_Optok,
CConfig::m_strBK11Pg121,
CConfig::m_strBK11Pg122,
CConfig::m_strBK11Pg131,
CConfig::m_strBK11Pg132,
CConfig::m_strBK10MSTD;
// Директории
fs::path CConfig::m_strBinPath,
CConfig::m_strToolsPath,
CConfig::m_strMemPath,
CConfig::m_strSavesPath,
CConfig::m_strTapePath,
CConfig::m_strScriptsPath,
CConfig::m_strROMPath,
CConfig::m_strIMGPath,
CConfig::m_strScreenShotsPath;


confStringParam CConfig::m_romModules[] =
{
	{ IDS_INI_BK10_RE2_017_MONITOR, &m_strBK10_017_Monitor, _T("bk10_017_mon.rom")        },
	{ IDS_INI_BK10_RE2_018_FOCAL,   &m_strBK10_018_Focal,   _T("bk10_018_focal.rom")      },
	{ IDS_INI_BK10_RE2_084_FOCAL,   &m_strBK10_084_Focal,   _T("bk10_084_focal.rom")      },
	{ IDS_INI_BK10_RE2_019_MSTD,    &m_strBK10_019_MSTD,    _T("bk10_019_mstd.rom")       },
	{ IDS_INI_BK10_RE2_106_BASIC1,  &m_strBK10_106_basic1,  _T("bk10_106_basic1.rom")     },
	{ IDS_INI_BK10_RE2_107_BASIC2,  &m_strBK10_107_basic2,  _T("bk10_107_basic2.rom")     },
	{ IDS_INI_BK10_RE2_108_BASIC3,  &m_strBK10_108_basic3,  _T("bk10_108_basic3.rom")     },
	{ IDS_INI_BK11_RE2_201_BOS,     &m_strBK11_201_bos,     _T("bk11_201_bos.rom")        },
	{ IDS_INI_BK11_RE2_202_EXT,     &m_strBK11_202_ext,     _T("bk11_202_ext.rom")        },
	{ IDS_INI_BK11_RE2_203_MSTD,    &m_strBK11_203_mstd,    _T("bk11_203_mstd.rom")       },
	{ IDS_INI_BK11_RE2_198_BASIC1,  &m_strBK11_198_basic1,  _T("bk11_198_basic1.rom")     },
	{ IDS_INI_BK11_RE2_199_BASIC2,  &m_strBK11_199_basic2,  _T("bk11_199_basic2.rom")     },
	{ IDS_INI_BK11_RE2_200_BASIC3,  &m_strBK11_200_basic3,  _T("bk11_200_basic3.rom")     },
	{ IDS_INI_BK11M_RE2_324_BOS,    &m_strBK11m_324_bos,    _T("bk11m_324_bos.rom")       },
	{ IDS_INI_BK11M_RE2_325_EXT,    &m_strBK11m_325_ext,    _T("bk11m_325_ext.rom")       },
	{ IDS_INI_BK11M_RE2_330_MSTD,   &m_strBK11m_330_mstd,   _T("bk11m_330_mstd.rom")      },
	{ IDS_INI_BK11M_RE2_327_BASIC1, &m_strBK11m_327_basic1, _T("bk11m_327_basic1.rom")    },
	{ IDS_INI_BK11M_RE2_328_BASIC2, &m_strBK11m_328_basic2, _T("bk11m_328_basic2.rom")    },
	{ IDS_INI_BK11M_RE2_329_BASIC3, &m_strBK11m_329_basic3, _T("bk11m_329_basic3.rom")    },
	{ IDS_INI_FDR,                  &m_strFDD_Std,          _T("DISK_326.rom")            },
	{ IDS_INI_FDR253,               &m_strFDD_Std253,       _T("DISK_253.rom")            },
	{ IDS_INI_FDR_A16M,             &m_strFDD_A16M,         _T("DISK_A16M_v2.41.rom")     },
	{ IDS_INI_FDR_SMK512,           &m_strFDD_SMK512,       _T("DISK_SMK512_v2.05.rom")   },
	{ IDS_INI_FDR_SAMARA,           &m_strFDD_Samara,       _T("DISK_SAMARA_HDD+FIS.rom") },
	{ IDS_INI_FDR_OPTOK,            &m_strFDD_Optok,        _T("DISK_OPTOK.rom")          },
	{ IDS_INI_BK11_RE_OPT_PG12_1,   &m_strBK11Pg121,        g_strEmptyUnit.GetString()    },
	{ IDS_INI_BK11_RE_OPT_PG12_2,   &m_strBK11Pg122,        g_strEmptyUnit.GetString()    },
	{ IDS_INI_BK11_RE_OPT_PG13_1,   &m_strBK11Pg131,        g_strEmptyUnit.GetString()    },
	{ IDS_INI_BK11_RE_OPT_PG13_2,   &m_strBK11Pg132,        g_strEmptyUnit.GetString()    },
	{ IDS_INI_BK10_OPT_MSTD,        &m_strBK10MSTD,         g_strEmptyUnit.GetString()    },
	{ 0,                            nullptr,                _T("")                        }
};

confStringParam CConfig::m_Directories[] =
{
	{ IDS_INI_PROGRAM_DIRECTORY,    &m_strBinPath,          _T("Bin")         },
	{ IDS_INI_TOOLS_DIRECTORY,      &m_strToolsPath,        _T("Tools")       },
	{ IDS_INI_MEM_DIRECTORY,        &m_strMemPath,          _T("Memory")      },
	{ IDS_INI_SAVES_DIRECTORY,      &m_strSavesPath,        _T("UserSaves")   },
	{ IDS_INI_TAPES_DIRECTORY,      &m_strTapePath,         _T("Tapes")       },
	{ IDS_INI_SCRIPTS_DIRECTORY,    &m_strScriptsPath,      _T("Scripts")     },
	{ IDS_INI_ROM_DIRECTORY,        &m_strROMPath,          _T("ROM")         },
	{ IDS_INI_IMG_DIRECTORY,        &m_strIMGPath,          _T("Img")         },
	{ IDS_INI_SSHOT_DIRECTORY,      &m_strScreenShotsPath,  _T("Screenshots") },
	{ 0,                            nullptr,                _T("") }
};

void CConfig::_intLoadConfig(const bool bLoadMain, const bool bRestore)
{
	int id{}, i = 0;

	// Инициализация директорий

	while ((id = m_Directories[i].nID) != 0)
	{
		// реализуем возможность задания произвольного пути
		fs::path strDefPath = iniFile.GetValueString(IDS_INI_SECTIONNAME_DIRECTORIES, id, m_Directories[i].defValue.c_str()).GetString();

		//если есть имя диска или "\\" в начале - то это абсолютный путь
		if (strDefPath.has_root_name())
		{
			*m_Directories[i].pstrValue = strDefPath;
		}
		else
		{
			// иначе - это относительный путь от домашней директории
			*m_Directories[i].pstrValue = GetConfCurrPath() / strDefPath;
		}

		i++;
	}

	// Инициализация параметров
	if (bLoadMain)
	{
		//      Основные параметры
		if (!bRestore)
		{
			m_strBKBoardType = iniFile.GetValueString(IDS_INI_SECTIONNAME_MAIN, IDS_INI_BKMODEL, g_mstrConfigBKModelParameters[static_cast<int>(CONF_BKMODEL::BK_0010_01)].strBKModelConfigName);
		}

		m_nScreenshotNumber = iniFile.GetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SSHOT_NUM, 1);
		m_nDateInsteadOfScreenshotNumber = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SSHOT_DATENUM, false);
		m_nScreenRenderType = static_cast<CONF_SCREEN_RENDER>(iniFile.GetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SCRRENDER_TYPE, static_cast<int>((IsWindowsVistaOrGreater()) ? CONF_SCREEN_RENDER::D2D : CONF_SCREEN_RENDER::VFW)));
		m_nOscRenderType = static_cast<CONF_OSCILLOSCOPE_RENDER>(iniFile.GetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_OSCRENDER_TYPE, static_cast<int>((IsWindowsVistaOrGreater()) ? CONF_OSCILLOSCOPE_RENDER::D2D : CONF_OSCILLOSCOPE_RENDER::OPENGL)));
		m_nSoundSampleRate = iniFile.GetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUND_SAMPLE_RATE, DEFAULT_SOUND_SAMPLE_RATE);
		m_nSoundChipFrequency = iniFile.GetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUNDCHIPFREQ, DEFAULT_EMU_SOUNDCHIP_FREQUENCY);
		m_nSoundChipModel[AY1] = static_cast<PSG_MODEL>(iniFile.GetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUNDCHIPMODEL,  static_cast<int>(PSG_MODEL::YM2149)));
		m_nSoundChipModel[AY2] = static_cast<PSG_MODEL>(iniFile.GetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUNDCHIPMODEL2, static_cast<int>(PSG_MODEL::YM2149)));
		m_n2AYWorkMode = static_cast<MULTI_PSG_MODE>(iniFile.GetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_2AYWORKMODE, static_cast<int>(MULTI_PSG_MODE::GRYPHON)));
		m_bUseLongBinFormat = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_LONGBIN, false);
		m_bOrigScreenshotSize = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ORIG_SCRNSHOT_SIZE, false);
		m_bBigButtons = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_BIGBUTTONS, false);
		m_bExclusiveOpenImages = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EXCLUSIVEOPENIMAGES, true);
		m_strFFMPEGLine = iniFile.GetValueString(IDS_INI_SECTIONNAME_MAIN, IDS_INI_FFMPEGCMDLINE, DEFAULT_FFMPEG_CMDLINE);
		m_nScreenW = iniFile.GetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SCRWIDTH, 1024);
		m_dAspectRatio = iniFile.GetValueFloat(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SCRASPECT, 4.0 / 3.0);
		m_bNativeRusLatSwitch = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_NATIVERUSLATSWITCH, true);
		m_bMMG = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEMMG, false);
		m_bEIS = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEEIS, false);
		m_bFIS = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEFIS, false);
		m_bFPU = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEFPU, false);
		m_bEnableIRPS = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLE_IRPS, false);
		m_bEmulateCBug = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_CBUG, true);
		m_bEmulate177702 = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_177702, true);
		m_bEmulateMovbPrefetchBug = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_MOVB_PREFETCH_BUG, true);
		m_bVM1G = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_VM1G, false);
		m_bEmulateEIS = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_EIS, false);
		m_bEmulateFIS = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_FIS, false);
		m_bEmulateFPU = iniFile.GetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_FPU, false);
		CheckRenders();
		CheckSSR();
		CheckSndChipFreq();
		CheckSndChipModel();
	}

	// теперь подготовим индивидуальное имя секции
	const CString strCustomize = m_strBKBoardType;
	// Инициализация Rom модулей, читаем их лишь для того, чтобы проверить наличие
	// и если их нет, то создаём
	i = 0;

	while ((id = m_romModules[i].nID) != 0)
	{
		*m_romModules[i].pstrValue = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_ROMMODULES, id, m_romModules[i].defValue.c_str()).GetString();
		i++;
	}

	//      Вариативные параметры
	m_nCPURunAddr       = Global::OctStringToWord(iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_CPU_RUN_ADDR, _T("000000")));  // если 0, то берётся значение по умолчанию для своей конфигурации
	m_nCPUFrequency     = iniFile.GetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_CPU_FREQUENCY, 0);  // если 0, то берётся значение по умолчанию для своей конфигурации
	m_nRegistersDumpInterval = iniFile.GetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_REGSDUMP_INTERVAL, 0);  // если 0, то выключено
	const CString strSoundVal = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_SOUNDVOLUME, _T("30%"));// Громкость
	m_nSoundVolume      = 0xffff * _tstoi(strSoundVal) / 100;

	for (int i = 0; i < NUMBER_VIEWS_MEM_DUMP; ++i)
	{
		m_arDumper[i].nAddr = Global::OctStringToWord(iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, g_arStrDumpAddrID[i], _T("000000")));
		CString str = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, g_arStrDumpListID[i], _T("0 : 0"));
		str.Trim();
		const int colon = str.Find(_T(':'), 0); // поищем начало разделителя

		if (colon >= 0)
		{
			CString strPagePos = str.Left(colon).Trim();
			CString strAddrPos = str.Right(str.GetLength() - colon - 1).Trim();
			m_arDumper[i].nPageListPos = Global::ToInt(strPagePos);
			m_arDumper[i].nAddrListPos = Global::ToInt(strAddrPos);
		}
		else
		{
			m_arDumper[i].nPageListPos = 0;
			m_arDumper[i].nAddrListPos = 0;
		}
	}

	m_nDisasmAddr       = Global::OctStringToWord(iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_ADDR_DISASM, _T("001000")));
	LoadPalettes(strCustomize);
	LoadJoyParams(strCustomize);
	LoadAYVolPanParams(strCustomize);
	// Инициализация опций
	m_bSavesDefault     = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SAVES_DEFAULT, false);
	m_bSpeaker          = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SPEAKER, true);
	m_bSpeakerFilter    = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SPEAKER_FILTER, true);
	m_bSpeakerDCOffset  = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SPEAKER_DCOFFSET, false);
	m_bCovox            = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX, false);
	m_bCovoxFilter      = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX_FILTER, true);
	m_bCovoxDCOffset    = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX_DCOFFSET, true);
	m_bStereoCovox      = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX_STEREO, false);
	m_bMenestrel        = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MENESTREL, false);
	m_bMenestrelFilter  = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MENESTREL_FILTER, true);
	m_bMenestrelDCOffset = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MENESTREL_DCOFFSET, false);
	m_bAY8910           = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AY8910, true);
	m_b2ndAY8910        = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_2NDAY8910, false);
	m_bAY8910Filter     = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AY8910_FILTER, true);
	m_bAY8910DCOffset   = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AY8910_DCOFFSET, false);
	m_bBKKeyboard       = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_BKKEYBOARD, true);
	m_bJCUKENKbd        = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_JCUKENKBD, false);
	m_bJoystick         = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_JOYSTICK, true);
	m_bICLBlock         = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_ICLBLOCK, false);
	m_bMouseMars        = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MOUSEM, false);
	m_bSmoothing        = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SMOOTHING, false); // параметры экрана
	m_bColorMode        = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COLOR_MODE, true);
	m_bAdaptBWMode      = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_BLACK_WHITE, false);
	m_bLuminoforeEmulMode = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_LUMINOFOREMODE, false);
	m_bFullscreenMode   = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_FULL_SCREEN, false);
	m_bPauseCPUAfterStart = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_PAUSE_CPU, false);
	m_bAskForBreak      = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_ASK_FOR_BREAK, false);
	m_bEmulateLoadTape  = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_EMUL_LOAD_TAPE, true);
	m_bEmulateSaveTape  = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_EMUL_SAVE_TAPE, true);
	m_bTapeAutoBeginDetection = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AUTO_BEG_TAPE, true);
	m_bTapeAutoEndDetection = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AUTO_END_TAPE, true);
	m_bShowPerformance  = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SHOW_PERFORMANCE_STRING, true);
	m_bEmulateFDDIO     = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_EMULATE_FDDIO, true);
	m_nVKBDType         = iniFile.GetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_VKBD_TYPE, 0);
	m_bAutodetectAYCovox = iniFile.GetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AUTODETECT_AYCOVOX, false);
	// ИРПС секция, всегда кастомная.
	m_strMapCOMPort     = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_MAPCOMPORT, g_ConsoleName);
	m_nTTYMode          = iniFile.GetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYMODE, 0);
	m_nTTYSpeed         = iniFile.GetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYSPEED, 9600);
	m_nTTYByteSize      = iniFile.GetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYBYTESIZE, 8);
	m_nTTYParity        = iniFile.GetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYPARITY, NOPARITY);
	m_nTTYStopBits      = iniFile.GetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYSTOPBITS, ONESTOPBIT);
	m_nCOMHSMode        = iniFile.GetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_COMHSMODE, 0);
	// AZBK секция.
	m_bAZWin1Off        = iniFile.GetValueBool(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZWIN1_OFF, true);
	m_bAZ037Off         = iniFile.GetValueBool(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ037_OFF, false);
	m_bAZ014Off         = iniFile.GetValueBool(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ014_OFF, false);
	m_nAZDefaultScreen  = iniFile.GetValueInt(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZDEFAULT_SCREEN, 1);
	m_strAZNetworkAdapter = iniFile.GetValueString(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZNETWORK_ADAPTER, _T(""));
	m_strAZLANDISK_DNS_NAME = iniFile.GetValueString(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ_LANDISK_DNS_NAME, _T("landisk.maxiol.com"));
	m_strAZLANDISK_EXT_PORT = iniFile.GetValueString(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ_LANDISK_EXT_PORT, _T("34162"));
	m_bAZ037OffState = false; // опции выключены.
	m_bAZ014OffState = false;
	// Инициализация приводов
	fs::path str = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVEA, g_strEmptyUnit).GetString();
	m_strFDDrives[0]    = CheckDriveImgName(str);
	str = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVEB, g_strEmptyUnit).GetString();
	m_strFDDrives[1]    = CheckDriveImgName(str);
	str = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVEC, g_strEmptyUnit).GetString();
	m_strFDDrives[2]    = CheckDriveImgName(str);
	str = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVED, g_strEmptyUnit).GetString();
	m_strFDDrives[3]    = CheckDriveImgName(str);
	str = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_HDD0, g_strEmptyUnit).GetString();
	m_strHDDrives[0]    = CheckDriveImgName(str);
	str = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_HDD1, g_strEmptyUnit).GetString();
	m_strHDDrives[1]    = CheckDriveImgName(str);

	if (m_bAY8910) // у сопра приоритет перед ковоксом
	{
		m_bCovox = false;
		m_bMenestrel = false;
	}
	else if (m_bCovox)
	{
		m_bAY8910 = false;
		m_bMenestrel = false;
	}
	else if (m_bMenestrel)
	{
		m_bAY8910 = false;
		m_bCovox = false;
	}

	if (m_bJoystick) // если включён джойстик
	{
		m_bICLBlock = false; // то блок нагрузок принудительно выключен, несмотря ни на что.
		m_bMouseMars = false; // то мышь выключена.
	}
	else if (m_bMouseMars)
	{
		m_bJoystick = false;
		m_bICLBlock = false;
	}
	else if (m_bICLBlock)
	{
		m_bJoystick = false;
		m_bMouseMars = false;
	}
}

void CConfig::CheckRenders()
{
	// проверим на корректность подсунутых нам значений из ini
	if (m_nScreenRenderType < CONF_SCREEN_RENDER::OPENGL || m_nScreenRenderType > CONF_SCREEN_RENDER::D3D)
	{
		m_nScreenRenderType = CONF_SCREEN_RENDER::OPENGL;
	}

	// проверим на корректность подсунутых нам значений из ini
	if (m_nOscRenderType < CONF_OSCILLOSCOPE_RENDER::OPENGL || m_nOscRenderType > CONF_OSCILLOSCOPE_RENDER::GDI)
	{
		m_nOscRenderType = CONF_OSCILLOSCOPE_RENDER::OPENGL;
	}

	if (!IsWindowsVistaOrGreater())
	{
		// если windows xp, то проверим на предмет поддерживаемых рендеров
		if (m_nScreenRenderType == CONF_SCREEN_RENDER::D2D) // D2D не поддерживается
		{
			m_nScreenRenderType = CONF_SCREEN_RENDER::VFW; // заменяется на DIB
		}

		// если windows xp, то проверим на предмет поддерживаемых рендеров
		if (m_nOscRenderType == CONF_OSCILLOSCOPE_RENDER::D2D) // D2D не поддерживается
		{
			m_nOscRenderType = CONF_OSCILLOSCOPE_RENDER::OPENGL; // заменяется на OGL
		}
	}
}

void CConfig::CheckSSR()
{
	bool bOk = false;

	for (const auto &i : g_EnabledSSR)
	{
		if (m_nSoundSampleRate == i)
		{
			bOk = true;
			break;
		}
	}

	if (!bOk)
	{
		m_nSoundSampleRate = DEFAULT_SOUND_SAMPLE_RATE;
	}
}

void CConfig::CheckSndChipModel()
{
	bool bOk = false;

	for (const auto &i : g_EnabledSoundChipModels)
	{
		if (m_nSoundChipModel[AY1] == i.nModelNum)
		{
			bOk = true;
			break;
		}
	}

	if (!bOk)
	{
		m_nSoundChipModel[AY1] = PSG_MODEL::YM2149;
	}

	// и повторим для второго чипа
	bOk = false;

	for (const auto &i : g_EnabledSoundChipModels)
	{
		if (m_nSoundChipModel[AY2] == i.nModelNum)
		{
			bOk = true;
			break;
		}
	}

	if (!bOk)
	{
		m_nSoundChipModel[AY2] = PSG_MODEL::YM2149;
	}
}

void CConfig::CheckSndChipFreq()
{
	// частота должна быть в пределах 1..2МГц
	if (m_nSoundChipFrequency < 1000000 || m_nSoundChipFrequency > 2000000)
	{
		// если это не так - применим частоту по умолчанию.
		m_nSoundChipFrequency = DEFAULT_EMU_SOUNDCHIP_FREQUENCY;
	}
}

/*
В конфиге есть значения только для чтения, которые не будут изменяться
и есть текущие опции, которые как раз будут изменяться.
*/
void CConfig::LoadConfig(const bool bLoadMain, const bool bRestore)
{
	_intLoadConfig(bLoadMain, bRestore);
	// объекты CPU и Debugger создаются и инициализируются до того, как вообще будет прочитана конфигурация
	// поэтому их надо переинициализировать при чтении конфига
	g_Platform.GetCPUPtr()->ReConfigCPU();
	g_Platform.GetDebuggerPtr()->InitMaps(true);
}

void CConfig::LoadConfig_FromMemory(uint8_t *pBuff, const UINT nSize)
{
	iniFile.ReadIniFromMemory(pBuff, nSize);
	_intLoadConfig(true, false);
	g_Platform.GetCPUPtr()->ReConfigCPU();
	g_Platform.GetDebuggerPtr()->InitMaps(true);
}

// сохранять в память надо только main и кастомные секции
ULONGLONG CConfig::SaveConfig_ToMemory(uint8_t *pBuff, const UINT nSize)
{
	SaveConfig();
	CIni customIni; // новый ини файл.
	CString strSectionName, strCustomSection, strCustomSuffix = _T(".") + m_strBKBoardType;
	// в память над сохранить:
	// 1. секцию Main
	strSectionName = CString(MAKEINTRESOURCE(IDS_INI_SECTIONNAME_MAIN));
	customIni.CopySection(&iniFile, strSectionName);
	// 2. секцию RomModules.Custom если есть
	strCustomSection = CString(MAKEINTRESOURCE(IDS_INI_SECTIONNAME_ROMMODULES)) + strCustomSuffix;
	customIni.CopySection(&iniFile, strCustomSection);
	// 3. секцию Parameters
	strSectionName = CString(MAKEINTRESOURCE(IDS_INI_SECTIONNAME_PARAMETERS));
	customIni.CopySection(&iniFile, strSectionName);
	// 3.1. секцию Parameters.Custom если есть
	strCustomSection = strSectionName + strCustomSuffix;
	customIni.CopySection(&iniFile, strCustomSection);
	// 4. секцию Options
	strSectionName = CString(MAKEINTRESOURCE(IDS_INI_SECTIONNAME_OPTIONS));
	customIni.CopySection(&iniFile, strSectionName);
	// 4.1. секцию Options.Custom если есть
	strCustomSection = strSectionName + strCustomSuffix;
	customIni.CopySection(&iniFile, strCustomSection);
	// 5. секцию Palettes.Custom если есть
	strCustomSection = CString(MAKEINTRESOURCE(IDS_INI_SECTIONNAME_PALETTES)) + strCustomSuffix;
	customIni.CopySection(&iniFile, strCustomSection);
	// 6. секцию Joystick Parameters.Custom если есть
	strCustomSection = CString(MAKEINTRESOURCE(IDS_INI_SECTIONNAME_JOYSTICK)) + strCustomSuffix;
	customIni.CopySection(&iniFile, strCustomSection);
	// 7. секцию ИРПС.Custom если есть
	strCustomSection = CString(MAKEINTRESOURCE(IDS_INI_SECTIONNAME_IRPS)) + strCustomSuffix;
	customIni.CopySection(&iniFile, strCustomSection);
	// 8. секцию AZBK
	strSectionName = CString(MAKEINTRESOURCE(IDS_INI_SECTIONNAME_AZBK));
	customIni.CopySection(&iniFile, strSectionName);
	// 8.1. секцию AZBK.Custom если есть
	strCustomSection = strSectionName + strCustomSuffix;
	customIni.CopySection(&iniFile, strCustomSection);
	// 9. секцию Drives.Custom если есть
	strCustomSection = CString(MAKEINTRESOURCE(IDS_INI_SECTIONNAME_DRIVES)) + strCustomSuffix;
	customIni.CopySection(&iniFile, strCustomSection);
	return customIni.FlushIniToMemory(pBuff, nSize);
}


void CConfig::SaveConfig()
{
	// теперь подготовим индивидуальное имя секции
	const CString strCustomize = m_strBKBoardType;
	// Сохранение Rom модулей - только по чтению, их не модифицируем
	int id{}, i = 0;

	while ((id = m_romModules[i].nID) != 0)
	{
		iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_ROMMODULES, id, m_romModules[i++].pstrValue->c_str());
	}

	// Сохранение директорий - только по чтению, их не модифицируем
	// Сохранение параметров
	//      Основные параметры
	iniFile.SetValueString(IDS_INI_SECTIONNAME_MAIN, IDS_INI_BKMODEL, m_strBKBoardType);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SSHOT_NUM, m_nScreenshotNumber);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SSHOT_DATENUM, m_nDateInsteadOfScreenshotNumber);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SCRRENDER_TYPE, static_cast<int>(m_nScreenRenderType));
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_OSCRENDER_TYPE, static_cast<int>(m_nOscRenderType));
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUND_SAMPLE_RATE, m_nSoundSampleRate);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUNDCHIPFREQ, m_nSoundChipFrequency);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUNDCHIPMODEL,  static_cast<int>(m_nSoundChipModel[AY1]));
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUNDCHIPMODEL2, static_cast<int>(m_nSoundChipModel[AY2]));
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_2AYWORKMODE, static_cast<int>(m_n2AYWorkMode));
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_LONGBIN, m_bUseLongBinFormat);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ORIG_SCRNSHOT_SIZE, m_bOrigScreenshotSize);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_BIGBUTTONS, m_bBigButtons);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EXCLUSIVEOPENIMAGES, m_bExclusiveOpenImages);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_MAIN, IDS_INI_FFMPEGCMDLINE, m_strFFMPEGLine);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SCRWIDTH, m_nScreenW);
	iniFile.SetValueFloat(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SCRASPECT, m_dAspectRatio);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_NATIVERUSLATSWITCH, m_bNativeRusLatSwitch);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEMMG, !!m_bMMG);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEEIS, !!m_bEIS);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEFIS, !!m_bFIS);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEFPU, !!m_bFPU);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLE_IRPS, m_bEnableIRPS);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_CBUG, m_bEmulateCBug);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_177702, m_bEmulate177702);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_MOVB_PREFETCH_BUG, m_bEmulateMovbPrefetchBug);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_VM1G, m_bVM1G);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_EIS, m_bEmulateEIS);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_FIS, m_bEmulateFIS);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_FPU, m_bEmulateFPU);
	//      Вариативные параметры
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_CPU_RUN_ADDR, Global::WordToOctString(m_nCPURunAddr));
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_CPU_FREQUENCY, m_nCPUFrequency);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_REGSDUMP_INTERVAL, m_nRegistersDumpInterval);
	// Сохраняем Громкость
	CString strSoundVal;
	auto v = static_cast<int>(static_cast<double>(m_nSoundVolume) * 100.0 / static_cast<double>(0xffff) + 0.01);
	strSoundVal.Format(_T("%d%%"), v);
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_SOUNDVOLUME, strSoundVal);

	for (int i = 0; i < NUMBER_VIEWS_MEM_DUMP; ++i)
	{
		iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, g_arStrDumpAddrID[i], Global::WordToOctString(m_arDumper[i].nAddr), true);
		CString str = Global::IntToString(m_arDumper[i].nPageListPos) + _T(" : ") + Global::IntToString(m_arDumper[i].nAddrListPos);
		iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, g_arStrDumpListID[i], str, true);
	}

	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_ADDR_DISASM, Global::WordToOctString(m_nDisasmAddr));
	SavePalettes(strCustomize);
	SaveJoyParams(strCustomize);
	SaveAYVolPanParams(strCustomize);
	// Сохранение опций
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SAVES_DEFAULT, m_bSavesDefault);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SPEAKER, m_bSpeaker);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SPEAKER_FILTER, m_bSpeakerFilter);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SPEAKER_DCOFFSET, m_bSpeakerDCOffset);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX, m_bCovox);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX_FILTER, m_bCovoxFilter);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX_DCOFFSET, m_bCovoxDCOffset);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX_STEREO, m_bStereoCovox);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MENESTREL, m_bMenestrel);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MENESTREL_FILTER, m_bMenestrelFilter);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MENESTREL_DCOFFSET, m_bMenestrelDCOffset);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AY8910, m_bAY8910);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_2NDAY8910, m_b2ndAY8910);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AY8910_FILTER, m_bAY8910Filter);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AY8910_DCOFFSET, m_bAY8910DCOffset);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_BKKEYBOARD, m_bBKKeyboard);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_JCUKENKBD, m_bJCUKENKbd);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_JOYSTICK, m_bJoystick);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_ICLBLOCK, m_bICLBlock);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MOUSEM, m_bMouseMars);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SMOOTHING, m_bSmoothing);    // параметры экрана
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COLOR_MODE, m_bColorMode);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_BLACK_WHITE, m_bAdaptBWMode);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_LUMINOFOREMODE, m_bLuminoforeEmulMode);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_FULL_SCREEN, m_bFullscreenMode);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_PAUSE_CPU, m_bPauseCPUAfterStart);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_ASK_FOR_BREAK, m_bAskForBreak);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_EMUL_LOAD_TAPE, m_bEmulateLoadTape);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_EMUL_SAVE_TAPE, m_bEmulateSaveTape);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AUTO_BEG_TAPE, m_bTapeAutoBeginDetection);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AUTO_END_TAPE, m_bTapeAutoEndDetection);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SHOW_PERFORMANCE_STRING, m_bShowPerformance);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_EMULATE_FDDIO, m_bEmulateFDDIO);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_VKBD_TYPE, m_nVKBDType);
	iniFile.SetValueBoolEx(strCustomize, IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AUTODETECT_AYCOVOX, m_bAutodetectAYCovox);
	// ИРПС секция, сохранение принудительно в кастомную секцию
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_MAPCOMPORT, m_strMapCOMPort, true);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYMODE, m_nTTYMode, true);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYSPEED, m_nTTYSpeed, true);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYBYTESIZE, m_nTTYByteSize, true);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYPARITY, m_nTTYParity, true);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYSTOPBITS, m_nTTYStopBits, true);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_IRPS, IDS_INI_COMHSMODE, m_nCOMHSMode, true);
	// AZBK секция
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZWIN1_OFF, m_bAZWin1Off);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ037_OFF, m_bAZ037Off);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ014_OFF, m_bAZ014Off);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZDEFAULT_SCREEN, m_nAZDefaultScreen);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZNETWORK_ADAPTER, m_strAZNetworkAdapter);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ_LANDISK_DNS_NAME, m_strAZLANDISK_DNS_NAME);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ_LANDISK_EXT_PORT, m_strAZLANDISK_EXT_PORT);
	// Сохранение приводов, принудительно в кастомную секцию
	SetDriveImgName(FDD_DRIVE::A, m_strFDDrives[0]); //сперва обработаем на всякий случай имена
	SetDriveImgName(FDD_DRIVE::B, m_strFDDrives[1]);
	SetDriveImgName(FDD_DRIVE::C, m_strFDDrives[2]);
	SetDriveImgName(FDD_DRIVE::D, m_strFDDrives[3]);
	SetDriveImgName(HDD_MODE::MASTER, m_strHDDrives[0]);
	SetDriveImgName(HDD_MODE::SLAVE, m_strHDDrives[1]);
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVEA, m_strFDDrives[0].c_str(), true);
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVEB, m_strFDDrives[1].c_str(), true);
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVEC, m_strFDDrives[2].c_str(), true);
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVED, m_strFDDrives[3].c_str(), true);
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_HDD0,   m_strHDDrives[0].c_str(), true);
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_DRIVES, IDS_INI_HDD1,   m_strHDDrives[1].c_str(), true);
	iniFile.FlushIni();
}

void CConfig::DefaultConfig()
{
	// идея следующая.
	// Создадим конфиг со значениями по умолчанию, и сохраним его на диске.
	// Создание Rom модулей
	int id{}, i = 0;

	while ((id = m_romModules[i].nID) != 0)
	{
		iniFile.SetValueString(IDS_INI_SECTIONNAME_ROMMODULES, id, m_romModules[i].defValue.c_str());
		i++;
	}

	// Создание директорий
	i = 0;

	while ((id = m_Directories[i].nID) != 0)
	{
		iniFile.SetValueString(IDS_INI_SECTIONNAME_DIRECTORIES, id, m_Directories[i].defValue.c_str());
		i++;
	}

	// Создание параметров
	//      Основные параметры
	iniFile.SetValueString(IDS_INI_SECTIONNAME_MAIN, IDS_INI_BKMODEL, g_mstrConfigBKModelParameters[static_cast<int>(CONF_BKMODEL::BK_0010_01)].strBKModelConfigName);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SSHOT_NUM, 1);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SSHOT_DATENUM, false);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SCRRENDER_TYPE, static_cast<int>((IsWindowsVistaOrGreater()) ? CONF_SCREEN_RENDER::D2D : CONF_SCREEN_RENDER::VFW));
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_OSCRENDER_TYPE, static_cast<int>((IsWindowsVistaOrGreater()) ? CONF_OSCILLOSCOPE_RENDER::D2D : CONF_OSCILLOSCOPE_RENDER::OPENGL));
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUND_SAMPLE_RATE, DEFAULT_SOUND_SAMPLE_RATE);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUNDCHIPFREQ, DEFAULT_EMU_SOUNDCHIP_FREQUENCY);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUNDCHIPMODEL,  static_cast<int>(PSG_MODEL::YM2149));
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SOUNDCHIPMODEL2, static_cast<int>(PSG_MODEL::YM2149));
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_2AYWORKMODE, static_cast<int>(MULTI_PSG_MODE::GRYPHON));
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_LONGBIN, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ORIG_SCRNSHOT_SIZE, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_BIGBUTTONS, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EXCLUSIVEOPENIMAGES, true);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_MAIN, IDS_INI_FFMPEGCMDLINE, DEFAULT_FFMPEG_CMDLINE);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SCRWIDTH, 1024);
	iniFile.SetValueFloat(IDS_INI_SECTIONNAME_MAIN, IDS_INI_SCRASPECT, 4.0 / 3.0);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_NATIVERUSLATSWITCH, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEMMG, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEEIS, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEFIS, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLEFPU, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_ENABLE_IRPS, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_CBUG, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_177702, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_MOVB_PREFETCH_BUG, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_VM1G, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_EIS, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_FIS, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_MAIN, IDS_INI_EMULATE_FPU, false);
	//      Вариативные параметры
	iniFile.SetValueString(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_CPU_RUN_ADDR, _T("000000"));  // если 0, то берётся значение по умолчанию для своей конфигурации
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_CPU_FREQUENCY, 0);  // если 0, то берётся значение по умолчанию для своей конфигурации
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_REGSDUMP_INTERVAL, 10);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_SOUNDVOLUME, _T("30%")); // Громкость

	for (int n = 0; n < NUMBER_VIEWS_MEM_DUMP; ++n)
	{
		iniFile.SetValueString(IDS_INI_SECTIONNAME_PARAMETERS, g_arStrDumpAddrID[n], _T("000000"));
		iniFile.SetValueString(IDS_INI_SECTIONNAME_PARAMETERS, g_arStrDumpListID[n], _T("0 : 0"));
	}

	iniFile.SetValueString(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_ADDR_DISASM, _T("001000"));
	// Создание опций
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SAVES_DEFAULT, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SPEAKER, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SPEAKER_FILTER, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SPEAKER_DCOFFSET, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX_FILTER, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX_DCOFFSET, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COVOX_STEREO, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MENESTREL, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MENESTREL_FILTER, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MENESTREL_DCOFFSET, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AY8910, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_2NDAY8910, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AY8910_FILTER, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AY8910_DCOFFSET, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_BKKEYBOARD, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_JCUKENKBD, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_JOYSTICK, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_ICLBLOCK, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_MOUSEM, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SMOOTHING, false);    // параметры экрана
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_COLOR_MODE, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_BLACK_WHITE, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_LUMINOFOREMODE, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_FULL_SCREEN, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_PAUSE_CPU, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_ASK_FOR_BREAK, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_EMUL_LOAD_TAPE, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_EMUL_SAVE_TAPE, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AUTO_BEG_TAPE, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AUTO_END_TAPE, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_SHOW_PERFORMANCE_STRING, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_EMULATE_FDDIO, true);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_VKBD_TYPE, 0);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_OPTIONS, IDS_INI_AUTODETECT_AYCOVOX, false);
	MakeDefaultPalettes();
	MakeDefaultJoyParam();
	MakeDefaultAYVolPanParam();
	// ИРПС
	iniFile.SetValueString(IDS_INI_SECTIONNAME_IRPS, IDS_INI_MAPCOMPORT, g_ConsoleName);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYMODE, 0);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYSPEED, 9600);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYBYTESIZE, 8);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYPARITY, NOPARITY);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_IRPS, IDS_INI_TTYSTOPBITS, ONESTOPBIT);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_IRPS, IDS_INI_COMHSMODE, 0);
	// AZBK
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZWIN1_OFF, true);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ037_OFF, false);
	iniFile.SetValueBool(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ014_OFF, false);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZDEFAULT_SCREEN, 1);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZNETWORK_ADAPTER, _T(""));
	iniFile.SetValueString(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ_LANDISK_DNS_NAME, _T("landisk.maxiol.com"));
	iniFile.SetValueString(IDS_INI_SECTIONNAME_AZBK, IDS_INI_AZ_LANDISK_EXT_PORT, _T("34162"));
	// Создание приводов
	iniFile.SetValueString(IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVEA, g_strEmptyUnit);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVEB, g_strEmptyUnit);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVEC, g_strEmptyUnit);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_DRIVES, IDS_INI_DRIVED, g_strEmptyUnit);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_DRIVES, IDS_INI_HDD0, g_strEmptyUnit);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_DRIVES, IDS_INI_HDD1, g_strEmptyUnit);
	iniFile.FlushIni();
}

bool CConfig::VerifyRoms()
{
	int p = 0;
	fs::path *pStr = nullptr;
	bool bRes = true;

	while ((pStr = m_romModules[p].pstrValue) != nullptr)   // берём очередной параметр
	{
		if (!pStr->empty()) // если значение параметра есть
		{
			if (!Global::isEmptyUnit(*pStr)) // и это не заглушка
			{
				const fs::path strPath = m_strROMPath / *pStr; // попробуем открыть этот файл

				if (!fs::exists(strPath))
				{
					const CString strError = CString(MAKEINTRESOURCE(IDS_ERRMSG_NOTROM));
					g_BKMsgBox.Show(strError + _T(" \'") + CString(strPath.c_str()) + _T("\'"), MB_OK | MB_ICONSTOP);
					bRes = false;
				}
			}
		}
		else
		{
			// если значения параметра нет, то тоже плохо
			const CString strError = CString(MAKEINTRESOURCE(IDS_ERRMSG_ROM_NOT_DEFINED));
			const CString str = CString(MAKEINTRESOURCE(m_romModules[p].nID));
			g_BKMsgBox.Show(strError + _T(" \'") + str + _T("\'"), MB_OK | MB_ICONSTOP);
			bRes = false;
		}

		p++;
	}

	return bRes;
}

void CConfig::SetBKBoardType(const CString &strBKBoardType)
{
	for (auto t = static_cast<int>(CONF_BKMODEL::BK_0010); t < static_cast<int>(CONF_BKMODEL::NUMBERS); ++t)
	{
		if (g_mstrConfigBKModelParameters[t].strBKModelConfigName == strBKBoardType)
		{
			SetBKModel(static_cast<CONF_BKMODEL>(t));
			return;
		}
	}

	SetBKModel(CONF_BKMODEL::BK_0010_01);
}

// возвращает номер текущей конфигурации.
// выход: если найдено, то номер, если не найдено - то 0 (значение по умолчанию)
// выдавать m_BKConfigModelNumber нельзя, т.к. это значение как раз формируется из
// результата этой функции. Вот такая-вот загогулина
CONF_BKMODEL CConfig::GetBKModel()
{
	for (auto t = static_cast<int>(CONF_BKMODEL::BK_0010); t < static_cast<int>(CONF_BKMODEL::NUMBERS); ++t)
	{
		if (g_mstrConfigBKModelParameters[t].strBKModelConfigName == m_strBKBoardType)
		{
			SetBKModel(static_cast<CONF_BKMODEL>(t));
			return static_cast<CONF_BKMODEL>(t);
		}
	}

	return CONF_BKMODEL::BK_0010_01;
}

// получение человекопонятного имени текущего конфига
const CString CConfig::GetBKConfName() const
{
	const UINT nID = g_mstrConfigBKModelParameters[static_cast<int>(m_BKConfigModelNumber)].nIDBKModelName;
	return CString(MAKEINTRESOURCE(nID));
}

void CConfig::SetBKModel(const CONF_BKMODEL n)
{
	m_BKConfigModelNumber = n;
	m_strBKBoardType = g_mstrConfigBKModelParameters[static_cast<int>(n)].strBKModelConfigName;
}


const CString CConfig::GetRomModuleName(const int nIniKey)
{
	return iniFile.GetValueStringEx(m_strBKBoardType, IDS_INI_SECTIONNAME_ROMMODULES, nIniKey, g_strEmptyUnit);
}

BK_DEV_MPI CConfig::GetBKBoardModel() const
{
	return g_mstrConfigBKModelParameters[static_cast<int>(m_BKConfigModelNumber)].nBKBoardModel;
}

BK_DEV_MPI CConfig::GetMPIDeviceModel() const
{
	return g_mstrConfigBKModelParameters[static_cast<int>(m_BKConfigModelNumber)].nMPIDeviceModel;
}

bool CConfig::isBK10() const
{
	switch (m_BKConfigModelNumber)
	{
		case CONF_BKMODEL::BK_0010:
		case CONF_BKMODEL::BK_0010_01:
		case CONF_BKMODEL::BK_0010_01_MSTD:
		case CONF_BKMODEL::BK_0010_01_EXT32RAM:
		case CONF_BKMODEL::BK_0010_01_FDD:
		case CONF_BKMODEL::BK_0010_01_A16M:
		case CONF_BKMODEL::BK_0010_01_SMK512:
		case CONF_BKMODEL::BK_0010_01_SAMARA:
		case CONF_BKMODEL::BK_0010_01_OPTOK:
		case CONF_BKMODEL::BK_0010_01_AZBK:
			return true;
	}

	return false;
}

bool CConfig::isBK11() const
{
	switch (m_BKConfigModelNumber)
	{
		case CONF_BKMODEL::BK_0011:
		case CONF_BKMODEL::BK_0011_FDD:
		case CONF_BKMODEL::BK_0011_A16M:
		case CONF_BKMODEL::BK_0011_SMK512:
		case CONF_BKMODEL::BK_0011_SAMARA:
			return true;
	}

	return false;
}

bool CConfig::isBK11M() const
{
	switch (m_BKConfigModelNumber)
	{
		case CONF_BKMODEL::BK_0011M:
		case CONF_BKMODEL::BK_0011M_FDD:
		case CONF_BKMODEL::BK_0011M_A16M:
		case CONF_BKMODEL::BK_0011M_SMK512:
		case CONF_BKMODEL::BK_0011M_SAMARA:
		case CONF_BKMODEL::BK_0011M_AZBK:
			return true;
	}

	return false;
}

int CConfig::GetDriveNum(const FDD_DRIVE::FDD_DRIVE eDrive) const
{
	switch (eDrive)
	{
		case FDD_DRIVE::A:
			return 0;

		case FDD_DRIVE::B:
			return 1;

		case FDD_DRIVE::C:
			return 2;

		case FDD_DRIVE::D:
			return 3;
	}

	return 0; // всегда возвращаем хоть что-то истинное
}

int CConfig::GetDriveNum(const HDD_MODE::HDD_MODE eDrive) const
{
	switch (eDrive)
	{
		case HDD_MODE::MASTER:
			return 0;

		case HDD_MODE::SLAVE:
			return 1;
	}

	return 0; // всегда возвращаем хоть что-то истинное
}

fs::path CConfig::CheckDriveImgName(const fs::path &str) const
{
	if (!Global::isEmptyUnit(str))
	{
		const fs::path strName = GetDriveImgName_Full(str);

		if (fs::exists(strName))
		{
			return strName;
		}
	}

	return g_strEmptyUnit.GetString();
}

fs::path CConfig::GetDriveImgName(const HDD_MODE::HDD_MODE eDrive) const
{
	const int nDrive = GetDriveNum(eDrive);
	return GetDriveImgName_Full(m_strHDDrives[nDrive]);
}

fs::path CConfig::GetDriveImgName(const FDD_DRIVE::FDD_DRIVE eDrive) const
{
	const int nDrive = GetDriveNum(eDrive);
	return GetDriveImgName_Full(m_strFDDrives[nDrive]);
}

fs::path CConfig::GetShortDriveImgName(const FDD_DRIVE::FDD_DRIVE eDrive) const
{
	const int nDrive = GetDriveNum(eDrive);
	return GetDriveImgName_Short(m_strFDDrives[nDrive]);
}

fs::path CConfig::GetShortDriveImgName(const HDD_MODE::HDD_MODE eDrive) const
{
	const int nDrive = GetDriveNum(eDrive);
	return GetDriveImgName_Short(m_strHDDrives[nDrive]);
}

fs::path CConfig::GetShortDriveImgName(const fs::path &strPathName) const
{
	return GetDriveImgName_Short(strPathName);
}

fs::path CConfig::GetDriveImgName_Full(const fs::path &str) const
{
	if (!Global::isEmptyUnit(str))
	{
		// прочитаем путь заданного привода
		if (!str.has_parent_path()) // если путь в имени образа отсутствует
		{
			// то возвращаем имя с полным путём к домашней папке
			return m_strIMGPath / str;
		}

		// если путь есть, а имени диска нету, то задан относительный путь
		if (!str.has_root_name())
		{
			// то возвращаем имя с полным путём к домашней папке
			return m_strIMGPath / str;
		}
	}

	// иначе, возвращаем имя как есть, предполагаем, что там путь верный.
	return str;
}

fs::path CConfig::GetDriveImgName_Short(const fs::path &str) const
{
	if (!Global::isEmptyUnit(str))
	{
		if (str.has_parent_path())
		{
			// теперь разберёмся с относительными путями.
			const CString ip = m_strIMGPath.c_str();
			const CString str1 = str.c_str();

			// надо из пути удалить базовый путь
			if (str1.Find(ip, 0) == 0) // если путь нашёлся, и он как и положено - в начале
			{
				// +1 потому что у путей нету замыкающего слеша
				return { str1.Mid(ip.GetLength() + 1).GetString() };
			}
		}
	}

	// иначе, возвращаем имя как есть, предполагаем, что там путь верный.
	return str;
}

void CConfig::SetDriveImgName(const HDD_MODE::HDD_MODE eDrive, const fs::path &strPathName)
{
	const int nDrive = GetDriveNum(eDrive);
	m_strHDDrives[nDrive] = GetDriveImgName_Short(strPathName);
}

void CConfig::SetDriveImgName(const FDD_DRIVE::FDD_DRIVE eDrive, const fs::path &strPathName)
{
	const int nDrive = GetDriveNum(eDrive);
	m_strFDDrives[nDrive] = GetDriveImgName_Short(strPathName);
}

// работа с палитрами

static const std::array<UINT, 16> g_arColIni =
{
	IDS_INI_PALCOL00,
	IDS_INI_PALCOL01,
	IDS_INI_PALCOL02,
	IDS_INI_PALCOL03,
	IDS_INI_PALCOL04,
	IDS_INI_PALCOL05,
	IDS_INI_PALCOL06,
	IDS_INI_PALCOL07,
	IDS_INI_PALCOL08,
	IDS_INI_PALCOL09,
	IDS_INI_PALCOL10,
	IDS_INI_PALCOL11,
	IDS_INI_PALCOL12,
	IDS_INI_PALCOL13,
	IDS_INI_PALCOL14,
	IDS_INI_PALCOL15
};

// создание палитр по умолчанию
void CConfig::MakeDefaultPalettes()
{
	// создадим стандартную обычную ЧБ палитру
	CString strPal = ::ColorToStr(g_pMonochromePalette_std[0][0])
	                 + _T(',') + ::ColorToStr(g_pMonochromePalette_std[0][1]);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_PALETTES, IDS_INI_PALBW, strPal);
	// создадим стандартную адаптивную ЧБ палитру
	strPal = ::PaletteToStr(g_pAdaptMonochromePalette_std[0]);
	iniFile.SetValueString(IDS_INI_SECTIONNAME_PALETTES, IDS_INI_PALADAPTBW, strPal);

	// создадим цветные палитры
	for (int i = 0; i < 16; ++i)
	{
		strPal = ::PaletteToStr(g_pColorPalettes_std[i]);
		iniFile.SetValueString(IDS_INI_SECTIONNAME_PALETTES, g_arColIni[i], strPal);
	}
}

// сохранение текущих палитр
void CConfig::SavePalettes(const CString &strCustomize)
{
	// сохраним стандартную обычную ЧБ палитру
	CString strPal = ::ColorToStr(g_pMonochromePalette[0][0])
	                 + _T(',') + ::ColorToStr(g_pMonochromePalette[0][1]);
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PALETTES, IDS_INI_PALBW, strPal);
	// сохраним стандартную адаптивную ЧБ палитру
	strPal = ::PaletteToStr(g_pAdaptMonochromePalette[0]);
	iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PALETTES, IDS_INI_PALADAPTBW, strPal);

	// сохраним цветные палитры
	for (int i = 0; i < 16; ++i)
	{
		strPal = ::PaletteToStr(g_pColorPalettes[i]);
		iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PALETTES, g_arColIni[i], strPal);
	}
}

// загрузка текущих палитр
void CConfig::LoadPalettes(const CString &strCustomize)
{
	// создадим стандартную обычную ЧБ палитру
	CString strPal = ::ColorToStr(g_pMonochromePalette_std[0][0])
	                 + _T(',') + ::ColorToStr(g_pMonochromePalette_std[0][1]);
	// загрузим текущую обычную ЧБ палитру
	CString strPalVal = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PALETTES, IDS_INI_PALBW, strPal);
	::StrToPalette(strPalVal, g_pMonochromePalette[0]);
	g_pMonochromePalette[0][2] = g_pMonochromePalette[1][0] = g_pMonochromePalette[1][1] = g_pMonochromePalette[0][0];
	g_pMonochromePalette[0][3] = g_pMonochromePalette[1][2] = g_pMonochromePalette[1][3] = g_pMonochromePalette[0][1];
	// создадим стандартную адаптивную ЧБ палитру
	strPal = ::PaletteToStr(g_pAdaptMonochromePalette_std[0]);
	// загрузим текущую адаптивную ЧБ палитру
	strPalVal = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PALETTES, IDS_INI_PALADAPTBW, strPal);
	::StrToPalette(strPalVal, g_pAdaptMonochromePalette[0]);
	g_pAdaptMonochromePalette[1][0] = g_pAdaptMonochromePalette[0][0];
	g_pAdaptMonochromePalette[1][1] = g_pAdaptMonochromePalette[0][1];
	g_pAdaptMonochromePalette[1][2] = g_pAdaptMonochromePalette[0][2];
	g_pAdaptMonochromePalette[1][3] = g_pAdaptMonochromePalette[0][3];

	// создадим цветные палитры
	for (int i = 0; i < 16; ++i)
	{
		strPal = ::PaletteToStr(g_pColorPalettes_std[i]);
		strPalVal = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_PALETTES, g_arColIni[i], strPal);
		::StrToPalette(strPalVal, g_pColorPalettes[i]);
	}
}

const std::array<JoyElem_t, BKJOY_PARAM_NUM> CConfig::m_arJoystick_std =
{
	{
		{_T("VK_UP"),    VK_UP,    0x48, 0001}, //BKJOY_UP = 0;
		{_T("VK_RIGHT"), VK_RIGHT, 0x4d, 0002}, //BKJOY_RIGHT = 1;
		{_T("VK_DOWN"),  VK_DOWN,  0x50, 0004}, //BKJOY_DOWN = 2;
		{_T("VK_LEFT"),  VK_LEFT,  0x4b, 0010}, //BKJOY_LEFT = 3;
		{_T("VK_HOME"),  VK_HOME,  0x47, 0040}, //BKJOY_FIRE = 4;
		{_T("VK_PRIOR"), VK_PRIOR, 0x49, 0100}, //BKJOY_ALTFIRE = 5;
		{_T("VK_END"),   VK_END,   0x4f, 0020}, //BKJOY_A = 6;
		{_T("VK_NEXT"),  VK_NEXT,  0x51, 0200}, //BKJOY_B = 7;
	}
};

static const std::array<UINT, BKJOY_PARAM_NUM> arJoyparamIni =
{
	IDS_INI_BKJOY_UP,
	IDS_INI_BKJOY_RIGHT,
	IDS_INI_BKJOY_DOWN,
	IDS_INI_BKJOY_LEFT,
	IDS_INI_BKJOY_FIRE,
	IDS_INI_BKJOY_ALTFIRE,
	IDS_INI_BKJOY_A,
	IDS_INI_BKJOY_B
};
void CConfig::MakeDefaultJoyParam()
{
	for (int i = 0; i < BKJOY_PARAM_NUM; ++i)
	{
		m_arJoystick[i] = m_arJoystick_std[i];
		const CString str = m_arJoystick[i].strVKeyName + _T(" : ") + Global::WordToOctString(m_arJoystick[i].nMask) + _T(" : ") + Global::DwordToStr(m_arJoystick[i].nFlags);
		iniFile.SetValueString(IDS_INI_SECTIONNAME_JOYSTICK, arJoyparamIni[i], str);
	}
}

// если массив параметров джойстика сделать вне класса, выделенная под него память успевает освободиться
// перед тем, как выполнится эта функция
void CConfig::SaveJoyParams(const CString &strCustomize)
{
	for (int i = 0; i < BKJOY_PARAM_NUM; ++i)
	{
		const CString str = m_arJoystick[i].strVKeyName + _T(" : ") + Global::WordToOctString(m_arJoystick[i].nMask) + _T(" : ") + Global::DwordToStr(m_arJoystick[i].nFlags);
		iniFile.SetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_JOYSTICK, arJoyparamIni[i], str);
	}
}

void CConfig::LoadJoyParams(const CString &strCustomize)
{
	for (int i = 0; i < BKJOY_PARAM_NUM; ++i)
	{
		const CString strDef = m_arJoystick_std[i].strVKeyName + _T(" : ") + Global::WordToOctString(m_arJoystick_std[i].nMask) + _T(" : ") + Global::DwordToStr(m_arJoystick_std[i].nFlags);
		CString strJoy = iniFile.GetValueStringEx(strCustomize, IDS_INI_SECTIONNAME_JOYSTICK, arJoyparamIni[i], strDef);
		// теперь парсим строку
		bool bOk = false;
		strJoy.Trim();
		int colon = strJoy.Find(_T(':'), 0); // поищем начало разделителя

		if (colon >= 0)
		{
			const CString strName = strJoy.Left(colon).Trim(); // выделим имя клавиши
			CString strMask = strJoy.Right(strJoy.GetLength() - colon - 1).Trim(); // выделим маску
			colon = strMask.Find(_T(':'), 0); // поищем начало разделителя
			UINT nFlags = 0;

			if (colon >= 0)
			{
				const CString strFlags = strMask.Right(strMask.GetLength() - colon - 1).Trim(); // выделим флаги
				strMask = strMask.Left(colon).Trim(); // выделим маску
				nFlags = Global::StrToDword(strFlags);
			}

			const UINT nVKey = ::getKeyValue(strName);

			if (nVKey != -1)
			{
				m_arJoystick[i].strVKeyName = strName;
				m_arJoystick[i].nVKey = nVKey;
				m_arJoystick[i].nFlags = nFlags;
				m_arJoystick[i].nMask = Global::OctStringToWord(strMask);
				bOk = true;
			}
		}

		if (!bOk)
		{
			// не нашлось - зададим по умолчанию
			m_arJoystick[i] = m_arJoystick_std[i];
		}
	}
}


void CConfig::SaveAYVolPanParams(const CString &strCustomize)
{
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1PANAL, m_AYParam[AY1].nPL[CHAN_A]);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1PANBL, m_AYParam[AY1].nPL[CHAN_B]);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1PANCL, m_AYParam[AY1].nPL[CHAN_C]);
	iniFile.SetValueFloatEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1VOLA, m_AYParam[AY1].V[CHAN_A]);
	iniFile.SetValueFloatEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1VOLB, m_AYParam[AY1].V[CHAN_B]);
	iniFile.SetValueFloatEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1VOLC, m_AYParam[AY1].V[CHAN_C]);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2PANAL, m_AYParam[AY2].nPL[CHAN_A]);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2PANBL, m_AYParam[AY2].nPL[CHAN_B]);
	iniFile.SetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2PANCL, m_AYParam[AY2].nPL[CHAN_C]);
	iniFile.SetValueFloatEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2VOLA, m_AYParam[AY2].V[CHAN_A]);
	iniFile.SetValueFloatEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2VOLB, m_AYParam[AY2].V[CHAN_B]);
	iniFile.SetValueFloatEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2VOLC, m_AYParam[AY2].V[CHAN_C]);
}

void CConfig::CheckParV(const CString &strCustomize, const int Sect, double &v)
{
	v = iniFile.GetValueFloatEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, Sect, AY_VOL_BASE);

	if (0 > v || v > AY_VOL_BASE)
	{
		v = AY_VOL_BASE;
	}
}

void CConfig::CheckParP(const CString &strCustomize, const int Sect, int def, int &v)
{
	v = iniFile.GetValueIntEx(strCustomize, IDS_INI_SECTIONNAME_PARAMETERS, Sect, def);

	if (0 > v || v > AY_PAN_BASE)
	{
		v = def;
	}
}

void CConfig::LoadAYVolPanParams(const CString &strCustomize)
{
	CheckParP(strCustomize, IDS_INI_AY1PANAL, AY_LEFT_PAN_DEFAULT,   m_AYParam[AY1].nPL[CHAN_A]);
	CheckParP(strCustomize, IDS_INI_AY1PANBL, AY_CENTER_PAN_DEFAULT, m_AYParam[AY1].nPL[CHAN_B]);
	CheckParP(strCustomize, IDS_INI_AY1PANCL, AY_RIGHT_PAN_DEFAULT,  m_AYParam[AY1].nPL[CHAN_C]);
	CheckParV(strCustomize, IDS_INI_AY1VOLA, m_AYParam[AY1].V[CHAN_A]);
	CheckParV(strCustomize, IDS_INI_AY1VOLB, m_AYParam[AY1].V[CHAN_B]);
	CheckParV(strCustomize, IDS_INI_AY1VOLC, m_AYParam[AY1].V[CHAN_C]);
	CheckParP(strCustomize, IDS_INI_AY2PANAL, AY_LEFT_PAN_DEFAULT,   m_AYParam[AY2].nPL[CHAN_A]);
	CheckParP(strCustomize, IDS_INI_AY2PANBL, AY_CENTER_PAN_DEFAULT, m_AYParam[AY2].nPL[CHAN_B]);
	CheckParP(strCustomize, IDS_INI_AY2PANCL, AY_RIGHT_PAN_DEFAULT,  m_AYParam[AY2].nPL[CHAN_C]);
	CheckParV(strCustomize, IDS_INI_AY2VOLA, m_AYParam[AY2].V[CHAN_A]);
	CheckParV(strCustomize, IDS_INI_AY2VOLB, m_AYParam[AY2].V[CHAN_B]);
	CheckParV(strCustomize, IDS_INI_AY2VOLC, m_AYParam[AY2].V[CHAN_C]);
}

void CConfig::MakeDefaultAYVolPanParam()
{
	initVolPan();
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1PANAL, m_AYParam[AY1].nPL[CHAN_A]);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1PANBL, m_AYParam[AY1].nPL[CHAN_B]);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1PANCL, m_AYParam[AY1].nPL[CHAN_C]);
	iniFile.SetValueFloat(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1VOLA, m_AYParam[AY1].V[CHAN_A]);
	iniFile.SetValueFloat(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1VOLB, m_AYParam[AY1].V[CHAN_B]);
	iniFile.SetValueFloat(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY1VOLC, m_AYParam[AY1].V[CHAN_C]);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2PANAL, m_AYParam[AY2].nPL[CHAN_A]);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2PANBL, m_AYParam[AY2].nPL[CHAN_B]);
	iniFile.SetValueInt(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2PANCL, m_AYParam[AY2].nPL[CHAN_C]);
	iniFile.SetValueFloat(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2VOLA, m_AYParam[AY2].V[CHAN_A]);
	iniFile.SetValueFloat(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2VOLB, m_AYParam[AY2].V[CHAN_B]);
	iniFile.SetValueFloat(IDS_INI_SECTIONNAME_PARAMETERS, IDS_INI_AY2VOLC, m_AYParam[AY2].V[CHAN_C]);
}

// выдача параметров заданного AY.
// если задать недопустимый индекс, то выдаёт значения по умолчанию. Чем можно пользоваться.
CConfig::AYVolPan_s CConfig::getVolPan(const int n) const
{
	if (0 <= n && n < AY_NUMS)
	{
		return m_AYParam[n];
	}

	return { AY_LEFT_PAN_DEFAULT, AY_CENTER_PAN_DEFAULT, AY_RIGHT_PAN_DEFAULT,
	         AY_PAN_BASE - AY_LEFT_PAN_DEFAULT, AY_PAN_BASE - AY_CENTER_PAN_DEFAULT, AY_PAN_BASE - AY_RIGHT_PAN_DEFAULT,
	         AY_VOL_BASE, AY_VOL_BASE, AY_VOL_BASE };
}

void CConfig::setVolPan(const int n, const AYVolPan_s &s)
{
	if (0 <= n && n < AY_NUMS)
	{
		m_AYParam[n] = s;
	}
}

void CConfig::initVolPan()
{
	m_AYParam[AY1] = getVolPan(-1); // инициализируем значениями по умолчанию.
	m_AYParam[AY2] = getVolPan(-1);
}



/* это правильная таблица, но на бк всё по особому, даже кои8 там нестандартный
// таблица соответствия верхней половины аскии кодов с 128 по 255, включая псевдографику
static const TCHAR koi8tbl_RFC1489[128] = {
0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, 0x2264, 0x2265, 0xA0,   0x2321, 0xB0,   0xB2,   0xB7,   0xF7,
0x2550, 0x2551, 0x2552, 0x451,  0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E,
0x255F, 0x2560, 0x2561, 0x401,  0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0xA9,
_T('ю'), _T('а'), _T('б'), _T('ц'), _T('д'), _T('е'), _T('ф'), _T('г'), _T('х'), _T('и'), _T('й'), _T('к'), _T('л'), _T('м'), _T('н'), _T('о'),
_T('п'), _T('я'), _T('р'), _T('с'), _T('т'), _T('у'), _T('ж'), _T('в'), _T('ь'), _T('ы'), _T('з'), _T('ш'), _T('э'), _T('щ'), _T('ч'), _T('ъ'),
_T('Ю'), _T('А'), _T('Б'), _T('Ц'), _T('Д'), _T('Е'), _T('Ф'), _T('Г'), _T('Х'), _T('И'), _T('Й'), _T('К'), _T('Л'), _T('М'), _T('Н'), _T('О'),
_T('П'), _T('Я'), _T('Р'), _T('С'), _T('Т'), _T('У'), _T('Ж'), _T('В'), _T('Ь'), _T('Ы'), _T('З'), _T('Ш'), _T('Э'), _T('Щ'), _T('Ч'), _T('Ъ')
};
*/
// вот бкшная таблица
// таблица соответствия верхней половины аскии кодов с 128 по 255, включая псевдографику
static const std::array<TCHAR, 128> koi8tbl =
{
	// {200..237} этих символов на бк нету.
	_T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '),
	_T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '),
	_T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '),
	_T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '), _T(' '),
	// {240..277}
	0xb6,    0x2534,  0x2665,  0x2510,  0x2561,  0x251c,  0x2514,  0x2550,
	0x2564,  0x2660,  0x250c,  0x252c,  0x2568,  0x2193,  0x253c,  0x2551,
	0x2524,  0x2190,  0x256c,  0x2191,  0x2663,  0x2500,  0x256b,  0x2502,
	0x2666,  0x2518,  0x256a,  0x2565,  0x2567,  0x255e,  0x2192,  0x2593,
	// {300..337}
	_T('ю'), _T('а'), _T('б'), _T('ц'), _T('д'), _T('е'), _T('ф'), _T('г'),
	_T('х'), _T('и'), _T('й'), _T('к'), _T('л'), _T('м'), _T('н'), _T('о'),
	_T('п'), _T('я'), _T('р'), _T('с'), _T('т'), _T('у'), _T('ж'), _T('в'),
	_T('ь'), _T('ы'), _T('з'), _T('ш'), _T('э'), _T('щ'), _T('ч'), _T('ъ'),
	// {340..377}
	_T('Ю'), _T('А'), _T('Б'), _T('Ц'), _T('Д'), _T('Е'), _T('Ф'), _T('Г'),
	_T('Х'), _T('И'), _T('Й'), _T('К'), _T('Л'), _T('М'), _T('Н'), _T('О'),
	_T('П'), _T('Я'), _T('Р'), _T('С'), _T('Т'), _T('У'), _T('Ж'), _T('В'),
	_T('Ь'), _T('Ы'), _T('З'), _T('Ш'), _T('Э'), _T('Щ'), _T('Ч'), _T('Ъ')
};

CString Global::WordToOctString(const uint16_t word)
{
	CString str;
	WordToOctString(word, str);
	return str;
}


CString Global::ByteToOctString(const uint8_t byte)
{
	CString str;
	ByteToOctString(byte, str);
	return str;
}

void Global::WordToOctString(uint16_t word, CString &str)
{
	LPTSTR pstr = str.GetBufferSetLength(7);

	for (int i = 5; i >= 0; --i)
	{
		pstr[i] = TCHAR(060 + (word & 7));
		word >>= 3;
	}

	str.ReleaseBuffer(6);
}

void Global::ByteToOctString(uint8_t byte, CString &str)
{
	LPTSTR pstr = str.GetBufferSetLength(4);

	for (int i = 2; i >= 0; --i)
	{
		pstr[i] = TCHAR(060 + (byte & 7));
		byte >>= 3;
	}

	str.ReleaseBuffer(3);
}


uint16_t Global::OctStringToWord(const CString &str)
{
	const int word = _tcstol(str, nullptr, 8);
	return LOWORD(word);
}


CString Global::IntToString(const int iInt, const int radix)
{
	CString str;
	_itot_s(iInt, str.GetBufferSetLength(256), 256, radix);
	str.ReleaseBuffer();
	return str;
}

CString Global::IntToFileLengthString(const int iInt)
{
	CString str = IntToString(iInt, 10); // текст в виде строки
	int nPos = str.GetLength() - 3; // указатель на позицию разделителя разрядов

	while (nPos > 0) // пока есть куда вставлять
	{
		str.Insert(nPos, _T(" ")); // вставляем разделитель
		nPos -= 3; // и сдвигаемся левее
	}

	return str;
}

// то же что и для дир + меняем страшные символы '\' и '/' на нестрашную '.'
void Global::SetSafeName(CString &str)
{
	SetSafeDir(str);
	str.Replace(_T('/'), _T('.'));
	str.Replace(_T('\\'), _T('.'));
}
// меняем страшные символы '>','<',':','|','?','*' на нестрашную '_'
// и все коды меньше пробела - на '_'
void Global::SetSafeDir(CString &str)
{
	str.Replace(_T('>'), _T('_'));
	str.Replace(_T('<'), _T('_'));
	str.Replace(_T(':'), _T('_'));
	str.Replace(_T('|'), _T('_'));
	str.Replace(_T('?'), _T('_'));
	str.Replace(_T('*'), _T('_'));
	const int len = str.GetLength();

	// а теперь заменим все символы с кодом меньше 32
	for (int i = 0; i < len; ++i)
	{
		if (str[i] < 32)
		{
			str.SetAt(i, _T('_'));
		}
	}
}

// Вход: msTime время в миллисекундах
CString Global::MsTimeToTimeString(const int msTime)
{
	const CString strMin = IntToString(msTime / 60000, 10);
	CString strSec;
	strSec.Format(_T("%02i"), msTime / 1000 % 60);
	return strMin + _T(':') + strSec;
}


CString Global::BKToUNICODE(std::span<uint8_t> pBuff)
{
	CString strRet;
	const size_t size = pBuff.size();
	LPTSTR pstr = strRet.GetBufferSetLength(static_cast<int>(size) + 1);
	size_t len = 0;

	while (len < size)
	{
		const TCHAR tch = BKToWIDEChar(pBuff[len]);
		pstr[len++] = tch;

		if (tch == 0)
		{
			break;
		}
	}

	strRet.ReleaseBuffer(static_cast<int>(len) - 1);
	return strRet;
}

TCHAR Global::BKToWIDEChar(const uint8_t b)
{
	if (b == 0)
	{
		return 0;
	}

	if (b < 32)
	{
		return _T(' ');
	}

	if (b == 127)
	{
		return TCHAR(0x25a0);
	}

	if (b >= 128)
	{
		return koi8tbl[b - 128];
	}

	return TCHAR(b);
}

// эта функция не выдаёт 0, заменяет его пробелом
TCHAR Global::BKToSafeWIDEChar(const uint8_t b)
{
	if (b < ' ')
	{
		return _T(' ');
	}

	if (b == 127)
	{
		return TCHAR(0x25a0);
	}

	if (b >= 128)
	{
		return TCHAR(koi8tbl[b - 128]);
	}

	return TCHAR(b);
}

/*
преобразование юникодной строки в бкшный кои8.
вход:
ustr - преобразуемая строка
pBuff - буфер, куда выводится результат
bufSize - размер буфера
bFillBuf - флаг. если строка короче размера буфера, буфер до конца забивается пробелами. (конца строки - 0 нету.)
*/
void Global::UNICODEtoBK(const CString &ustr, std::span<uint8_t> pBuff, const bool bFillBuf)
{
	const int len = ustr.GetLength();
	const size_t bufSize = pBuff.size();
	size_t bn = 0;

	for (int n = 0; n < len; ++n)
	{
		pBuff[bn++] = WIDEtoBKChar(ustr.GetAt(n));// берём очередной символ

		if (bn >= bufSize) // если буфер БКшной строки короче юникодной строки
		{
			break; // прерываем цикл
		}
	}

	if (bFillBuf) // если нужно и можно
	{
		while (bn < bufSize) // забьём конец БКшного буфера пробелами
		{
			pBuff[bn++] = 32;
		}
	}
}

uint8_t Global::WIDEtoBKChar(const TCHAR ch)
{
	if (ch == _T('\t'))
	{
		return 9;
	}

	if (ch == _T('\n'))
	{
		return 10;
	}

	if (ch < 32)    // если символ меньше пробела,
	{
		return 32;  // то будет пробел
	}

	if ((32 <= ch) && (ch < 127)) // если буквы-цифры- знаки препинания
	{
		return LOBYTE(ch); // то буквы-цифры- знаки препинания
	}

	if (ch == 0x401)    // Если Ё
	{
		return 0345;    // то Е
	}

	if (ch == 0x451)    // если ё
	{
		return 0305;    // то е
	}

	if (ch == 0x25a0)   // если такое
	{
		return 127; // то это. это единственное исключение в нижней половине аски кодов
	}

	// если всякие другие символы
	// то ищем в таблице нужный нам символ, а его номер - будет кодом кои8
	for (uint8_t i = 0; i < 128; ++i)
	{
		if (koi8tbl[i] == ch)
		{
			return (i + 0200);
		}
	}

	return 32; // если такого символа нету в таблице - будет пробел
}


int Global::ToInt(const CString &str)
{
	return std::stoi(str.GetString(), nullptr);
}

long Global::ToLong(const CString &str)
{
	return std::stol(str.GetString(), nullptr);
}

long long Global::ToLongLong(const CString &str)
{
	return std::stoll(str.GetString(), nullptr);
}

unsigned long Global::ToULong(const CString &str)
{
	return std::stoul(str.GetString(), nullptr);
}

unsigned long long Global::ToULongLong(const CString &str)
{
	return std::stoull(str.GetString(), nullptr);
}



CString Global::DwordToStr(DWORD d)
{
	CString str;
	LPTSTR buf = str.GetBufferSetLength(16);
	_sntprintf(buf, 16, _T("%08x"), d);
	str.ReleaseBuffer();
	return _T("#") + str;
}

DWORD Global::StrToDword(const CString &str)
{
	if (str.GetAt(0) == _T('#'))
	{
		return std::stol((str.GetString() + 1), nullptr, 16);
	}

	return 0;
}

#ifdef _DEBUG
// функция для вывода сообщений об ошибках при отладке, когда совсем ничего не понятно, почему не работает
void Global::GetLastErrorOut(LPTSTR lpszFunction)
{
	// Retrieve the system error message for the last-error code
	LPVOID lpMsgBuf = nullptr;
	DWORD dw = GetLastError();
	FormatMessage(
	    FORMAT_MESSAGE_ALLOCATE_BUFFER |
	    FORMAT_MESSAGE_FROM_SYSTEM |
	    FORMAT_MESSAGE_IGNORE_INSERTS,
	    nullptr,
	    dw,
	    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
	    (LPTSTR)&lpMsgBuf,
	    0, nullptr);
	// Display the error message and exit the process
	CString strErr;
	strErr.Format(_T("%s failed with error %u: %s"), lpszFunction, dw, (LPCTSTR)lpMsgBuf);
	MessageBox(nullptr, strErr.GetString(), _T("Error"), MB_OK);
	LocalFree(lpMsgBuf);
}
#endif


bool Global::CheckFFMPEG()
{
	const fs::path strName = g_Config.GetConfCurrPath() / _T("ffmpeg.exe");
	return !!fs::exists(strName);
}

bool Global::SaveRawFile(const uint8_t *buf, const uint16_t addr, const uint16_t len, const fs::path &strName)
{
	if (buf && !strName.empty())
	{
		CFile file;

		if (file.Open(strName.c_str(), CFile::modeCreate | CFile::modeWrite | CFile::typeBinary))
		{
			file.Write(buf, len);
			file.Close();
			return true;
		}

		g_BKMsgBox.Show(_T("Ошибка создания файла."), MB_OK);
	}

	return false;
}

/*
* Сохранить массив в формате bin
* Вход: buf - указатель на массив данных
* addr - адрес загрузки
* len - длина файла, == размеру массива buf
* strName - имя файла
*/
bool Global::SaveBinFile(const uint8_t *buf, const uint16_t addr, const uint16_t len, const fs::path &strName)
{
	if (buf && !strName.empty())
	{
		CFile file;

		if (file.Open(strName.c_str(), CFile::modeCreate | CFile::modeWrite | CFile::typeBinary))
		{
			// запишем заголовок
			file.Write(&addr, sizeof(uint16_t));
			file.Write(&len, sizeof(uint16_t));

			if (g_Config.m_bUseLongBinFormat)
			{
				constexpr auto BKNAME_LEN = 16;
				std::array<uint8_t, BKNAME_LEN> bkName{};
				const CString str = strName.stem().c_str();
				UNICODEtoBK(str, bkName, true);
				file.Write(bkName.data(), BKNAME_LEN); // имя файла
			}

			file.Write(buf, len);

			if (g_Config.m_bUseLongBinFormat)
			{
				int crc = 0;

				for (int i = 0; i < len; ++i)
				{
					crc += buf[i];

					if (crc & 0xFFFF0000)
					{
						// если случился перенос в 17 разряд (т.е. бит С для word)
						crc &= 0x0000FFFF; // его обнулим
						crc++; // но прибавим к сумме
					}
				}

				file.Write(&crc, sizeof(uint16_t));
			}

			file.Close();
			return true;
		}

		g_BKMsgBox.Show(_T("Ошибка создания файла."), MB_OK);
	}

	return false;
}

/*Проверка на заголовок бин.
* Вход: fsize - реальный размер файла
*       bfp - структура, в которой заполнены поля addr и length - первые 2 слова файла
* Выход: true - bin файл,
*        false - не бин файл, и заполнены остальные поля структуры bfp - offset и isCrc
*/
bool Global::CheckBinHeader(const ULONGLONG fsize, BinFileParameters_t &bfp)
{
	if (fsize < 65536)
	{
		bfp.offset = 4;
		bfp.isCrc = false;

		if (bfp.length == fsize - 4  // если простой бин
		        || (bfp.length <= fsize - 4 && fsize - 4 - bfp.length <= 03200 && (0700 <= bfp.addr && bfp.addr <= 02000))) // или кривой, но укладывающийся в логику бин файла
		{
			return true;
		}

		if (bfp.length == fsize - 6) // если усложнённый
		{
			bfp.isCrc = true;
			return true;
		}

		if (bfp.length == fsize - 22) // и не сложный
		{
			bfp.offset = 20;
			bfp.isCrc = true;
			return true;
		}

		bfp.offset = 0; // не бин файл
	}

	return false;
}

bool Global::CheckBinFile(const fs::path &pathName, BinFileParameters_t &bfp)
{
	CFile binFile;

	// если файл есть и открылся для чтения
	if (fs::exists(pathName) &&
	        binFile.Open(pathName.c_str(), CFile::modeRead | CFile::typeBinary))
	{
		// читаем первое слово - это адрес, второе слово - это длина
		if (binFile.Read(&bfp.addr, sizeof(uint16_t)) == sizeof(uint16_t) &&
		        binFile.Read(&bfp.length, sizeof(uint16_t)) == sizeof(uint16_t))
		{
			const auto fsize = binFile.GetLength();
			return CheckBinHeader(fsize, bfp);
		}
	}

	return false;
}

/*
* Загрузить массив в формате bin (или не bin с адреса 0)
* CRC игнорируется
* Вход: buf - указатель на массив данных
* addr - адрес загрузки
* len - длина файла, == размеру массива buf
* strName - имя загружаемого файла
* bStrict == true - строго бин файл, false - любой файл
*/
bool Global::LoadBinFile(std::unique_ptr<uint8_t[]> &buf, uint16_t &addr, uint16_t &len, const fs::path &pathName, const bool bStrict)
{
	addr = len = 0;
	fs::path pathWorkName = pathName;

	if (!fs::exists(pathWorkName)) //если такого файла нет
	{
		if (!pathName.has_extension()) // если расширения нет
		{
			CString strExt(MAKEINTRESOURCE(IDS_FILEEXT_BINARY));
			pathWorkName.replace_extension(strExt.GetString()); // добавим расширение
		}
		else // если расширение есть
		{
			pathWorkName.replace_extension(); // удалим расширение
		}

		if (!fs::exists(pathWorkName)) //если и такого файла нет
		{
			return false; // выходим с ошибкой
		}
	}

	bool bRet = false;
	CFile file;
	if (file.Open(pathWorkName.c_str(), CFile::modeRead | CFile::typeBinary))
	{
		BinFileParameters_t bfp{ 0, 0, 0, false };

		// читаем первое слово - это адрес, и второе - это размер
		if (file.Read(&bfp.addr, sizeof(uint16_t)) == sizeof(uint16_t) &&
		        file.Read(&bfp.length, sizeof(uint16_t)) == sizeof(uint16_t))
		{
			const auto nFileLen = file.GetLength();
			bool bIsBin = CheckBinHeader(nFileLen, bfp);
			file.Seek(bfp.offset, CFile::SeekPosition::begin);

			if (!bIsBin)
			{
				//будем загружать как простой файл.
				bfp.addr = 0;
				bfp.length = (min(0160000ull, nFileLen)) & 0xffff; //ограничим размер, 1600000 - это максимум в режиме ALL CMK-512, режимы ОЗУ11 и Hlt11, где доступно 170000 и 177600 байтов ОЗУ учитывать не будем
			}

			if (bIsBin || !bStrict)
			{
				buf = std::make_unique<uint8_t[]>(bfp.length);

				if (buf)
				{
					file.Read(buf.get(), bfp.length);
					addr = bfp.addr;
					len = bfp.length;
					bRet = true;
				}
			}
		}

		file.Close();
	}

	return bRet;
}

// выход: true - строка равна g_strEmptyUnit
//      false - не равна
bool Global::isEmptyUnit(const CString &s)
{
	return !s.CollateNoCase(g_strEmptyUnit);
}

bool Global::isEmptyUnit(const fs::path &s)
{
	return isEmptyUnit(CString(s.c_str()));
}

void Global::SetMonospaceFont(HWND hwnd, CFont *pFont)
{
	LOGFONT lf{};
	::GetObject((HGDIOBJ)::SendMessage(hwnd, WM_GETFONT, 0, 0), sizeof(LOGFONT), &lf);
	_tcscpy_s(lf.lfFaceName, LF_FACESIZE, _T("Courier New\0"));

	if (!pFont->CreateFontIndirect(&lf))
	{
		_tcscpy_s(lf.lfFaceName, LF_FACESIZE, _T("Lucida Console\0"));
		pFont->CreateFontIndirect(&lf);
	}
}

