Skip to content

Commit

Permalink
Implement consistent cross-platform ini file management.
Browse files Browse the repository at this point in the history
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
stevewgr committed Nov 20, 2024
1 parent f91d896 commit 3e517e1
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 69 deletions.
103 changes: 66 additions & 37 deletions src/common/Ini.cpp
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);
}
31 changes: 20 additions & 11 deletions src/common/Ini.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

#include "N3Utils.h"

#include <simpleini/SimpleIni.h>
#include <shared_mutex>

class CIni {
public:
CIni();
Expand All @@ -10,23 +13,29 @@ class CIni {
const fs::path & IniFile() const { return m_fsFile; }
void IniFileSet(const fs::path & fsFile);

int SetBool(std::string_view szSection, std::string_view szKey, bool bDefault) const;
int SetInt(std::string_view szSection, std::string_view szKey, int iDefault) const;
int SetString(std::string_view szSection, std::string_view szKey, std::string_view szDefault) const;
void SetBool(std::string_view szSection, std::string_view szKey, bool bDefault) const;
void SetInt(std::string_view szSection, std::string_view szKey, int iDefault) const;
void SetFloat(std::string_view szSection, std::string_view szKey, float fDefault) const;
void SetString(std::string_view szSection, std::string_view szKey, std::string_view szDefault) const;

bool GetBool(std::string_view szSection, std::string_view szKey, bool bDefault) const;
int GetInt(std::string_view szSection, std::string_view szKey, int iDefault) const;
std::string GetString(std::string_view szSection, std::string_view szKey, std::string_view szDefault) const;
const char * GetString(std::string_view szSection, std::string_view szKey, std::string_view szDefault,
char * szBuffer, size_t iSize) const;
bool GetBool(std::string_view szSection, std::string_view szKey, bool bDefault) const;
int GetInt(std::string_view szSection, std::string_view szKey, int iDefault) const;
float GetFloat(std::string_view szSection, std::string_view szKey, float fDefault) const;
std::string GetString(std::string_view szSection, std::string_view szKey, std::string_view szDefault) const;

private:
bool Exists(std::string_view szSection, std::string_view szKey) const;
void ReloadFile() const;

public:
bool m_bWriteDefaults; // Writes default configs if a requested key/value not exists.
bool m_bWriteDefaults; // Writes defaults if a requested key does not exists.

private:
fs::path m_fsFile;
char m_szFileRef[260];

mutable CSimpleIniA m_Ini;
mutable std::shared_mutex m_mtxIni;

mutable fs::file_time_type m_tpLastWriteTime;
mutable std::chrono::steady_clock::time_point m_tpLastReload; // Last reload timestamp
const std::chrono::seconds m_dReloadIntervalSeconds{3}; // Hot reload interval
};
4 changes: 2 additions & 2 deletions src/game/KnightOnLine.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>StdAfx.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_N3GAME;N3LOG_ENABLE_SPDLOG;_CRT_SECURE_NO_WARNINGS;DIRECTINPUT_VERSION=0x0800;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(SrcDir)\engine;$(VendorDir)\imgui\include;$(VendorDir)\spdlog\include;$(VendorDir)\jpeglib\include;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\Include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(SrcDir)\engine;$(VendorDir)\simpleini\include;$(VendorDir)\imgui\include;$(VendorDir)\spdlog\include;$(VendorDir)\jpeglib\include;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\Include</AdditionalIncludeDirectories>
<AdditionalOptions>/utf-8 /wd4018 /wd4091 /wd4244 /wd4267 /wd4477 /wd4838 /wd6031 /wd26495 /wd4102 %(AdditionalOptions)</AdditionalOptions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
Expand Down Expand Up @@ -97,7 +97,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>StdAfx.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_N3GAME;_CRT_SECURE_NO_WARNINGS;DIRECTINPUT_VERSION=0x0800;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(SrcDir)\engine;$(VendorDir)\imgui\include;$(VendorDir)\spdlog\include;$(VendorDir)\jpeglib\include;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\Include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(SrcDir)\engine;$(VendorDir)\simpleini\include;$(VendorDir)\imgui\include;$(VendorDir)\spdlog\include;$(VendorDir)\jpeglib\include;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\Include</AdditionalIncludeDirectories>
<AdditionalOptions>/utf-8 /wd4018 /wd4091 /wd4244 /wd4267 /wd4477 /wd4838 /wd6031 /wd26495 /wd4102 %(AdditionalOptions)</AdditionalOptions>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FunctionLevelLinking>true</FunctionLevelLinking>
Expand Down
8 changes: 4 additions & 4 deletions src/server/AIServer/AIServer.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>StdAfx.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_3DSERVER;_REPENT;_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\Include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(VendorDir)\simpleini\include;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\include</AdditionalIncludeDirectories>
<AdditionalOptions>/utf-8 /wd4018 /wd4091 /wd4244 /wd4267 /wd4477 /wd4838 /wd6031 /wd26495 /wd4102 %(AdditionalOptions)</AdditionalOptions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
Expand All @@ -70,7 +70,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalDependencies>d3dx9.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SrcDir)\common;$(VendorDir)\dxsdk9\Lib\x86</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(VendorDir)\dxsdk9\lib\x86</AdditionalLibraryDirectories>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
<Midl>
Expand All @@ -95,7 +95,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>StdAfx.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_3DSERVER;_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\Include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(VendorDir)\simpleini\include;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\include</AdditionalIncludeDirectories>
<AdditionalOptions>/utf-8 /wd4018 /wd4091 /wd4244 /wd4267 /wd4477 /wd4838 /wd6031 /wd26495 /wd4102 %(AdditionalOptions)</AdditionalOptions>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FunctionLevelLinking>true</FunctionLevelLinking>
Expand All @@ -104,7 +104,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalDependencies>d3dx9.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SrcDir)\common;$(VendorDir)\dxsdk9\Lib\x86</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(VendorDir)\dxsdk9\lib\x86</AdditionalLibraryDirectories>
<LinkTimeCodeGeneration>UseFastLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
Expand Down
4 changes: 2 additions & 2 deletions src/server/Aujard/Aujard.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>StdAfx.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;__SAMMA;_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(WindowsSDK_IncludePath)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(VendorDir)\simpleini\include;$(WindowsSDK_IncludePath)</AdditionalIncludeDirectories>
<AdditionalOptions>/utf-8 /wd4018 /wd4091 /wd4244 /wd4267 /wd4477 /wd4838 /wd6031 /wd26495 /wd4102 %(AdditionalOptions)</AdditionalOptions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
Expand Down Expand Up @@ -93,7 +93,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>StdAfx.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;__SAMMA;_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(WindowsSDK_IncludePath)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(VendorDir)\simpleini\include;$(WindowsSDK_IncludePath)</AdditionalIncludeDirectories>
<AdditionalOptions>/utf-8 /wd4018 /wd4091 /wd4244 /wd4267 /wd4477 /wd4838 /wd6031 /wd26495 /wd4102 %(AdditionalOptions)</AdditionalOptions>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FunctionLevelLinking>true</FunctionLevelLinking>
Expand Down
8 changes: 4 additions & 4 deletions src/server/Ebenezer/Ebenezer.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>StdAfx.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_3DSERVER;_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\Include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(VendorDir)\simpleini\include;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\include</AdditionalIncludeDirectories>
<AdditionalOptions>/utf-8 /wd4018 /wd4091 /wd4244 /wd4267 /wd4477 /wd4838 /wd6031 /wd26495 /wd4102 %(AdditionalOptions)</AdditionalOptions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
Expand All @@ -70,7 +70,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalDependencies>d3dx9.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SrcDir)\common;$(VendorDir)\dxsdk9\Lib\x86</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(VendorDir)\dxsdk9\lib\x86</AdditionalLibraryDirectories>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
<Midl>
Expand All @@ -95,7 +95,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>StdAfx.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_3DSERVER;_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\Include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)res;$(SrcDir)\common;$(VendorDir)\simpleini\include;$(WindowsSDK_IncludePath);$(VendorDir)\dxsdk9\include</AdditionalIncludeDirectories>
<AdditionalOptions>/utf-8 /wd4018 /wd4091 /wd4244 /wd4267 /wd4477 /wd4838 /wd6031 /wd26495 /wd4102 %(AdditionalOptions)</AdditionalOptions>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FunctionLevelLinking>true</FunctionLevelLinking>
Expand All @@ -104,7 +104,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalDependencies>d3dx9.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SrcDir)\common;$(VendorDir)\dxsdk9\Lib\x86</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(VendorDir)\dxsdk9\lib\x86</AdditionalLibraryDirectories>
<LinkTimeCodeGeneration>UseFastLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
Expand Down
Loading

0 comments on commit 3e517e1

Please sign in to comment.