Air-Trap 1.0.0
A multiplayer R-Type clone game engine built with C++23 and ECS architecture
Loading...
Searching...
No Matches
SimpleJsonParser.hpp
Go to the documentation of this file.
1/*
2** SimpleJsonParser.hpp
3** Minimal JSON parser for weapon configs (no external dependencies)
4*/
5#pragma once
6
7#include <string>
8#include <unordered_map>
9#include <fstream>
10#include <sstream>
11#include <cctype>
12
13namespace rtp::config {
14
16public:
17 std::unordered_map<std::string, std::string> data;
18
19 bool parse(const std::string& filepath) {
20 std::ifstream file(filepath);
21 if (!file.is_open()) return false;
22
23 std::stringstream buffer;
24 buffer << file.rdbuf();
25 std::string content = buffer.str();
26
27 return parseObject(content);
28 }
29
30 bool parseContent(const std::string& jsonContent) {
31 return parseObject(jsonContent);
32 }
33
34 std::string getString(const std::string& key, const std::string& defaultVal = "") const {
35 auto it = data.find(key);
36 return (it != data.end()) ? it->second : defaultVal;
37 }
38
39 int getInt(const std::string& key, int defaultVal = 0) const {
40 auto it = data.find(key);
41 if (it == data.end()) return defaultVal;
42 try {
43 return std::stoi(it->second);
44 } catch (...) {
45 return defaultVal;
46 }
47 }
48
49 float getFloat(const std::string& key, float defaultVal = 0.0f) const {
50 auto it = data.find(key);
51 if (it == data.end()) return defaultVal;
52 try {
53 return std::stof(it->second);
54 } catch (...) {
55 return defaultVal;
56 }
57 }
58
59 bool getBool(const std::string& key, bool defaultVal = false) const {
60 auto it = data.find(key);
61 if (it == data.end()) return defaultVal;
62 return it->second == "true" || it->second == "1";
63 }
64
65private:
66 bool parseObject(const std::string& content) {
67 size_t pos = 0;
68 skipWhitespace(content, pos);
69
70 if (pos >= content.size() || content[pos] != '{') return false;
71 pos++; // skip '{'
72
73 while (pos < content.size()) {
74 skipWhitespace(content, pos);
75 if (pos >= content.size()) break;
76 if (content[pos] == '}') break;
77
78 // Parse key
79 std::string key = parseString(content, pos);
80 if (key.empty()) break;
81
82 skipWhitespace(content, pos);
83 if (pos >= content.size() || content[pos] != ':') break;
84 pos++; // skip ':'
85
86 skipWhitespace(content, pos);
87
88 // Parse value (can be string, number, bool, or nested object)
89 if (content[pos] == '{') {
90 // Nested object - store raw content for later parsing
91 std::string nestedContent = extractNestedObject(content, pos);
92 data[key] = nestedContent;
93 } else {
94 std::string value = parseValue(content, pos);
95 data[key] = value;
96 }
97
98 skipWhitespace(content, pos);
99 if (pos < content.size() && content[pos] == ',') pos++;
100 }
101
102 return true;
103 }
104
105 void skipWhitespace(const std::string& str, size_t& pos) {
106 while (pos < str.size() && std::isspace(str[pos])) pos++;
107 }
108
109 std::string parseString(const std::string& str, size_t& pos) {
110 if (pos >= str.size() || str[pos] != '"') return "";
111 pos++; // skip opening quote
112
113 std::string result;
114 while (pos < str.size() && str[pos] != '"') {
115 if (str[pos] == '\\' && pos + 1 < str.size()) {
116 pos++;
117 }
118 result += str[pos++];
119 }
120
121 if (pos < str.size()) pos++; // skip closing quote
122 return result;
123 }
124
125 std::string parseValue(const std::string& str, size_t& pos) {
126 if (pos >= str.size()) return "";
127
128 if (str[pos] == '"') {
129 return parseString(str, pos);
130 }
131
132 // Parse number or boolean
133 std::string result;
134 while (pos < str.size() && str[pos] != ',' && str[pos] != '}' && str[pos] != ']') {
135 if (!std::isspace(str[pos])) {
136 result += str[pos];
137 }
138 pos++;
139 }
140 return result;
141 }
142
143 std::string extractNestedObject(const std::string& str, size_t& pos) {
144 if (pos >= str.size() || str[pos] != '{') return "";
145
146 size_t start = pos;
147 int braceCount = 0;
148
149 while (pos < str.size()) {
150 if (str[pos] == '{') braceCount++;
151 else if (str[pos] == '}') {
152 braceCount--;
153 if (braceCount == 0) {
154 pos++;
155 return str.substr(start, pos - start);
156 }
157 }
158 pos++;
159 }
160
161 return "";
162 }
163
164public:
165 const std::unordered_map<std::string, std::string>& getData() const { return data; }
166};
167
168// Helper to parse nested weapon objects from weapons.json
169inline std::unordered_map<std::string, SimpleJson> parseWeaponsFile(const std::string& filepath = "config/common/weapons.json") {
170 std::unordered_map<std::string, SimpleJson> weapons;
171
172 std::ifstream file(filepath);
173 if (!file.is_open()) return weapons;
174
175 std::stringstream buffer;
176 buffer << file.rdbuf();
177 std::string content = buffer.str();
178
179 size_t pos = 0;
180 while (pos < content.size() && std::isspace(content[pos])) pos++;
181 if (pos >= content.size() || content[pos] != '{') return weapons;
182 pos++;
183
184 while (pos < content.size()) {
185 while (pos < content.size() && std::isspace(content[pos])) pos++;
186 if (pos >= content.size() || content[pos] == '}') break;
187
188 // Parse weapon name (key)
189 if (content[pos] != '"') break;
190 pos++;
191 std::string weaponName;
192 while (pos < content.size() && content[pos] != '"') {
193 weaponName += content[pos++];
194 }
195 if (pos < content.size()) pos++; // skip closing quote
196
197 while (pos < content.size() && std::isspace(content[pos])) pos++;
198 if (pos >= content.size() || content[pos] != ':') break;
199 pos++;
200
201 while (pos < content.size() && std::isspace(content[pos])) pos++;
202
203 // Extract weapon object
204 if (content[pos] == '{') {
205 size_t objStart = pos;
206 int braceCount = 0;
207 while (pos < content.size()) {
208 if (content[pos] == '{') braceCount++;
209 else if (content[pos] == '}') {
210 braceCount--;
211 if (braceCount == 0) {
212 pos++;
213 std::string weaponContent = content.substr(objStart, pos - objStart);
214 SimpleJson weaponData;
215 weaponData.parseContent(weaponContent);
216 weapons[weaponName] = weaponData;
217 break;
218 }
219 }
220 pos++;
221 }
222 }
223
224 while (pos < content.size() && std::isspace(content[pos])) pos++;
225 if (pos < content.size() && content[pos] == ',') pos++;
226 }
227
228 return weapons;
229}
230
231} // namespace rtp::config
bool parseContent(const std::string &jsonContent)
std::string getString(const std::string &key, const std::string &defaultVal="") const
std::unordered_map< std::string, std::string > data
std::string parseValue(const std::string &str, size_t &pos)
bool getBool(const std::string &key, bool defaultVal=false) const
std::string parseString(const std::string &str, size_t &pos)
const std::unordered_map< std::string, std::string > & getData() const
int getInt(const std::string &key, int defaultVal=0) const
std::string extractNestedObject(const std::string &str, size_t &pos)
void skipWhitespace(const std::string &str, size_t &pos)
bool parse(const std::string &filepath)
bool parseObject(const std::string &content)
float getFloat(const std::string &key, float defaultVal=0.0f) const
File : SpriteMapConfig.hpp License: MIT Author : GitHub Copilot Date : 18/01/2026.
std::unordered_map< std::string, SimpleJson > parseWeaponsFile(const std::string &filepath="config/common/weapons.json")