2015-05-23 22:55:12 -06:00
|
|
|
// Copyright 2008 Dolphin Emulator Project
|
2015-05-17 17:08:10 -06:00
|
|
|
// Licensed under GPLv2+
|
2013-04-17 21:09:55 -06:00
|
|
|
// Refer to the license.txt file included.
|
2008-12-07 21:46:09 -07:00
|
|
|
|
2014-02-10 11:54:46 -07:00
|
|
|
#pragma once
|
2008-12-07 21:46:09 -07:00
|
|
|
|
Common/IniFile: Make CaseInsensitiveStringCompare usable with heterogenous lookup
Previously, when performing find() operations or indexing operations on
the section map, it would need to operate on a std::string key.
This means cases like:
map.find(some_string_view)
aren't usable, which kind of sucks, especially given for most cases, we
use regular string literals to perform operations in calling code.
However, since C++14, it's possible to use heterogenous lookup to avoid
needing to construct exact key types. In otherwords, we can perform the
above or use string literals without constructing a std::string instance
around them implicitly.
We simply need to specify a member type within our comparison struct
named is_transparent, to allow std::map to perform automatic type
deduction.
We also slightly alter the algorithm to an equivalent compatible with
std::string_view (which need not be null-terminated), as strcasecmp
requires null-terminated strings.
While we're at it, we can also provide a helper function to the struct
for comparing string equality rather than only less than. This allows
removing other usages of strcasecmp in other functions, allowing for the
transition of them to std::string_view.
2019-06-16 14:57:05 -06:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cctype>
|
2014-06-24 11:28:02 -06:00
|
|
|
#include <list>
|
2013-09-06 11:56:19 -06:00
|
|
|
#include <map>
|
2014-02-17 03:18:15 -07:00
|
|
|
#include <string>
|
Common/IniFile: Make CaseInsensitiveStringCompare usable with heterogenous lookup
Previously, when performing find() operations or indexing operations on
the section map, it would need to operate on a std::string key.
This means cases like:
map.find(some_string_view)
aren't usable, which kind of sucks, especially given for most cases, we
use regular string literals to perform operations in calling code.
However, since C++14, it's possible to use heterogenous lookup to avoid
needing to construct exact key types. In otherwords, we can perform the
above or use string literals without constructing a std::string instance
around them implicitly.
We simply need to specify a member type within our comparison struct
named is_transparent, to allow std::map to perform automatic type
deduction.
We also slightly alter the algorithm to an equivalent compatible with
std::string_view (which need not be null-terminated), as strcasecmp
requires null-terminated strings.
While we're at it, we can also provide a helper function to the struct
for comparing string equality rather than only less than. This allows
removing other usages of strcasecmp in other functions, allowing for the
transition of them to std::string_view.
2019-06-16 14:57:05 -06:00
|
|
|
#include <string_view>
|
2010-06-03 12:05:08 -06:00
|
|
|
#include <vector>
|
2008-12-07 21:46:09 -07:00
|
|
|
|
2014-02-19 20:11:52 -07:00
|
|
|
#include "Common/CommonTypes.h"
|
2018-06-03 06:18:58 -06:00
|
|
|
#include "Common/StringUtil.h"
|
2008-12-07 21:46:09 -07:00
|
|
|
|
2013-09-06 11:56:19 -06:00
|
|
|
struct CaseInsensitiveStringCompare
|
|
|
|
{
|
Common/IniFile: Make CaseInsensitiveStringCompare usable with heterogenous lookup
Previously, when performing find() operations or indexing operations on
the section map, it would need to operate on a std::string key.
This means cases like:
map.find(some_string_view)
aren't usable, which kind of sucks, especially given for most cases, we
use regular string literals to perform operations in calling code.
However, since C++14, it's possible to use heterogenous lookup to avoid
needing to construct exact key types. In otherwords, we can perform the
above or use string literals without constructing a std::string instance
around them implicitly.
We simply need to specify a member type within our comparison struct
named is_transparent, to allow std::map to perform automatic type
deduction.
We also slightly alter the algorithm to an equivalent compatible with
std::string_view (which need not be null-terminated), as strcasecmp
requires null-terminated strings.
While we're at it, we can also provide a helper function to the struct
for comparing string equality rather than only less than. This allows
removing other usages of strcasecmp in other functions, allowing for the
transition of them to std::string_view.
2019-06-16 14:57:05 -06:00
|
|
|
// Allow heterogenous lookup.
|
|
|
|
using is_transparent = void;
|
|
|
|
|
|
|
|
bool operator()(std::string_view a, std::string_view b) const
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
Common/IniFile: Make CaseInsensitiveStringCompare usable with heterogenous lookup
Previously, when performing find() operations or indexing operations on
the section map, it would need to operate on a std::string key.
This means cases like:
map.find(some_string_view)
aren't usable, which kind of sucks, especially given for most cases, we
use regular string literals to perform operations in calling code.
However, since C++14, it's possible to use heterogenous lookup to avoid
needing to construct exact key types. In otherwords, we can perform the
above or use string literals without constructing a std::string instance
around them implicitly.
We simply need to specify a member type within our comparison struct
named is_transparent, to allow std::map to perform automatic type
deduction.
We also slightly alter the algorithm to an equivalent compatible with
std::string_view (which need not be null-terminated), as strcasecmp
requires null-terminated strings.
While we're at it, we can also provide a helper function to the struct
for comparing string equality rather than only less than. This allows
removing other usages of strcasecmp in other functions, allowing for the
transition of them to std::string_view.
2019-06-16 14:57:05 -06:00
|
|
|
return std::lexicographical_compare(
|
|
|
|
a.begin(), a.end(), b.begin(), b.end(), [](char lhs, char rhs) {
|
|
|
|
return std::tolower(static_cast<u8>(lhs)) < std::tolower(static_cast<u8>(rhs));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsEqual(std::string_view a, std::string_view b)
|
|
|
|
{
|
|
|
|
if (a.size() != b.size())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char lhs, char rhs) {
|
|
|
|
return std::tolower(static_cast<u8>(lhs)) == std::tolower(static_cast<u8>(rhs));
|
|
|
|
});
|
2016-06-24 02:43:46 -06:00
|
|
|
}
|
2013-09-06 11:56:19 -06:00
|
|
|
};
|
|
|
|
|
2010-06-04 13:56:34 -06:00
|
|
|
class IniFile
|
2008-12-07 21:46:09 -07:00
|
|
|
{
|
2010-06-03 12:05:08 -06:00
|
|
|
public:
|
2016-06-24 02:43:46 -06:00
|
|
|
class Section
|
|
|
|
{
|
|
|
|
friend class IniFile;
|
|
|
|
|
|
|
|
public:
|
2017-03-22 16:19:53 -06:00
|
|
|
Section();
|
|
|
|
explicit Section(std::string name_);
|
2019-06-16 15:28:00 -06:00
|
|
|
bool Exists(std::string_view key) const;
|
|
|
|
bool Delete(std::string_view key);
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2018-12-26 19:15:32 -07:00
|
|
|
void Set(const std::string& key, std::string new_value);
|
|
|
|
|
2018-06-03 06:18:58 -06:00
|
|
|
template <typename T>
|
2018-12-26 19:15:32 -07:00
|
|
|
void Set(const std::string& key, T&& new_value)
|
2018-06-03 06:18:58 -06:00
|
|
|
{
|
2018-12-26 19:15:32 -07:00
|
|
|
Set(key, ValueToString(std::forward<T>(new_value)));
|
2018-06-03 06:18:58 -06:00
|
|
|
}
|
2016-06-24 02:43:46 -06:00
|
|
|
|
|
|
|
template <typename T>
|
2018-12-26 19:15:32 -07:00
|
|
|
void Set(const std::string& key, T&& new_value, const std::common_type_t<T>& default_value)
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
2018-06-03 06:18:58 -06:00
|
|
|
if (new_value != default_value)
|
2018-12-26 19:15:32 -07:00
|
|
|
Set(key, std::forward<T>(new_value));
|
2016-06-24 02:43:46 -06:00
|
|
|
else
|
|
|
|
Delete(key);
|
|
|
|
}
|
|
|
|
|
2019-06-16 15:28:00 -06:00
|
|
|
bool Get(std::string_view key, std::string* value,
|
2018-12-26 19:15:32 -07:00
|
|
|
const std::string& default_value = NULL_STRING) const;
|
|
|
|
|
2018-06-03 06:39:38 -06:00
|
|
|
template <typename T>
|
2019-06-16 15:28:00 -06:00
|
|
|
bool Get(std::string_view key, T* value, const std::common_type_t<T>& default_value = {}) const
|
2018-06-03 06:39:38 -06:00
|
|
|
{
|
|
|
|
std::string temp;
|
|
|
|
bool retval = Get(key, &temp);
|
|
|
|
if (retval && TryParse(temp, value))
|
|
|
|
return true;
|
|
|
|
*value = default_value;
|
|
|
|
return false;
|
|
|
|
}
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2018-12-26 19:15:32 -07:00
|
|
|
void SetLines(std::vector<std::string> lines);
|
2016-02-10 11:19:15 -07:00
|
|
|
bool GetLines(std::vector<std::string>* lines, const bool remove_comments = true) const;
|
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
bool operator<(const Section& other) const { return name < other.name; }
|
2016-01-14 15:38:06 -07:00
|
|
|
using SectionMap = std::map<std::string, std::string, CaseInsensitiveStringCompare>;
|
|
|
|
|
|
|
|
const std::string& GetName() const { return name; }
|
|
|
|
const SectionMap& GetValues() const { return values; }
|
2016-02-10 11:19:15 -07:00
|
|
|
bool HasLines() const { return !m_lines.empty(); }
|
2018-04-12 06:18:04 -06:00
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
protected:
|
|
|
|
std::string name;
|
|
|
|
|
|
|
|
std::vector<std::string> keys_order;
|
2016-01-14 15:38:06 -07:00
|
|
|
SectionMap values;
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2016-02-10 11:19:15 -07:00
|
|
|
std::vector<std::string> m_lines;
|
2016-06-24 02:43:46 -06:00
|
|
|
};
|
|
|
|
|
2019-06-07 16:25:32 -06:00
|
|
|
IniFile();
|
|
|
|
~IniFile();
|
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
/**
|
|
|
|
* Loads sections and keys.
|
|
|
|
* @param filename filename of the ini file which should be loaded
|
|
|
|
* @param keep_current_data If true, "extends" the currently loaded list of sections and keys with
|
|
|
|
* the loaded data (and replaces existing entries). If false, existing data will be erased.
|
|
|
|
* @warning Using any other operations than "Get*" and "Exists" is untested and will behave
|
|
|
|
* unexpectedly
|
|
|
|
* @todo This really is just a hack to support having two levels of gameinis (defaults and
|
|
|
|
* user-specified) and should eventually be replaced with a less stupid system.
|
|
|
|
*/
|
|
|
|
bool Load(const std::string& filename, bool keep_current_data = false);
|
|
|
|
|
|
|
|
bool Save(const std::string& filename);
|
|
|
|
|
|
|
|
// Returns true if key exists in section
|
2019-06-16 15:28:00 -06:00
|
|
|
bool Exists(std::string_view section_name, std::string_view key) const;
|
2016-06-24 02:43:46 -06:00
|
|
|
|
|
|
|
template <typename T>
|
2019-06-16 15:28:00 -06:00
|
|
|
bool GetIfExists(std::string_view section_name, std::string_view key, T* value)
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
2019-06-16 15:28:00 -06:00
|
|
|
if (Exists(section_name, key))
|
|
|
|
return GetOrCreateSection(section_name)->Get(key, value);
|
2016-06-24 02:43:46 -06:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2019-06-16 15:28:00 -06:00
|
|
|
bool GetIfExists(std::string_view section_name, std::string_view key, T* value, T default_value)
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
2019-06-16 15:28:00 -06:00
|
|
|
if (Exists(section_name, key))
|
|
|
|
return GetOrCreateSection(section_name)->Get(key, value, default_value);
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2019-06-16 15:28:00 -06:00
|
|
|
*value = default_value;
|
2016-06-24 02:43:46 -06:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-06-16 15:28:00 -06:00
|
|
|
bool GetKeys(std::string_view section_name, std::vector<std::string>* keys) const;
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2019-06-16 15:28:00 -06:00
|
|
|
void SetLines(std::string_view section_name, const std::vector<std::string>& lines);
|
|
|
|
void SetLines(std::string_view section_name, std::vector<std::string>&& lines);
|
|
|
|
bool GetLines(std::string_view section_name, std::vector<std::string>* lines,
|
|
|
|
bool remove_comments = true) const;
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2019-06-16 15:28:00 -06:00
|
|
|
bool DeleteKey(std::string_view section_name, std::string_view key);
|
|
|
|
bool DeleteSection(std::string_view section_name);
|
2016-06-24 02:43:46 -06:00
|
|
|
|
|
|
|
void SortSections();
|
|
|
|
|
2019-06-16 15:28:00 -06:00
|
|
|
Section* GetOrCreateSection(std::string_view section_name);
|
2016-06-24 02:43:46 -06:00
|
|
|
|
|
|
|
// This function is related to parsing data from lines of INI files
|
|
|
|
// It's used outside of IniFile, which is why it is exposed publicly
|
|
|
|
// In particular it is used in PostProcessing for its configuration
|
2019-07-08 05:35:53 -06:00
|
|
|
static void ParseLine(std::string_view line, std::string* keyOut, std::string* valueOut);
|
2014-07-29 10:47:56 -06:00
|
|
|
|
2016-01-14 15:38:06 -07:00
|
|
|
const std::list<Section>& GetSections() const { return sections; }
|
2018-04-12 06:18:04 -06:00
|
|
|
|
2008-12-07 21:46:09 -07:00
|
|
|
private:
|
2016-06-24 02:43:46 -06:00
|
|
|
std::list<Section> sections;
|
2008-12-07 21:46:09 -07:00
|
|
|
|
2019-06-16 15:28:00 -06:00
|
|
|
const Section* GetSection(std::string_view section_name) const;
|
|
|
|
Section* GetSection(std::string_view section_name);
|
2014-03-29 04:05:44 -06:00
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
static const std::string& NULL_STRING;
|
2008-12-07 21:46:09 -07:00
|
|
|
};
|