A few more conversion changes:
* To go along with the previous change, XSFConfig now had internal get/set value functions that use type traits to choose between the enum and non-enum versions. (This also means I do not need to manually double-cast enums now.)
* Fix bug with the Windows API string conversions, so they do not include the trailing null byte in the output string.
* Added function to trim trailing 0s (and the decimal point if necessary) from a string that came from a double.
--- a/src/in_ncsf/XSFConfig_NCSF.cpp
+++ b/src/in_ncsf/XSFConfig_NCSF.cpp
@@ -290,7 +290,7 @@
if (chanId < 8)
buf += L"PSG/Noise?";
else if (chanId < 14)
- buf += std::to_wstring(chn.reg.waveDuty == 7 ? 0 : 12.5 * (chn.reg.waveDuty + 1)) + L"% Square";
+ buf += ConvertFuncs::TrimDoubleString(std::to_wstring(chn.reg.waveDuty == 7 ? 0 : 12.5 * (chn.reg.waveDuty + 1))) + L"% Square";
else
buf += L"Noise";
buf += L")";
@@ -304,7 +304,7 @@
std::wstring tmpBuf = ConvertFuncs::StringToWString(NumToHexString(chn.reg.timer)).substr(2);
buf = L"$" + tmpBuf + L" (";
- tmpBuf = std::to_wstring((ARM7_CLOCK / 2) / static_cast<double>(0x10000 - chn.reg.timer) / 8);
+ tmpBuf = ConvertFuncs::TrimDoubleString(std::to_wstring((ARM7_CLOCK / 2) / static_cast<double>(0x10000 - chn.reg.timer) / 8));
if (tmpBuf.find('.') != std::wstring::npos)
tmpBuf = tmpBuf.substr(0, tmpBuf.find('.') + 2);
buf += tmpBuf + L" Hz)";
--- a/src/in_xsf_framework/XSFConfig.cpp
+++ b/src/in_xsf_framework/XSFConfig.cpp
@@ -68,8 +68,8 @@
this->defaultLength = ConvertFuncs::StringToMS(this->configIO->GetValue("DefaultLength", XSFConfig::initDefaultLength));
this->defaultFade = ConvertFuncs::StringToMS(this->configIO->GetValue("DefaultFade", XSFConfig::initDefaultFade));
this->volume = this->configIO->GetValue("Volume", XSFConfig::initVolume);
- this->volumeType = static_cast<VolumeType>(this->configIO->GetValue("VolumeType", static_cast<int>(XSFConfig::initVolumeType)));
- this->peakType = static_cast<PeakType>(this->configIO->GetValue("PeakType", static_cast<int>(XSFConfig::initPeakType)));
+ this->volumeType = this->configIO->GetValue("VolumeType", XSFConfig::initVolumeType);
+ this->peakType = this->configIO->GetValue("PeakType", XSFConfig::initPeakType);
this->sampleRate = this->configIO->GetValue("SampleRate", XSFConfig::initSampleRate);
this->titleFormat = this->configIO->GetValue("TitleFormat", XSFConfig::initTitleFormat);
@@ -228,7 +228,7 @@
SetWindowTextW(GetDlgItem(hwndDlg, idDefaultFade), ConvertFuncs::MSToWString(this->defaultFade).c_str());
SetWindowTextW(GetDlgItem(hwndDlg, idSkipSilenceOnStartSec), ConvertFuncs::MSToWString(this->skipSilenceOnStartSec).c_str());
SetWindowTextW(GetDlgItem(hwndDlg, idDetectSilenceSec), ConvertFuncs::MSToWString(this->detectSilenceSec).c_str());
- SetWindowTextW(GetDlgItem(hwndDlg, idVolume), std::to_wstring(this->volume).c_str());
+ SetWindowTextW(GetDlgItem(hwndDlg, idVolume), ConvertFuncs::TrimDoubleString(std::to_wstring(this->volume)).c_str());
SendMessageW(GetDlgItem(hwndDlg, idReplayGain), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Disabled"));
SendMessageW(GetDlgItem(hwndDlg, idReplayGain), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Use Volume Tag"));
SendMessageW(GetDlgItem(hwndDlg, idReplayGain), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Track"));
@@ -335,7 +335,7 @@
SetWindowTextW(GetDlgItem(hwndDlg, idDefaultFade), ConvertFuncs::StringToWString(XSFConfig::initDefaultFade).c_str());
SetWindowTextW(GetDlgItem(hwndDlg, idSkipSilenceOnStartSec), ConvertFuncs::StringToWString(XSFConfig::initSkipSilenceOnStartSec).c_str());
SetWindowTextW(GetDlgItem(hwndDlg, idDetectSilenceSec), ConvertFuncs::StringToWString(XSFConfig::initDetectSilenceSec).c_str());
- SetWindowTextW(GetDlgItem(hwndDlg, idVolume), std::to_wstring(XSFConfig::initVolume).c_str());
+ SetWindowTextW(GetDlgItem(hwndDlg, idVolume), ConvertFuncs::TrimDoubleString(std::to_wstring(XSFConfig::initVolume)).c_str());
SendMessageW(GetDlgItem(hwndDlg, idReplayGain), CB_SETCURSEL, XSFConfig::initVolumeType, 0);
SendMessageW(GetDlgItem(hwndDlg, idClipProtect), CB_SETCURSEL, XSFConfig::initPeakType, 0);
auto found = std::find(this->supportedSampleRates.begin(), this->supportedSampleRates.end(), XSFConfig::initSampleRate);
--- a/src/in_xsf_framework/XSFConfig.h
+++ b/src/in_xsf_framework/XSFConfig.h
@@ -16,6 +16,26 @@
class XSFConfigIO
{
+private:
+ // enum versions
+ template<typename T> typename std::enable_if_t<std::is_enum_v<T>, T> GetValueInternal(const std::string &name, const T &defaultValue) const
+ {
+ return convertTo<T>(this->GetValueString(name, std::to_string(static_cast<std::underlying_type_t<T>>(defaultValue))));
+ }
+ template<typename T> typename std::enable_if_t<std::is_enum_v<T>> SetValueInternal(const std::string &name, const T &value)
+ {
+ this->SetValueString(name, std::to_string(static_cast<std::underlying_type_t<T>>(value)));
+ }
+
+ // non-enum versions
+ template<typename T> typename std::enable_if_t<!std::is_enum_v<T> &&std::is_arithmetic_v<T>, T> GetValueInternal(const std::string &name, const T &defaultValue) const
+ {
+ return convertTo<T>(this->GetValueString(name, std::to_string(defaultValue)));
+ }
+ template<typename T> typename std::enable_if_t<!std::is_enum_v<T> &&std::is_arithmetic_v<T>> SetValueInternal(const std::string &name, const T &value)
+ {
+ this->SetValueString(name, std::to_string(value));
+ }
protected:
XSFConfigIO() { }
public:
@@ -24,10 +44,10 @@
virtual ~XSFConfigIO() { }
virtual void SetValueString(const std::string &name, const std::string &value) = 0;
- template<typename T> void SetValue(const std::string &name, const T &value) { this->SetValueString(name, std::to_string(value)); }
+ template<typename T> void SetValue(const std::string &name, const T &value) { this->SetValueInternal(name, value); }
void SetValue(const std::string &name, const std::string &value) { this->SetValueString(name, value); }
virtual std::string GetValueString(const std::string &name, const std::string &defaultValue) const = 0;
- template<typename T> T GetValue(const std::string &name, const T &defaultValue) const { return convertTo<T>(this->GetValueString(name, std::to_string(defaultValue))); }
+ template<typename T> T GetValue(const std::string &name, const T &defaultValue) const { return this->GetValueInternal(name, defaultValue); }
std::string GetValue(const std::string &name, const std::string &defaultValue) const { return this->GetValueString(name, defaultValue); }
virtual void SetHInstance(HINSTANCE) { }
virtual HINSTANCE GetHInstance() const { return nullptr; }
--- a/src/in_xsf_framework/convert.h
+++ b/src/in_xsf_framework/convert.h
@@ -130,19 +130,28 @@
{
double seconds = time / 1000.0;
if (seconds < 60)
- return std::to_string(seconds);
+ return ConvertFuncs::TrimDoubleString(std::to_string(seconds));
unsigned long minutes = static_cast<unsigned long>(seconds) / 60;
seconds -= minutes * 60;
if (minutes < 60)
- return std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + std::to_string(seconds);
+ return std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + ConvertFuncs::TrimDoubleString(std::to_string(seconds));
unsigned long hours = minutes / 60;
minutes %= 60;
- return std::to_string(hours) + ":" + (minutes < 10 ? "0" : "") + std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + std::to_string(seconds);
+ return std::to_string(hours) + ":" + (minutes < 10 ? "0" : "") + std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + ConvertFuncs::TrimDoubleString(std::to_string(seconds));
}
static std::wstring MSToWString(unsigned long time)
{
return ConvertFuncs::StringToWString(ConvertFuncs::MSToString(time));
+ }
+
+ // Derived from https://stackoverflow.com/a/13709929 (specifically the comments)
+ template<typename T> static std::basic_string<T> TrimDoubleString(const std::basic_string<T> &str)
+ {
+ auto strCopy = str;
+ auto lastNonZero = strCopy.find_last_not_of('0');
+ strCopy.erase(lastNonZero + (lastNonZero == strCopy.find('.') ? 0 : 1));
+ return strCopy;
}
static std::wstring StringToWString(const std::string &str)
@@ -151,7 +160,7 @@
int bufferSize = MultiByteToWideChar(CP_UTF8, 0, strC, -1, nullptr, 0);
auto buffer = std::vector<wchar_t>(bufferSize);
MultiByteToWideChar(CP_UTF8, 0, strC, -1, &buffer[0], bufferSize);
- return std::wstring(buffer.begin(), buffer.end());
+ return std::wstring(buffer.begin(), buffer.begin() + bufferSize - 1);
}
static std::string WStringToString(const std::wstring &wstr)
@@ -160,7 +169,7 @@
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, wstrC, -1, nullptr, 0, nullptr, nullptr);
auto buffer = std::vector<char>(bufferSize);
WideCharToMultiByte(CP_UTF8, 0, wstrC, -1, &buffer[0], bufferSize, nullptr, nullptr);
- return std::string(buffer.begin(), buffer.end());
+ return std::string(buffer.begin(), buffer.begin() + bufferSize - 1);
}
};