Browse code

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.

Naram Qashat authored on 2021/03/19 14:27:11
Showing 4 changed files
... ...
@@ -290,7 +290,7 @@ void XSFConfig_NCSF::RefreshSoundView()
290 290
 				if (chanId < 8)
291 291
 					buf += L"PSG/Noise?";
292 292
 				else if (chanId < 14)
293
-					buf += std::to_wstring(chn.reg.waveDuty == 7 ? 0 : 12.5 * (chn.reg.waveDuty + 1)) + L"% Square";
293
+					buf += ConvertFuncs::TrimDoubleString(std::to_wstring(chn.reg.waveDuty == 7 ? 0 : 12.5 * (chn.reg.waveDuty + 1))) + L"% Square";
294 294
 				else
295 295
 					buf += L"Noise";
296 296
 				buf += L")";
... ...
@@ -304,7 +304,7 @@ void XSFConfig_NCSF::RefreshSoundView()
304 304
 
305 305
 			std::wstring tmpBuf = ConvertFuncs::StringToWString(NumToHexString(chn.reg.timer)).substr(2);
306 306
 			buf = L"$" + tmpBuf + L" (";
307
-			tmpBuf = std::to_wstring((ARM7_CLOCK / 2) / static_cast<double>(0x10000 - chn.reg.timer) / 8);
307
+			tmpBuf = ConvertFuncs::TrimDoubleString(std::to_wstring((ARM7_CLOCK / 2) / static_cast<double>(0x10000 - chn.reg.timer) / 8));
308 308
 			if (tmpBuf.find('.') != std::wstring::npos)
309 309
 				tmpBuf = tmpBuf.substr(0, tmpBuf.find('.') + 2);
310 310
 			buf += tmpBuf + L" Hz)";
... ...
@@ -68,8 +68,8 @@ void XSFConfig::LoadConfig()
68 68
 	this->defaultLength = ConvertFuncs::StringToMS(this->configIO->GetValue("DefaultLength", XSFConfig::initDefaultLength));
69 69
 	this->defaultFade = ConvertFuncs::StringToMS(this->configIO->GetValue("DefaultFade", XSFConfig::initDefaultFade));
70 70
 	this->volume = this->configIO->GetValue("Volume", XSFConfig::initVolume);
71
-	this->volumeType = static_cast<VolumeType>(this->configIO->GetValue("VolumeType", static_cast<int>(XSFConfig::initVolumeType)));
72
-	this->peakType = static_cast<PeakType>(this->configIO->GetValue("PeakType", static_cast<int>(XSFConfig::initPeakType)));
71
+	this->volumeType = this->configIO->GetValue("VolumeType", XSFConfig::initVolumeType);
72
+	this->peakType = this->configIO->GetValue("PeakType", XSFConfig::initPeakType);
73 73
 	this->sampleRate = this->configIO->GetValue("SampleRate", XSFConfig::initSampleRate);
74 74
 	this->titleFormat = this->configIO->GetValue("TitleFormat", XSFConfig::initTitleFormat);
75 75
 
... ...
@@ -228,7 +228,7 @@ INT_PTR CALLBACK XSFConfig::ConfigDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wPa
228 228
 			SetWindowTextW(GetDlgItem(hwndDlg, idDefaultFade), ConvertFuncs::MSToWString(this->defaultFade).c_str());
229 229
 			SetWindowTextW(GetDlgItem(hwndDlg, idSkipSilenceOnStartSec), ConvertFuncs::MSToWString(this->skipSilenceOnStartSec).c_str());
230 230
 			SetWindowTextW(GetDlgItem(hwndDlg, idDetectSilenceSec), ConvertFuncs::MSToWString(this->detectSilenceSec).c_str());
231
-			SetWindowTextW(GetDlgItem(hwndDlg, idVolume), std::to_wstring(this->volume).c_str());
231
+			SetWindowTextW(GetDlgItem(hwndDlg, idVolume), ConvertFuncs::TrimDoubleString(std::to_wstring(this->volume)).c_str());
232 232
 			SendMessageW(GetDlgItem(hwndDlg, idReplayGain), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Disabled"));
233 233
 			SendMessageW(GetDlgItem(hwndDlg, idReplayGain), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Use Volume Tag"));
234 234
 			SendMessageW(GetDlgItem(hwndDlg, idReplayGain), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Track"));
... ...
@@ -335,7 +335,7 @@ void XSFConfig::ResetConfigDefaults(HWND hwndDlg)
335 335
 	SetWindowTextW(GetDlgItem(hwndDlg, idDefaultFade), ConvertFuncs::StringToWString(XSFConfig::initDefaultFade).c_str());
336 336
 	SetWindowTextW(GetDlgItem(hwndDlg, idSkipSilenceOnStartSec), ConvertFuncs::StringToWString(XSFConfig::initSkipSilenceOnStartSec).c_str());
337 337
 	SetWindowTextW(GetDlgItem(hwndDlg, idDetectSilenceSec), ConvertFuncs::StringToWString(XSFConfig::initDetectSilenceSec).c_str());
338
-	SetWindowTextW(GetDlgItem(hwndDlg, idVolume), std::to_wstring(XSFConfig::initVolume).c_str());
338
+	SetWindowTextW(GetDlgItem(hwndDlg, idVolume), ConvertFuncs::TrimDoubleString(std::to_wstring(XSFConfig::initVolume)).c_str());
339 339
 	SendMessageW(GetDlgItem(hwndDlg, idReplayGain), CB_SETCURSEL, XSFConfig::initVolumeType, 0);
340 340
 	SendMessageW(GetDlgItem(hwndDlg, idClipProtect), CB_SETCURSEL, XSFConfig::initPeakType, 0);
341 341
 	auto found = std::find(this->supportedSampleRates.begin(), this->supportedSampleRates.end(), XSFConfig::initSampleRate);
... ...
@@ -16,6 +16,26 @@
16 16
 
17 17
 class XSFConfigIO
18 18
 {
19
+private:
20
+	// enum versions
21
+	template<typename T> typename std::enable_if_t<std::is_enum_v<T>, T> GetValueInternal(const std::string &name, const T &defaultValue) const
22
+	{
23
+		return convertTo<T>(this->GetValueString(name, std::to_string(static_cast<std::underlying_type_t<T>>(defaultValue))));
24
+	}
25
+	template<typename T> typename std::enable_if_t<std::is_enum_v<T>> SetValueInternal(const std::string &name, const T &value)
26
+	{
27
+		this->SetValueString(name, std::to_string(static_cast<std::underlying_type_t<T>>(value)));
28
+	}
29
+
30
+	// non-enum versions
31
+	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
32
+	{
33
+		return convertTo<T>(this->GetValueString(name, std::to_string(defaultValue)));
34
+	}
35
+	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)
36
+	{
37
+		this->SetValueString(name, std::to_string(value));
38
+	}
19 39
 protected:
20 40
 	XSFConfigIO() { }
21 41
 public:
... ...
@@ -24,10 +44,10 @@ public:
24 44
 
25 45
 	virtual ~XSFConfigIO() { }
26 46
 	virtual void SetValueString(const std::string &name, const std::string &value) = 0;
27
-	template<typename T> void SetValue(const std::string &name, const T &value) { this->SetValueString(name, std::to_string(value)); }
47
+	template<typename T> void SetValue(const std::string &name, const T &value) { this->SetValueInternal(name, value); }
28 48
 	void SetValue(const std::string &name, const std::string &value) { this->SetValueString(name, value); }
29 49
 	virtual std::string GetValueString(const std::string &name, const std::string &defaultValue) const = 0;
30
-	template<typename T> T GetValue(const std::string &name, const T &defaultValue) const { return convertTo<T>(this->GetValueString(name, std::to_string(defaultValue))); }
50
+	template<typename T> T GetValue(const std::string &name, const T &defaultValue) const { return this->GetValueInternal(name, defaultValue); }
31 51
 	std::string GetValue(const std::string &name, const std::string &defaultValue) const { return this->GetValueString(name, defaultValue); }
32 52
 	virtual void SetHInstance(HINSTANCE) { }
33 53
 	virtual HINSTANCE GetHInstance() const { return nullptr; }
... ...
@@ -130,14 +130,14 @@ public:
130 130
 	{
131 131
 		double seconds = time / 1000.0;
132 132
 		if (seconds < 60)
133
-			return std::to_string(seconds);
133
+			return ConvertFuncs::TrimDoubleString(std::to_string(seconds));
134 134
 		unsigned long minutes = static_cast<unsigned long>(seconds) / 60;
135 135
 		seconds -= minutes * 60;
136 136
 		if (minutes < 60)
137
-			return std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + std::to_string(seconds);
137
+			return std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + ConvertFuncs::TrimDoubleString(std::to_string(seconds));
138 138
 		unsigned long hours = minutes / 60;
139 139
 		minutes %= 60;
140
-		return std::to_string(hours) + ":" + (minutes < 10 ? "0" : "") + std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + std::to_string(seconds);
140
+		return std::to_string(hours) + ":" + (minutes < 10 ? "0" : "") + std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + ConvertFuncs::TrimDoubleString(std::to_string(seconds));
141 141
 	}
142 142
 
143 143
 	static std::wstring MSToWString(unsigned long time)
... ...
@@ -145,13 +145,22 @@ public:
145 145
 		return ConvertFuncs::StringToWString(ConvertFuncs::MSToString(time));
146 146
 	}
147 147
 
148
+	// Derived from https://stackoverflow.com/a/13709929 (specifically the comments)
149
+	template<typename T> static std::basic_string<T> TrimDoubleString(const std::basic_string<T> &str)
150
+	{
151
+		auto strCopy = str;
152
+		auto lastNonZero = strCopy.find_last_not_of('0');
153
+		strCopy.erase(lastNonZero + (lastNonZero == strCopy.find('.') ? 0 : 1));
154
+		return strCopy;
155
+	}
156
+
148 157
 	static std::wstring StringToWString(const std::string &str)
149 158
 	{
150 159
 		auto strC = str.c_str();
151 160
 		int bufferSize = MultiByteToWideChar(CP_UTF8, 0, strC, -1, nullptr, 0);
152 161
 		auto buffer = std::vector<wchar_t>(bufferSize);
153 162
 		MultiByteToWideChar(CP_UTF8, 0, strC, -1, &buffer[0], bufferSize);
154
-		return std::wstring(buffer.begin(), buffer.end());
163
+		return std::wstring(buffer.begin(), buffer.begin() + bufferSize - 1);
155 164
 	}
156 165
 
157 166
 	static std::string WStringToString(const std::wstring &wstr)
... ...
@@ -160,6 +169,6 @@ public:
160 169
 		int bufferSize = WideCharToMultiByte(CP_UTF8, 0, wstrC, -1, nullptr, 0, nullptr, nullptr);
161 170
 		auto buffer = std::vector<char>(bufferSize);
162 171
 		WideCharToMultiByte(CP_UTF8, 0, wstrC, -1, &buffer[0], bufferSize, nullptr, nullptr);
163
-		return std::string(buffer.begin(), buffer.end());
172
+		return std::string(buffer.begin(), buffer.begin() + bufferSize - 1);
164 173
 	}
165 174
 };