ASW Lib
A.D.S. Games SDL Wrapper Library. A library targeted at Allegro4 users who want to switch to SDL3 and use modern c++.
Loading...
Searching...
No Matches
config.cpp
Go to the documentation of this file.
2
3#include <SDL3/SDL.h>
4#include <algorithm>
5#include <cctype>
6#include <cerrno>
7#include <climits>
8#include <cstdlib>
9#include <map>
10#include <string_view>
11
12#include "./asw/modules/log.h"
13
14namespace {
15std::map<std::string, std::string, std::less<>> settings;
16
17std::string_view trim(std::string_view text)
18{
19 const auto is_space = [](char c) { return std::isspace(static_cast<unsigned char>(c)) != 0; };
20 while (!text.empty() && is_space(text.front())) {
21 text.remove_prefix(1);
22 }
23 while (!text.empty() && is_space(text.back())) {
24 text.remove_suffix(1);
25 }
26 return text;
27}
28
29std::string lower(std::string_view text)
30{
31 std::string result(text);
32 std::ranges::transform(result, result.begin(),
33 [](char c) { return static_cast<char>(std::tolower(static_cast<unsigned char>(c))); });
34 return result;
35}
36
37void parse(std::string_view text, const std::string& path)
38{
39 int line_number = 0;
40 while (!text.empty()) {
41 const auto end = text.find('\n');
42 const auto raw = text.substr(0, end);
43 text = end == std::string_view::npos ? std::string_view { } : text.substr(end + 1);
44 ++line_number;
45
46 const auto line = trim(raw);
47 if (line.empty() || line.front() == '#' || line.front() == ';') {
48 continue;
49 }
50
51 const auto equals = line.find('=');
52 const auto key = trim(line.substr(0, equals));
53 if (equals == std::string_view::npos || key.empty()) {
54 asw::log::warn("{}:{}: ignoring line, use key = value", path, line_number);
55 continue;
56 }
57
58 const auto value = trim(line.substr(equals + 1));
59 settings.insert_or_assign(std::string(key), std::string(value));
60 asw::log::info("Config: {} = {}", key, value);
61 }
62}
63} // namespace
64
66{
67 settings.clear();
68
69 const char* path = SDL_getenv(PATH_VARIABLE);
70 if (path != nullptr && *path != '\0') {
71 load(path);
72 }
73}
74
76{
77 settings.clear();
78}
79
80bool asw::config::load(const std::string& path)
81{
82 std::size_t size = 0;
83 void* data = SDL_LoadFile(path.c_str(), &size);
84 if (data == nullptr) {
85 asw::log::warn("Could not read config {}: {}", path, SDL_GetError());
86 return false;
87 }
88
89 parse(std::string_view(static_cast<const char*>(data), size), path);
90 SDL_free(data);
91 return true;
92}
93
94bool asw::config::has(const std::string& key)
95{
96 return settings.contains(key);
97}
98
99std::optional<std::string> asw::config::get_string(const std::string& key)
100{
101 const auto it = settings.find(key);
102 if (it == settings.end()) {
103 return std::nullopt;
104 }
105 return it->second;
106}
107
108std::optional<bool> asw::config::get_bool(const std::string& key)
109{
110 const auto value = get_string(key);
111 if (!value) {
112 return std::nullopt;
113 }
114
115 const auto v = lower(*value);
116 if (v == "1" || v == "true" || v == "yes" || v == "on") {
117 return true;
118 }
119 if (v == "0" || v == "false" || v == "no" || v == "off") {
120 return false;
121 }
122
123 asw::log::warn("Ignoring config {} = {}, use true or false", key, *value);
124 return std::nullopt;
125}
126
127std::optional<int> asw::config::get_int(const std::string& key)
128{
129 const auto value = get_string(key);
130 if (!value) {
131 return std::nullopt;
132 }
133
134 char* end = nullptr;
135 errno = 0;
136 const long parsed = std::strtol(value->c_str(), &end, 10);
137 if (value->empty() || *end != '\0' || errno == ERANGE || parsed < INT_MIN || parsed > INT_MAX) {
138 asw::log::warn("Ignoring config {} = {}, use a whole number", key, *value);
139 return std::nullopt;
140 }
141
142 return static_cast<int>(parsed);
143}
144
145std::optional<float> asw::config::get_float(const std::string& key)
146{
147 const auto value = get_string(key);
148 if (!value) {
149 return std::nullopt;
150 }
151
152 char* end = nullptr;
153 errno = 0;
154 const float parsed = std::strtof(value->c_str(), &end);
155 if (value->empty() || *end != '\0' || errno == ERANGE) {
156 asw::log::warn("Ignoring config {} = {}, use a number", key, *value);
157 return std::nullopt;
158 }
159
160 return parsed;
161}
Settings from a config file, set by whoever runs the game.
Structured logging system.
std::map< std::string, std::string, std::less<> > settings
Definition config.cpp:15
std::string_view trim(std::string_view text)
Definition config.cpp:17
void parse(std::string_view text, const std::string &path)
Definition config.cpp:37
std::string lower(std::string_view text)
Definition config.cpp:29
void _shutdown()
Remove all settings. Called by asw::core::shutdown().
Definition config.cpp:75
std::optional< std::string > get_string(const std::string &key)
Get a setting as text.
Definition config.cpp:99
constexpr const char * PATH_VARIABLE
Name of the environment variable that holds the config file path.
Definition config.h:18
std::optional< float > get_float(const std::string &key)
Get a setting as a number.
Definition config.cpp:145
bool has(const std::string &key)
Check if a setting is set.
Definition config.cpp:94
std::optional< int > get_int(const std::string &key)
Get a setting as a whole number.
Definition config.cpp:127
bool load(const std::string &path)
Load settings from a file. Settings already loaded stay, unless the file sets them again.
Definition config.cpp:80
void _init()
Load the file named by ASW_CONFIG, if it is set. Called by asw::core::init() before the window opens.
Definition config.cpp:65
std::optional< bool > get_bool(const std::string &key)
Get a setting as true or false. 1, true, yes and on are true. 0, false, no and off are false....
Definition config.cpp:108
void info(const std::string &message)
Log an info message.
Definition log.cpp:186
void warn(const std::string &message)
Log a warning message.
Definition log.cpp:191