-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement consistent cross-platform ini file management.
This update introduces a unified ini file management class to ensure cross-platform compatibility and consistent behavior across the project. The changes leverage the SimpleIni library, which was added by updating the vendor submodule. Key changes: - Refactored and enhanced src/common/Ini.cpp and Ini.h for centralized ini file handling. - Updated various vcxproj files (e.g., KnightOnLine, AIServer, Aujard) to integrate the new implementation. - Simplified and standardized ini file logic to replace platform-specific handling. This improvement sets the stage for portable and maintainable configuration management across all components.
- Loading branch information
Showing
11 changed files
with
107 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,74 +1,103 @@ | ||
#include "StdAfx.h" | ||
#include "Ini.h" | ||
|
||
#include <shared_mutex> | ||
|
||
CIni::CIni() { | ||
m_Ini.SetUnicode(); | ||
IniFileSet(fs::mktemp_file("config", ".ini")); | ||
m_bWriteDefaults = true; | ||
} | ||
|
||
CIni::CIni(const fs::path & fsFile, bool bWriteDefaults /* = true*/) { | ||
CIni::CIni(const fs::path & fsFile, bool bWriteDefaults /* = true */) { | ||
m_Ini.SetUnicode(); | ||
IniFileSet(fsFile); | ||
m_bWriteDefaults = bWriteDefaults; | ||
} | ||
|
||
void CIni::IniFileSet(const fs::path & fsFile) { | ||
m_fsFile = fsFile.is_absolute() ? fsFile : (n3std::get_app_dir() / fsFile); | ||
ReloadFile(); | ||
} | ||
|
||
void CIni::ReloadFile() const { | ||
if (!fs::exists(m_fsFile)) { | ||
return; | ||
} | ||
|
||
memset(m_szFileRef, 0, sizeof(m_szFileRef)); | ||
m_fsFile.string().copy(m_szFileRef, sizeof(m_szFileRef) - 1); | ||
std::unique_lock lock(m_mtxIni); | ||
|
||
// Check both last write timestamp and interval to avoid excessive reloads. | ||
// Defaults written by the app updates the file timestamp, so we also ensure | ||
// a minimum time interval before reloading. | ||
auto tpCurrentWriteTime = fs::last_write_time(m_fsFile); | ||
auto tpNow = std::chrono::steady_clock::now(); | ||
auto tpElapsedSeconds = std::chrono::duration_cast<std::chrono::seconds>(tpNow - m_tpLastReload); | ||
bool bNeedsReload = tpCurrentWriteTime != m_tpLastWriteTime && tpElapsedSeconds >= m_dReloadIntervalSeconds; | ||
if (!bNeedsReload) { | ||
return; | ||
} | ||
|
||
m_Ini.Reset(); | ||
m_Ini.LoadFile(m_fsFile.c_str()); | ||
m_tpLastReload = tpNow; | ||
m_tpLastWriteTime = tpCurrentWriteTime; | ||
} | ||
|
||
int CIni::SetBool(std::string_view szSection, std::string_view szKey, bool bDefault) const { | ||
return SetInt(szSection, szKey, bDefault ? 1 : 0); | ||
void CIni::SetBool(std::string_view szSection, std::string_view szKey, bool bDefault) const { | ||
SetString(szSection, szKey, bDefault ? "1" : "0"); | ||
} | ||
|
||
int CIni::SetInt(std::string_view szSection, std::string_view szKey, int iDefault) const { | ||
std::string szDefault = std::format("{:d}", iDefault); | ||
return WritePrivateProfileString(szSection.data(), szKey.data(), szDefault.c_str(), m_szFileRef); | ||
void CIni::SetInt(std::string_view szSection, std::string_view szKey, int iDefault) const { | ||
SetString(szSection, szKey, std::to_string(iDefault)); | ||
} | ||
|
||
int CIni::SetString(std::string_view szSection, std::string_view szKey, std::string_view szDefault) const { | ||
return WritePrivateProfileString(szSection.data(), szKey.data(), szDefault.data(), m_szFileRef); | ||
void CIni::SetFloat(std::string_view szSection, std::string_view szKey, float fDefault) const { | ||
SetString(szSection, szKey, std::format("{:.04f}", fDefault)); | ||
} | ||
|
||
void CIni::SetString(std::string_view szSection, std::string_view szKey, std::string_view szDefault) const { | ||
std::unique_lock lock(m_mtxIni); | ||
m_Ini.SetValue(szSection.data(), szKey.data(), szDefault.data()); | ||
m_Ini.SaveFile(m_fsFile.c_str()); | ||
} | ||
|
||
bool CIni::GetBool(std::string_view szSection, std::string_view szKey, bool bDefault) const { | ||
return GetInt(szSection.data(), szKey.data(), bDefault ? 1 : 0) ? true : false; | ||
return GetInt(szSection, szKey, bDefault ? 1 : 0) ? true : false; | ||
} | ||
|
||
int CIni::GetInt(std::string_view szSection, std::string_view szKey, int iDefault) const { | ||
int iRet = iDefault; | ||
if (m_bWriteDefaults && !Exists(szSection, szKey)) { | ||
std::string szDefault = std::format("{:d}", iDefault); | ||
WritePrivateProfileString(szSection.data(), szKey.data(), szDefault.c_str(), m_szFileRef); | ||
} else { | ||
iRet = GetPrivateProfileInt(szSection.data(), szKey.data(), iDefault, m_szFileRef); | ||
std::string szValue = GetString(szSection, szKey, std::to_string(iDefault)); | ||
|
||
int iValue = iDefault; | ||
auto errConv = std::from_chars(szValue.data(), szValue.data() + szValue.size(), iValue); | ||
if (errConv.ec == std::errc::invalid_argument || errConv.ec == std::errc::result_out_of_range) { | ||
return iDefault; | ||
} | ||
|
||
return iRet; | ||
return iValue; | ||
} | ||
|
||
std::string CIni::GetString(std::string_view szSection, std::string_view szKey, std::string_view szDefault) const { | ||
std::string szBuff(szDefault.length() + 1000, '\0'); // + 1000 to make the buffer big enough for most cases | ||
GetString(szSection, szKey, szDefault, szBuff.data(), szBuff.length()); | ||
szBuff.erase(szBuff.find('\0')); | ||
return szBuff; | ||
} | ||
float CIni::GetFloat(std::string_view szSection, std::string_view szKey, float fDefault) const { | ||
std::string szValue = GetString(szSection, szKey, std::format("{:.04f}", fDefault)); | ||
|
||
const char * CIni::GetString(std::string_view szSection, std::string_view szKey, std::string_view szDefault, | ||
char * szBuffer, size_t iSize) const { | ||
if (m_bWriteDefaults && !Exists(szSection, szKey)) { | ||
WritePrivateProfileString(szSection.data(), szKey.data(), szDefault.data(), m_szFileRef); | ||
snprintf(szBuffer, iSize, "%s", szDefault.data()); | ||
} else { | ||
GetPrivateProfileString(szSection.data(), szKey.data(), szDefault.data(), szBuffer, (DWORD)iSize, m_szFileRef); | ||
float fValue = fDefault; | ||
auto errConv = std::from_chars(szValue.data(), szValue.data() + szValue.size(), fValue); | ||
if (errConv.ec == std::errc::invalid_argument || errConv.ec == std::errc::result_out_of_range) { | ||
return fDefault; | ||
} | ||
|
||
return szBuffer; | ||
return fValue; | ||
} | ||
|
||
bool CIni::Exists(std::string_view szSection, std::string_view szKey) const { | ||
char szBuff[10]{}; | ||
std::string_view szDefault = "__UNSET__"; | ||
GetPrivateProfileString(szSection.data(), szKey.data(), szDefault.data(), szBuff, sizeof(szBuff), m_szFileRef); | ||
return szDefault != szBuff; | ||
std::string CIni::GetString(std::string_view szSection, std::string_view szKey, std::string_view szDefault) const { | ||
ReloadFile(); | ||
if (m_bWriteDefaults && !m_Ini.KeyExists(szSection.data(), szKey.data())) { | ||
SetString(szSection, szKey, szDefault); | ||
return std::string(szDefault); | ||
} | ||
|
||
std::shared_lock lock(m_mtxIni); | ||
const char * szValue = m_Ini.GetValue(szSection.data(), szKey.data(), szDefault.data()); | ||
return szValue ? std::string(szValue) : std::string(szDefault); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.