1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 16:57:16 +01:00
SqMod/module/Library/IO/INI.hpp
Sandu Liviu Catalin 4a6bfc086c Major plugin refactor and cleanup.
Switched to POCO library for unified platform/library interface.
Deprecated the external module API. It was creating more problems than solving.
Removed most built-in libraries in favor of system libraries for easier maintenance.
Cleaned and secured code with help from static analyzers.
2021-01-30 08:51:39 +02:00

960 lines
32 KiB
C++

#pragma once
// ------------------------------------------------------------------------------------------------
#include "Core/Common.hpp"
// ------------------------------------------------------------------------------------------------
#include <SimpleIni.h>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
/* ------------------------------------------------------------------------------------------------
* Allows the user to inspect the result of certain operations and act accordingly.
*/
class IniResult
{
private:
// --------------------------------------------------------------------------------------------
String m_Action; // The action that was performed.
SQInteger m_Result; // The internal result code.
public:
/* --------------------------------------------------------------------------------------------
* Construct with no specific action or result.
*/
IniResult()
: m_Action("unknown action"), m_Result(SI_OK)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Construct with no specific result.
*/
explicit IniResult(const SQChar * action)
: m_Action(!action ? _SC("") : action), m_Result(SI_OK)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Construct with no specific action.
*/
explicit IniResult(SQInteger result)
: m_Action("unknown action"), m_Result(result)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Construct with specific action and result.
*/
IniResult(const SQChar * action, SQInteger result)
: m_Action(!action ? _SC("") : action), m_Result(result)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Copy constructor.
*/
IniResult(const IniResult & o) = default;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~IniResult() = default;
/* --------------------------------------------------------------------------------------------
* Copy assignment operator.
*/
IniResult & operator = (const IniResult & o) = default;
/* --------------------------------------------------------------------------------------------
* Perform an equality comparison between two results.
*/
bool operator == (const IniResult & o) const
{
return (m_Result == o.m_Result);
}
/* --------------------------------------------------------------------------------------------
* Perform an inequality comparison between two results.
*/
bool operator != (const IniResult & o) const
{
return (m_Result != o.m_Result);
}
/* --------------------------------------------------------------------------------------------
* Implicit conversion to boolean for use in boolean operations.
*/
operator bool () const // NOLINT(google-explicit-constructor)
{
return (m_Result >= 0);
}
/* --------------------------------------------------------------------------------------------
* Used by the script engine to compare two instances of this type.
*/
SQMOD_NODISCARD int32_t Cmp(const IniResult & o) const
{
if (m_Result == o.m_Result)
{
return 0;
}
else if (m_Result > o.m_Result)
{
return 1;
}
else
{
return -1;
}
}
/* --------------------------------------------------------------------------------------------
* Used by the script engine to convert an instance of this type to a string.
*/
SQMOD_NODISCARD const SQChar * ToString() const
{
return m_Action.c_str();
}
/* --------------------------------------------------------------------------------------------
* See whether this instance references a valid INI result.
*/
SQMOD_NODISCARD bool IsValid() const
{
return (m_Result >= 0);
}
/* --------------------------------------------------------------------------------------------
* Retrieve the associated action.
*/
SQMOD_NODISCARD const SQChar * GetAction() const
{
return m_Action.c_str();
}
/* --------------------------------------------------------------------------------------------
* Retrieve the resulted code.
*/
SQMOD_NODISCARD SQInteger GetResult() const
{
return m_Result;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the resulted code.
*/
void Check() const;
};
/* ------------------------------------------------------------------------------------------------
* Manages a reference counted INI document instance.
*/
class IniDocumentRef
{
// --------------------------------------------------------------------------------------------
friend class IniDocument;
public:
// --------------------------------------------------------------------------------------------
typedef CSimpleIniA Type; // The managed type.
// --------------------------------------------------------------------------------------------
typedef Type* Pointer; // Pointer to the managed type.
typedef const Type* ConstPtr; // Constant pointer to the managed type.
// --------------------------------------------------------------------------------------------
typedef Type& Reference; // Reference to the managed type.
typedef const Type& ConstRef; // Constant reference to the managed type.
// --------------------------------------------------------------------------------------------
typedef unsigned int Counter; // Reference counter type.
/* --------------------------------------------------------------------------------------------
* Validate the document reference and throw an error if invalid.
*/
void Validate() const;
private:
// --------------------------------------------------------------------------------------------
Pointer m_Ptr; // The document reader, writer and manager instance.
Counter* m_Ref; // Reference count to the managed instance.
/* --------------------------------------------------------------------------------------------
* Grab a strong reference to a document instance.
*/
void Grab()
{
if (m_Ptr)
{
++(*m_Ref);
}
}
/* --------------------------------------------------------------------------------------------
* Drop a strong reference to a document instance.
*/
void Drop()
{
if (m_Ptr && --(*m_Ref) == 0)
{
delete m_Ptr;
delete m_Ref;
m_Ptr = nullptr;
m_Ref = nullptr;
}
}
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
IniDocumentRef(bool utf8, bool multikey, bool multiline)
: m_Ptr(new Type(utf8, multikey, multiline)), m_Ref(new Counter(1))
{
/* ... */
}
public:
/* --------------------------------------------------------------------------------------------
* Default constructor (null).
*/
IniDocumentRef()
: m_Ptr(nullptr), m_Ref(nullptr)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Copy constructor.
*/
IniDocumentRef(const IniDocumentRef & o)
: m_Ptr(o.m_Ptr), m_Ref(o.m_Ref)
{
Grab();
}
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
IniDocumentRef(IniDocumentRef && o) noexcept
: m_Ptr(o.m_Ptr), m_Ref(o.m_Ref)
{
o.m_Ptr = nullptr;
o.m_Ref = nullptr;
}
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~IniDocumentRef()
{
Drop();
}
/* --------------------------------------------------------------------------------------------
* Copy assignment operator.
*/
IniDocumentRef & operator = (const IniDocumentRef & o) noexcept // NOLINT(bugprone-unhandled-self-assignment)
{
if (m_Ptr != o.m_Ptr)
{
Drop();
m_Ptr = o.m_Ptr;
m_Ref = o.m_Ref;
Grab();
}
return *this;
}
/* --------------------------------------------------------------------------------------------
* Move assignment operator.
*/
IniDocumentRef & operator = (IniDocumentRef && o) noexcept
{
if (m_Ptr != o.m_Ptr)
{
m_Ptr = o.m_Ptr;
m_Ref = o.m_Ref;
o.m_Ptr = nullptr;
o.m_Ref = nullptr;
}
return *this;
}
/* --------------------------------------------------------------------------------------------
* Perform an equality comparison between two document instances.
*/
bool operator == (const IniDocumentRef & o) const
{
return (m_Ptr == o.m_Ptr);
}
/* --------------------------------------------------------------------------------------------
* Perform an inequality comparison between two document instances.
*/
bool operator != (const IniDocumentRef & o) const
{
return (m_Ptr != o.m_Ptr);
}
/* --------------------------------------------------------------------------------------------
* Implicit conversion to boolean for use in boolean operations.
*/
operator bool () const // NOLINT(google-explicit-constructor)
{
return static_cast< bool >(m_Ptr);
}
/* --------------------------------------------------------------------------------------------
* Implicit conversion to the managed instance pointer.
*/
operator Pointer () // NOLINT(google-explicit-constructor)
{
return m_Ptr;
}
/* --------------------------------------------------------------------------------------------
* Implicit conversion to the managed instance pointer.
*/
operator ConstPtr () const // NOLINT(google-explicit-constructor)
{
return m_Ptr;
}
/* --------------------------------------------------------------------------------------------
* Implicit conversion to the managed instance reference.
*/
operator Reference () // NOLINT(google-explicit-constructor)
{
assert(m_Ptr);
return *m_Ptr;
}
/* --------------------------------------------------------------------------------------------
* Implicit conversion to the managed instance reference.
*/
operator ConstRef () const // NOLINT(google-explicit-constructor)
{
assert(m_Ptr);
return *m_Ptr;
}
/* --------------------------------------------------------------------------------------------
* Member operator for dereferencing the managed pointer.
*/
Pointer operator -> () const
{
assert(m_Ptr);
return m_Ptr;
}
/* --------------------------------------------------------------------------------------------
* Indirection operator for obtaining a reference of the managed pointer.
*/
Reference operator * () const
{
assert(m_Ptr);
return *m_Ptr;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the number of active references to the managed instance.
*/
SQMOD_NODISCARD Counter Count() const
{
return (m_Ptr && m_Ref) ? (*m_Ref) : 0;
}
};
/* ------------------------------------------------------------------------------------------------
* Class that can access and iterate a series of entries in the INI document.
*/
class IniEntries
{
// --------------------------------------------------------------------------------------------
friend class IniDocument;
protected:
// --------------------------------------------------------------------------------------------
typedef IniDocumentRef::Type::TNamesDepend Container;
// --------------------------------------------------------------------------------------------
typedef Container::iterator Iterator;
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
IniEntries(const IniDocumentRef & ini, Container & list) // NOLINT(modernize-pass-by-value)
: m_Doc(ini), m_List(), m_Elem()
{
m_List.swap(list);
Reset();
}
private:
// ---------------------------------------------------------------------------------------------
IniDocumentRef m_Doc; // The document that contains the elements.
Container m_List; // The list of elements to iterate.
Iterator m_Elem; // The currently processed element.
public:
/* --------------------------------------------------------------------------------------------
* Default constructor. (null)
*/
IniEntries()
: m_Doc(), m_List(), m_Elem(m_List.end())
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Copy constructor.
*/
IniEntries(const IniEntries & o)
: m_Doc(o.m_Doc), m_List(o.m_List), m_Elem()
{
Reset();
}
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~IniEntries() = default;
/* --------------------------------------------------------------------------------------------
* Copy assignment operator.
*/
IniEntries & operator = (const IniEntries & o)
{
m_Doc = o.m_Doc;
m_List = o.m_List;
Reset();
return *this;
}
/* --------------------------------------------------------------------------------------------
* Used by the script engine to compare two instances of this type.
*/
SQMOD_NODISCARD int32_t Cmp(const IniEntries & o) const;
/* --------------------------------------------------------------------------------------------
* Used by the script engine to convert an instance of this type to a string.
*/
SQMOD_NODISCARD const SQChar * ToString() const
{
return GetItem();
}
/* --------------------------------------------------------------------------------------------
* Return whether the current element is valid and can be accessed.
*/
SQMOD_NODISCARD bool IsValid() const
{
return !(m_List.empty() || m_Elem == m_List.end());
}
/* --------------------------------------------------------------------------------------------
* Return whether the entry list is empty.
*/
SQMOD_NODISCARD bool IsEmpty() const
{
return m_List.empty();
}
/* --------------------------------------------------------------------------------------------
* Return the number of active references to this document instance.
*/
SQMOD_NODISCARD uint32_t GetRefCount() const
{
return m_Doc.Count();
}
/* --------------------------------------------------------------------------------------------
* Return the total entries in the list.
*/
SQMOD_NODISCARD int32_t GetSize() const
{
return static_cast< int32_t >(m_List.size());
}
/* --------------------------------------------------------------------------------------------
* Reset the internal iterator to the first element.
*/
void Reset()
{
if (m_List.empty())
{
m_Elem = m_List.end();
}
else
{
m_Elem = m_List.begin();
}
}
/* --------------------------------------------------------------------------------------------
* Go to the next element.
*/
void Next();
/* --------------------------------------------------------------------------------------------
* Go to the previous element.
*/
void Prev();
/* --------------------------------------------------------------------------------------------
* Advance a certain number of elements.
*/
void Advance(int32_t n);
/* --------------------------------------------------------------------------------------------
* Retreat a certain number of elements.
*/
void Retreat(int32_t n);
/* --------------------------------------------------------------------------------------------
* Sort the entries using the default options.
*/
void Sort()
{
if (!m_List.empty())
{
m_List.sort(IniDocumentRef::Type::Entry::KeyOrder());
}
}
/* --------------------------------------------------------------------------------------------
* Sort the entries by name of key only.
*/
void SortByKeyOrder()
{
if (!m_List.empty())
{
m_List.sort(IniDocumentRef::Type::Entry::KeyOrder());
}
}
/* --------------------------------------------------------------------------------------------
* Sort the entries by their load order and then name of key.
*/
void SortByLoadOrder()
{
if (!m_List.empty())
{
m_List.sort(IniDocumentRef::Type::Entry::LoadOrder());
}
}
/* --------------------------------------------------------------------------------------------
* Retrieve the string value of the current element item.
*/
SQMOD_NODISCARD const SQChar * GetItem() const;
/* --------------------------------------------------------------------------------------------
* Retrieve the string value of the current element comment.
*/
SQMOD_NODISCARD const SQChar * GetComment() const;
/* --------------------------------------------------------------------------------------------
* Retrieve the order of the current element.
*/
SQMOD_NODISCARD int32_t GetOrder() const;
};
/* ------------------------------------------------------------------------------------------------
* Class that can read/write and alter the contents of INI files.
*/
class IniDocument
{
protected:
// --------------------------------------------------------------------------------------------
typedef IniDocumentRef::Type::TNamesDepend Container;
private:
// ---------------------------------------------------------------------------------------------
IniDocumentRef m_Doc; // The main INI document instance.
public:
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
IniDocument()
: m_Doc(false, false, true)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Explicit constructor.
*/
explicit IniDocument(bool utf8)
: m_Doc(utf8, false, true)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Explicit constructor.
*/
IniDocument(bool utf8, bool multikey)
: m_Doc(utf8, multikey, true)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Explicit constructor.
*/
IniDocument(bool utf8, bool multikey, bool multiline)
: m_Doc(utf8, multikey, multiline)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
IniDocument(const IniDocument & o) = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~IniDocument() = default;
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
IniDocument & operator = (const IniDocument & o) = delete;
/* --------------------------------------------------------------------------------------------
* Used by the script engine to compare two instances of this type.
*/
SQMOD_NODISCARD int32_t Cmp(const IniDocument & o) const;
/* --------------------------------------------------------------------------------------------
* Used by the script engine to convert an instance of this type to a string.
*/
SQMOD_NODISCARD const SQChar * ToString() const // NOLINT(readability-convert-member-functions-to-static)
{
return _SC("");
}
/* --------------------------------------------------------------------------------------------
* See whether this instance references a valid INI document.
*/
SQMOD_NODISCARD bool IsValid() const
{
return m_Doc;
}
/* --------------------------------------------------------------------------------------------
* Return the number of active references to this document instance.
*/
SQMOD_NODISCARD uint32_t GetRefCount() const
{
return m_Doc.Count();
}
/* --------------------------------------------------------------------------------------------
* See whether any data has been loaded into this document.
*/
SQMOD_NODISCARD bool IsEmpty() const
{
return m_Doc->IsEmpty();
}
/* --------------------------------------------------------------------------------------------
* Deallocate all memory stored by this document.
*/
void Reset() const
{
m_Doc->Reset();
}
/* --------------------------------------------------------------------------------------------
* See whether the INI data is treated as unicode.
*/
SQMOD_NODISCARD bool GetUnicode() const
{
return m_Doc->IsUnicode();
}
/* --------------------------------------------------------------------------------------------
* Set whether the INI data should be treated as unicode.
*/
void SetUnicode(bool toggle)
{
m_Doc->SetUnicode(toggle);
}
/* --------------------------------------------------------------------------------------------
* See whether multiple identical keys be permitted in the file.
*/
SQMOD_NODISCARD bool GetMultiKey() const
{
return m_Doc->IsMultiKey();
}
/* --------------------------------------------------------------------------------------------
* Set whether multiple identical keys be permitted in the file.
*/
void SetMultiKey(bool toggle)
{
m_Doc->SetMultiKey(toggle);
}
/* --------------------------------------------------------------------------------------------
* See whether data values are permitted to span multiple lines in the file.
*/
SQMOD_NODISCARD bool GetMultiLine() const
{
return m_Doc->IsMultiLine();
}
/* --------------------------------------------------------------------------------------------
* Set whether data values are permitted to span multiple lines in the file.
*/
void SetMultiLine(bool toggle)
{
m_Doc->SetMultiLine(toggle);
}
/* --------------------------------------------------------------------------------------------
* See whether spaces are added around the equals sign when writing key/value pairs out.
*/
SQMOD_NODISCARD bool GetSpaces() const
{
return m_Doc->UsingSpaces();
}
/* --------------------------------------------------------------------------------------------
* Set whether spaces are added around the equals sign when writing key/value pairs out.
*/
void SetSpaces(bool toggle)
{
m_Doc->SetSpaces(toggle);
}
/* --------------------------------------------------------------------------------------------
* Load an INI file from disk into memory.
*/
IniResult LoadFile(const SQChar * filepath);
/* --------------------------------------------------------------------------------------------
* Load INI file data direct from a string. (LoadString collides with the windows api)
*/
IniResult LoadData(const SQChar * source)
{
return LoadData(source, -1);
}
/* --------------------------------------------------------------------------------------------
* Load INI file data direct from a string. (LoadString collides with the windows api)
*/
IniResult LoadData(const SQChar * source, int32_t size);
/* --------------------------------------------------------------------------------------------
* Save an INI file from memory to disk.
*/
IniResult SaveFile(const SQChar * filepath)
{
return SaveFile(filepath, true);
}
/* --------------------------------------------------------------------------------------------
* Save an INI file from memory to disk.
*/
IniResult SaveFile(const SQChar * filepath, bool signature);
/* --------------------------------------------------------------------------------------------
* Save the INI data to a string.
*/
Object SaveData(bool signature);
/* --------------------------------------------------------------------------------------------
* Retrieve all section names.
*/
SQMOD_NODISCARD IniEntries GetAllSections() const;
/* --------------------------------------------------------------------------------------------
* Retrieve all unique key names in a section.
*/
SQMOD_NODISCARD IniEntries GetAllKeys(const SQChar * section) const;
/* --------------------------------------------------------------------------------------------
* Retrieve all values for a specific key.
*/
SQMOD_NODISCARD IniEntries GetAllValues(const SQChar * section, const SQChar * key) const;
/* --------------------------------------------------------------------------------------------
* Query the number of keys in a specific section.
*/
SQMOD_NODISCARD int32_t GetSectionSize(const SQChar * section) const;
/* --------------------------------------------------------------------------------------------
* See whether a certain key has multiple instances.
*/
SQMOD_NODISCARD bool HasMultipleKeys(const SQChar * section, const SQChar * key) const;
/* --------------------------------------------------------------------------------------------
* Retrieve the value for a specific key.
*/
SQMOD_NODISCARD const char * GetValue(const SQChar * section, const SQChar * key, const SQChar * def) const;
/* --------------------------------------------------------------------------------------------
* Retrieve a numeric value for a specific key.
*/
SQMOD_NODISCARD SQInteger GetInteger(const SQChar * section, const SQChar * key, SQInteger def) const;
/* --------------------------------------------------------------------------------------------
* Retrieve a numeric value for a specific key.
*/
SQMOD_NODISCARD SQFloat GetFloat(const SQChar * section, const SQChar * key, SQFloat def) const;
/* --------------------------------------------------------------------------------------------
* Retrieve a boolean value for a specific key.
*/
SQMOD_NODISCARD bool GetBoolean(const SQChar * section, const SQChar * key, bool def) const;
/* --------------------------------------------------------------------------------------------
* Add or update a section or value.
*/
IniResult SetValue(const SQChar * section, const SQChar * key, const SQChar * value)
{
return SetValue(section, key, value, false, nullptr);
}
/* --------------------------------------------------------------------------------------------
* Add or update a section or value.
*/
IniResult SetValue(const SQChar * section, const SQChar * key, const SQChar * value, bool force)
{
return SetValue(section, key, value, force, nullptr);
}
/* --------------------------------------------------------------------------------------------
* Add or update a section or value.
*/
IniResult SetValue(const SQChar * section, const SQChar * key, const SQChar * value, bool force, const SQChar * comment);
/* --------------------------------------------------------------------------------------------
* Add or update a numeric value.
*/
IniResult SetInteger(const SQChar * section, const SQChar * key, SQInteger value)
{
return SetInteger(section, key, value, false, false, nullptr);
}
/* --------------------------------------------------------------------------------------------
* Add or update a numeric value.
*/
IniResult SetInteger(const SQChar * section, const SQChar * key, SQInteger value, bool hex)
{
return SetInteger(section, key, value, hex, false, nullptr);
}
/* --------------------------------------------------------------------------------------------
* Add or update a numeric value.
*/
IniResult SetInteger(const SQChar * section, const SQChar * key, SQInteger value, bool hex, bool force)
{
return SetInteger(section, key, value, hex, force, nullptr);
}
/* --------------------------------------------------------------------------------------------
* Add or update a numeric value.
*/
IniResult SetInteger(const SQChar * section, const SQChar * key, SQInteger value, bool hex, bool force, const SQChar * comment);
/* --------------------------------------------------------------------------------------------
* Add or update a double value.
*/
IniResult SetFloat(const SQChar * section, const SQChar * key, SQFloat value)
{
return SetFloat(section, key, value, false, nullptr);
}
/* --------------------------------------------------------------------------------------------
* Add or update a double value.
*/
IniResult SetFloat(const SQChar * section, const SQChar * key, SQFloat value, bool force)
{
return SetFloat(section, key, value, force, nullptr);
}
/* --------------------------------------------------------------------------------------------
* Add or update a double value.
*/
IniResult SetFloat(const SQChar * section, const SQChar * key, SQFloat value, bool force, const SQChar * comment);
/* --------------------------------------------------------------------------------------------
* Add or update a double value.
*/
IniResult SetBoolean(const SQChar * section, const SQChar * key, bool value)
{
return SetBoolean(section, key, value, false, nullptr);
}
/* --------------------------------------------------------------------------------------------
* Add or update a double value.
*/
IniResult SetBoolean(const SQChar * section, const SQChar * key, bool value, bool force)
{
return SetBoolean(section, key, value, force, nullptr);
}
/* --------------------------------------------------------------------------------------------
* Add or update a boolean value.
*/
IniResult SetBoolean(const SQChar * section, const SQChar * key, bool value, bool force, const SQChar * comment);
/* --------------------------------------------------------------------------------------------
* Delete an entire section, or a key from a section.
*/
bool DeleteValue(const SQChar * section)
{
return DeleteValue(section, nullptr, nullptr, false);
}
/* --------------------------------------------------------------------------------------------
* Delete an entire section, or a key from a section.
*/
bool DeleteValue(const SQChar * section, const SQChar * key)
{
return DeleteValue(section, key, nullptr, false);
}
/* --------------------------------------------------------------------------------------------
* Delete an entire section, or a key from a section.
*/
bool DeleteValue(const SQChar * section, const SQChar * key, const SQChar * value)
{
return DeleteValue(section, key, value, false);
}
/* --------------------------------------------------------------------------------------------
* Delete an entire section, or a key from a section.
*/
bool DeleteValue(const SQChar * section, const SQChar * key, const SQChar * value, bool empty);
};
} // Namespace:: SqMod