Skip to content

Instantly share code, notes, and snippets.

@thennequin
Last active November 16, 2022 16:00
Show Gist options
  • Save thennequin/c604c8caa38048a1fcdc to your computer and use it in GitHub Desktop.
Save thennequin/c604c8caa38048a1fcdc to your computer and use it in GitHub Desktop.
Ini Config Reader/Writer
Ini::IniConfig& m_oIniConfig;
Ini::CategoryValueMap& mCategories = m_oIniConfig.GetCategories();
if (ImGui::Button("Reload"))
{
m_oIniConfig.Load("Config.ini", true, false);
}
ImGui::SameLine();
if (ImGui::Button("Save"))
{
m_oIniConfig.Save();
}
if (ImGui::TreeNode("IniConfig"))
{
for (Ini::CategoryValueMap::iterator itCat = mCategories.begin(); itCat != mCategories.end(); ++itCat)
{
if (ImGui::TreeNode(itCat->first.c_str()))
{
for (Ini::Values::iterator itVal = itCat->second.begin(); itVal != itCat->second.end(); ++itVal)
{
if (itVal->m_bIsComment)
{
ImGui::TextColored(ImVec4(0.f,0.7f,0.f,1.f), "%s", itVal->m_sName.c_str());
}
else
{
ImGui::Text( "%s = %s", itVal->m_sName.c_str(), itVal->m_sValue.c_str());
}
}
ImGui::TreePop();
}
}
ImGui::TreePop();
}
#include "IniConfig.h"
namespace Ini
{
//////////////////////////////////////////
// Value
//////////////////////////////////////////
Value::Value(const String& sName, const String& sValue, bool bIsComment)
{
m_sName = sName;
m_sValue = sValue;
m_bIsComment = bIsComment;
}
Value::Value(const Value& oCopy)
{
m_sName = oCopy.m_sName;
m_sValue = oCopy.m_sValue;
m_bIsComment = oCopy.m_bIsComment;
}
//////////////////////////////////////////
// IniConfig
//////////////////////////////////////////
IniConfig::IniConfig()
{
m_bHasChanges = false;
}
IniConfig::~IniConfig()
{
}
int IniConfig::Load(const String& sFilename, bool bKeepComment, bool bKeepExisting)
{
if (!sFilename.empty())
{
FILE* pFile = fopen(sFilename.c_str(), "r");
if (NULL != pFile)
{
EReadState eState = E_READ_STATE_NONE;
char cCurrentChar;
char cPreviousChar = ' ';
String sCurrentCategory;
String sCurrentName;
String sCurrentValue;
CategoryValueMap mCategories;
int iCurrentLine = 1;
bool bAddValue = false;
if (bKeepExisting)
{
mCategories = m_mCategories;
}
size_t iReadSize;
while (true)
{
iReadSize = fread(&cCurrentChar, sizeof(char), 1, pFile);
if (iReadSize > 0)
{
if (cCurrentChar == '\r')
{
continue;
}
if (cCurrentChar == '\n')
{
++iCurrentLine;
}
switch (eState)
{
case E_READ_STATE_NONE:
switch (cCurrentChar)
{
case '#':
sCurrentName = "";
eState = E_READ_STATE_COMMENT;
break;
case '[':
sCurrentCategory = "";
eState = E_READ_STATE_CATEGORY;
break;
case ' ':
case '\t':
case '\r':
case '\n':
//Ignore char
break;
default:
eState = E_READ_STATE_PRE_NAME;
break;
}
break;
case E_READ_STATE_CATEGORY:
switch (cCurrentChar)
{
case ']':
Trim(sCurrentCategory);
eState = E_READ_STATE_POST_CATEGORY;
break;
default:
sCurrentCategory += cCurrentChar;
break;
}
break;
case E_READ_STATE_POST_CATEGORY:
switch (cCurrentChar)
{
case '\n':
eState = E_READ_STATE_NONE;
break;
case ' ':
case '\t':
//Ignore
break;
default: // Invalid Char
fclose(pFile);
return iCurrentLine;
}
break;
case E_READ_STATE_COMMENT:
switch (cCurrentChar)
{
case '\n':
mCategories[sCurrentCategory].push_back(Value(sCurrentName, sCurrentValue, true));
eState = E_READ_STATE_NONE;
break;
default:
sCurrentName += cCurrentChar;
break;
}
break;
case E_READ_STATE_PRE_NAME:
switch (cCurrentChar)
{
case '\r':
case '\n':
eState = E_READ_STATE_NONE;
break;
case ' ':
case '\t':
//Ignore
break;
default:
eState = E_READ_STATE_NAME;
sCurrentName = "";
sCurrentName += cPreviousChar;
sCurrentName += cCurrentChar;
break;
}
break;
case E_READ_STATE_NAME:
switch (cCurrentChar)
{
case '\r':
case '\n':
eState = E_READ_STATE_NONE;
break;
case '=':
eState = E_READ_STATE_VALUE;
Trim(sCurrentName);
sCurrentValue = "";
if (sCurrentName.empty())
{
fclose(pFile);
return iCurrentLine;
}
break;
default:
sCurrentName += cCurrentChar;
break;
}
break;
case E_READ_STATE_VALUE:
switch (cCurrentChar)
{
default:
sCurrentValue += cCurrentChar;
break;
case '\r':
case '\n':
Trim(sCurrentValue);
bAddValue = true;
eState = E_READ_STATE_NONE;
break;
}
break;
}
cPreviousChar = cCurrentChar;
}
if (bAddValue || (iReadSize == 0 && !sCurrentName.empty()))
{
bAddValue = false;
//Error if value name already exist
for (Values::const_iterator itValue = mCategories[sCurrentCategory].begin(); itValue != mCategories[sCurrentCategory].end(); ++itValue)
{
if (!itValue->m_bIsComment && itValue->m_sName == sCurrentName)
{
fclose(pFile);
return iCurrentLine;
}
}
mCategories[sCurrentCategory].push_back(Value(sCurrentName, sCurrentValue, eState == E_READ_STATE_COMMENT));
sCurrentName = "";
sCurrentValue = "";
}
if (iReadSize == 0)
{
break;
}
}
m_mCategories = mCategories;
m_sLastFilename = sFilename;
fclose(pFile);
return 0;
}
}
return -1;
}
bool IniConfig::Save(const String& sFilename, const String& sEOL )
{
FILE* pFile = NULL;
if (!sFilename.empty())
{
pFile = fopen(sFilename.c_str(), "w");
}
else if (!m_sLastFilename.empty())
{
pFile = fopen(m_sLastFilename.c_str(), "w");
}
if (NULL != pFile)
{
for (CategoryValueMap::const_iterator it = m_mCategories.begin(); it != m_mCategories.end(); ++it)
{
fprintf(pFile, "\n[%s]\n", it->first.c_str());
for (Values::const_iterator itValue = it->second.begin(); itValue != it->second.end(); ++itValue)
{
if (itValue->m_bIsComment)
{
fprintf(pFile, "#%s\n", itValue->m_sName.c_str());
}
else
{
fprintf(pFile, "%s=%s\n", itValue->m_sName.c_str(), itValue->m_sValue.c_str());
}
}
}
fclose(pFile);
return true;
}
return false;
}
bool IniConfig::HasChanges()
{
return m_bHasChanges;
}
bool IniConfig::HasCategory(const String& sCategory) const
{
return (m_mCategories.find(sCategory) != m_mCategories.end());
}
bool IniConfig::HasValue(const String& sCategory, const String& sName) const
{
CategoryValueMap::const_iterator itCategory = m_mCategories.find(sCategory);
if (itCategory != m_mCategories.end())
{
for (Values::const_iterator itValue = itCategory->second.begin(); itValue != itCategory->second.end(); ++itValue)
{
if (!itValue->m_bIsComment && itValue->m_sValue == sName)
{
return true;
}
}
}
return false;
}
CategoryValueMap& IniConfig::GetCategories()
{
return m_mCategories;
}
String IniConfig::ReadStringValue(const String& sCategory, const String& sName, const String& sDefaultValue, bool bWriteDefaultValue)
{
Values::const_iterator itValue;
if (GetValueConst(sCategory, sName, itValue))
{
return itValue->m_sValue;
}
if (bWriteDefaultValue)
{
m_mCategories[sCategory].push_back(Value(sName, sDefaultValue, false));
}
return sDefaultValue;
}
/*int IniConfig::ReadIntValue(const String& sCategory, const String& sName, int iDefault, bool bWriteDefaultValue)
{
Values::const_iterator itValue;
if (GetValueConst(sCategory, sName, itValue))
{
//return itValue->m_sValue;
}
if (bWriteDefaultValue)
{
//m_mCategories[sCategory].push_back(Value(sName, sDefaultValue, false));
}
return iDefault;
}
float IniConfig::ReadFloatValue(const String& sCategory, const String& sName, float fDefault, bool bWriteDefaultValue)
{
Values::const_iterator itValue;
if (GetValueConst(sCategory, sName, itValue))
{
//std::stringfor
//return itValue->m_sValue;
}
if (bWriteDefaultValue)
{
//m_mCategories[sCategory].push_back(Value(sName, sDefaultValue, false));
}
return fDefault;
}*/
void IniConfig::WriteStringValue(const String& sCategory, const String& sName, const String& sValue)
{
Values::iterator itValue;
if (GetValue(sCategory, sName, itValue))
{
itValue->m_sValue = sValue;
}
else
{
m_mCategories[sCategory].push_back(Value(sName, sValue));
}
}
/*void IniConfig::WriteIntValue(const String& sCategory, const String& sName, int iValue)
{
//TODO
}
void IniConfig::WriteFloatValue(const String& sCategory, const String& sName, float fValue)
{
//TODO
}*/
bool IniConfig::GetValueConst(const String& sCategory, const String& sName, Values::const_iterator& itOutValue) const
{
CategoryValueMap::const_iterator itCategory = m_mCategories.find(sCategory);
if (itCategory != m_mCategories.end())
{
for (Values::const_iterator itValue = itCategory->second.begin(); itValue != itCategory->second.end(); ++itValue)
{
if (!itValue->m_bIsComment && itValue->m_sName == sName)
{
itOutValue = itValue;
return true;
}
}
}
return false;
}
bool IniConfig::GetValue(const String& sCategory, const String& sName, Values::iterator& itOutValue)
{
CategoryValueMap::iterator itCategory = m_mCategories.find(sCategory);
if (itCategory != m_mCategories.end())
{
for (Values::iterator itValue = itCategory->second.begin(); itValue != itCategory->second.end(); ++itValue)
{
if (!itValue->m_bIsComment && itValue->m_sName == sName)
{
itOutValue = itValue;
return true;
}
}
}
return false;
}
void IniConfig::Trim(String& sToTrim, char cToRemove)
{
//Trim Right
int iNewLen = sToTrim.length();
while (iNewLen > 0)
{
if (sToTrim[iNewLen - 1] == cToRemove)
{
--iNewLen;
}
else
{
break;
}
}
//TrimLeft
int iStart = 0;
while (iStart < iNewLen)
{
if (sToTrim[iStart] == cToRemove)
{
++iStart;
}
else
{
break;
}
}
sToTrim = sToTrim.substr(iStart, iNewLen - iStart);
}
}
#ifndef _INI_READER_H_
#define _INI_READER_H_
#include <vector>
#include <string>
#include <map>
namespace Ini
{
using String = std::string;
struct Value
{
public:
Value(const String& sName = "", const String& sValue = "", bool bIsComment = false); //{ Name = name; Value = value; IsCom = Com; }
Value(const Value& oCopy);
String m_sName;
String m_sValue;
bool m_bIsComment;
};
typedef std::vector<Value> Values;
typedef std::map<String, Values> CategoryValueMap;
class IniConfig
{
private:
enum EReadState
{
E_READ_STATE_NONE,
E_READ_STATE_CATEGORY,
E_READ_STATE_POST_CATEGORY,
E_READ_STATE_COMMENT,
E_READ_STATE_PRE_NAME,
E_READ_STATE_NAME,
E_READ_STATE_VALUE,
E_READ_STATE_MAX
};
public:
IniConfig();
~IniConfig();
/* Return value
0 = Ok
-1 = Can't open the file
>=1 = Error found on this line
*/
int Load(const String& sFilename, bool bKeepComment = true, bool bKeepExisting = false);
bool Save(const String& sFilename = "", const String& sEOL = "\n");
bool HasChanges();
bool HasCategory(const String& sCategory) const;
bool HasValue(const String& sCategory, const String& sName) const;
CategoryValueMap& GetCategories();
String ReadStringValue(const String& sCategory, const String& sName, const String& sDefaultValue = "", bool bWriteDefaultValue = true);
//int ReadIntValue(const String& sCategory, const String& sName, int iDefault = 0, bool bWriteDefaultValue = true);
//float ReadFloatValue(const String& sCategory, const String& sName, float fDefault = 0, bool bWriteDefaultValue = true);
void WriteStringValue(const String& sCategory, const String& sName, const String& sValue);
//void WriteIntValue(const String& sCategory, const String& sName, int iValue);
//void WriteFloatValue(const String& sCategory, const String& sName, float fValue);
protected:
bool GetValueConst(const String& sCategory, const String& sName, Values::const_iterator& itOutValue) const;
bool GetValue(const String& sCategory, const String& sName, Values::iterator& itOutValue);
void Trim(String& sToTrim, char cToRemove = ' ');
protected:
bool m_bHasChanges;
String m_sLastFilename;
CategoryValueMap m_mCategories;
};
};
#endif //_INI_READER_H_
@colesnicov
Copy link

Hi thennequin, in file DisplayInImGui.hon line 2 must be:

static Ini::IniConfig m_oIniConfig;

must be static and without the apostrophe? Otherwise, with the apostrophe throwing an error on incomplete type and no static with the next film to delete the file contents from the window. And also how it is with the editing of INI file? It's a good gizmo cos here disclosed, but the implementation is not complete. You implements it somewhere or it must implement sam (the editing and saving)?

@moebiussurfing
Copy link

hey,
what's is the purpose of this snippet?
I am looking for a way to include the Tree collapse states into the .ini file settings.
Could that code help on that?
regards

@thennequin
Copy link
Author

Hi thennequin, in file DisplayInImGui.hon line 2 must be:

static Ini::IniConfig m_oIniConfig;

must be static and without the apostrophe? Otherwise, with the apostrophe throwing an error on incomplete type and no static with the next film to delete the file contents from the window. And also how it is with the editing of INI file? It's a good gizmo cos here disclosed, but the implementation is not complete. You implements it somewhere or it must implement sam (the editing and saving)?

Oh sorry for the very late response, I didn't see your comment.
m_oIniConfig is here for the exemple, it should not compile because it's an undefined reference.
It's to the user to fill it with a static or something else.

hey, what's is the purpose of this snippet? I am looking for a way to include the Tree collapse states into the .ini file settings. Could that code help on that? regards

Ini file is composed by Categories and Keys/Values.
The ImGuiIniDisplay juste allow to display the categories and their keys/values in TreeNodes.
If you want a hierarchy, Ini system is not relevant.
Prefer a system like Json, XML or YAML.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment