Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
TilesetParser.cpp
Go to the documentation of this file.
1/*
2 * TilesetParser.cpp - Parse external tileset files
3 */
4
5#include "../include/TilesetParser.h"
6#include "../include/TiledJsonHelper.h"
7#include "../../system/system_utils.h"
8#include <fstream>
9#include <sstream>
10
11#define MINIZ_NO_ARCHIVE
12#include "../third_party/miniz/miniz.h"
13#include "../third_party/tinyxml2/tinyxml2.h"
14
15namespace Olympe {
16namespace Tiled {
17
21
25
26 bool TilesetParser::ParseFile(const std::string& filepath, TiledTileset& tileset)
27 {
28 // Detect format by extension
29 size_t dotPos = filepath.find_last_of('.');
30 if (dotPos == std::string::npos) {
31 SYSTEM_LOG << "TilesetParser: No file extension in " << filepath << std::endl;
32 return false;
33 }
34
35 std::string ext = filepath.substr(dotPos);
36
37 if (ext == ".tsx") {
38 return ParseTSX(filepath, tileset);
39 }
40 else if (ext == ".tsj" || ext == ".json") {
41 return ParseTSJ(filepath, tileset);
42 }
43 else {
44 SYSTEM_LOG << "TilesetParser: Unknown tileset format: " << ext << std::endl;
45 return false;
46 }
47 }
48
49 bool TilesetParser::ParseTSX(const std::string& filepath, TiledTileset& tileset)
50 {
51 tinyxml2::XMLDocument doc;
52 if (doc.LoadFile(filepath.c_str()) != tinyxml2::XML_SUCCESS) {
53 SYSTEM_LOG << "TilesetParser: Failed to load TSX file: " << filepath << std::endl;
54 return false;
55 }
56
57 tinyxml2::XMLElement* tsElement = doc.FirstChildElement("tileset");
58 if (!tsElement) {
59 SYSTEM_LOG << "TilesetParser: No <tileset> element in " << filepath << std::endl;
60 return false;
61 }
62
63 // Parse tileset attributes
64 tileset.name = tsElement->Attribute("name") ? tsElement->Attribute("name") : "";
65 tileset.tilewidth = tsElement->IntAttribute("tilewidth", 0);
66 tileset.tileheight = tsElement->IntAttribute("tileheight", 0);
67 tileset.tilecount = tsElement->IntAttribute("tilecount", 0);
68 tileset.columns = tsElement->IntAttribute("columns", 0);
69 tileset.spacing = tsElement->IntAttribute("spacing", 0);
70 tileset.margin = tsElement->IntAttribute("margin", 0);
71
72 // ====================================================================
73 // Parse tileoffset element
74 // This is the CRITICAL value that must be extracted and propagated
75 // ====================================================================
76 tinyxml2::XMLElement* offsetElement = tsElement->FirstChildElement("tileoffset");
77 if (offsetElement) {
78 tileset.tileoffsetX = offsetElement->IntAttribute("x", 0);
79 tileset.tileoffsetY = offsetElement->IntAttribute("y", 0);
80 SYSTEM_LOG << "TilesetParser (TSX): Parsed tileoffset ("
81 << tileset.tileoffsetX << ", " << tileset.tileoffsetY
82 << ") for tileset '" << tileset.name << "'\n";
83 }
84 else
85 {
86 // Explicit defaults when no tileoffset element present
87 tileset.tileoffsetX = 0;
88 tileset.tileoffsetY = 0;
89 }
90
91 // Parse image element (for image-based tilesets)
92 tinyxml2::XMLElement* imageElement = tsElement->FirstChildElement("image");
93 if (imageElement) {
94 tileset.image = imageElement->Attribute("source") ? imageElement->Attribute("source") : "";
95 tileset.imagewidth = imageElement->IntAttribute("width", 0);
96 tileset.imageheight = imageElement->IntAttribute("height", 0);
97 const char* trans = imageElement->Attribute("trans");
98 if (trans) {
99 std::string transStr = trans;
100 tileset.transparentcolor = (transStr[0] == '#') ? transStr : ("#" + transStr);
101 }
102 }
103
104 // Parse tiles (for collection tilesets or tiles with properties)
105 for (tinyxml2::XMLElement* tileElement = tsElement->FirstChildElement("tile");
106 tileElement != nullptr;
107 tileElement = tileElement->NextSiblingElement("tile"))
108 {
110 tile.id = tileElement->IntAttribute("id", 0);
111
112 const char* typeAttr = tileElement->Attribute("type");
113 if (typeAttr) {
114 tile.type = typeAttr;
115 }
116
117 // Parse tile image (for collection tilesets)
118 tinyxml2::XMLElement* tileImageElement = tileElement->FirstChildElement("image");
119 if (tileImageElement) {
120 tile.image = tileImageElement->Attribute("source") ?
121 tileImageElement->Attribute("source") : "";
122 tile.imagewidth = tileImageElement->IntAttribute("width", 0);
123 tile.imageheight = tileImageElement->IntAttribute("height", 0);
124 }
125
126 // Parse properties
127 tinyxml2::XMLElement* propertiesElement = tileElement->FirstChildElement("properties");
128 if (propertiesElement) {
130 }
131
132 tileset.tiles.push_back(tile);
133 }
134
135 // Parse tileset properties
136 tinyxml2::XMLElement* propertiesElement = tsElement->FirstChildElement("properties");
137 if (propertiesElement) {
139 }
140
141 return true;
142 }
143
144 bool TilesetParser::ParseTSJ(const std::string& filepath, TiledTileset& tileset)
145 {
146 std::ifstream file(filepath);
147 if (!file.is_open()) {
148 SYSTEM_LOG << "TilesetParser: Failed to open TSJ file: " << filepath << std::endl;
149 return false;
150 }
151
152 json j;
153 // Remplacer file >> j; par un parseur manuel utilisant le contenu du fichier
154 std::stringstream buffer;
155 buffer << file.rdbuf();
156 try {
157 j = json::parse(buffer.str());
158 } catch (const std::exception& e) {
159 SYSTEM_LOG << "TilesetParser: JSON parse error in " << filepath << ": " << e.what() << std::endl;
160 return false;
161 }
162
163 // Parse tileset data
164 tileset.name = GetString(j, "name");
165 tileset.tilewidth = GetInt(j, "tilewidth");
166 tileset.tileheight = GetInt(j, "tileheight");
167 tileset.tilecount = GetInt(j, "tilecount");
168 tileset.columns = GetInt(j, "columns");
169 tileset.spacing = GetInt(j, "spacing");
170 tileset.margin = GetInt(j, "margin");
171
172 // ====================================================================
173 // Parse tileoffset
174 // This is the CRITICAL value that must be extracted and propagated
175 // ====================================================================
176 if (HasKey(j, "tileoffset"))
177 {
178 const auto& offset = j["tileoffset"];
179 tileset.tileoffsetX = GetInt(offset, "x");
180 tileset.tileoffsetY = GetInt(offset, "y");
181 SYSTEM_LOG << "TilesetParser (TSJ): Parsed tileoffset ("
182 << tileset.tileoffsetX << ", " << tileset.tileoffsetY
183 << ") for tileset '" << tileset.name << "'\n";
184 }
185 else
186 {
187 // Explicit defaults when no tileoffset property present
188 tileset.tileoffsetX = 0;
189 tileset.tileoffsetY = 0;
190 }
191
192 tileset.image = GetString(j, "image");
193 tileset.imagewidth = GetInt(j, "imagewidth");
194 tileset.imageheight = GetInt(j, "imageheight");
195 tileset.transparentcolor = GetString(j, "transparentcolor");
196
197 // Parse tiles
198 if (HasKey(j, "tiles") && j["tiles"].is_array()) {
199 for (const auto& tilePair : j["tiles"].items()) {
202 tileset.tiles.push_back(tile);
203 }
204 }
205
206 // Parse properties
207 if (HasKey(j, "properties")) {
208 ParsePropertiesFromJSON(j["properties"], tileset.properties);
209 }
210
211 return true;
212 }
213
214 void TilesetParser::ParsePropertiesFromXML(void* element, std::map<std::string, TiledProperty>& properties)
215 {
216 tinyxml2::XMLElement* propsElement = static_cast<tinyxml2::XMLElement*>(element);
217
218 for (tinyxml2::XMLElement* propElement = propsElement->FirstChildElement("property");
219 propElement != nullptr;
220 propElement = propElement->NextSiblingElement("property"))
221 {
223 prop.name = propElement->Attribute("name") ? propElement->Attribute("name") : "";
224
225 const char* typeAttr = propElement->Attribute("type");
226 std::string typeStr = typeAttr ? typeAttr : "string";
227
228 if (typeStr == "int") {
229 prop.type = PropertyType::Int;
230 prop.intValue = propElement->IntAttribute("value", 0);
231 }
232 else if (typeStr == "float") {
234 prop.floatValue = propElement->FloatAttribute("value", 0.0f);
235 }
236 else if (typeStr == "bool") {
238 prop.boolValue = propElement->BoolAttribute("value", false);
239 }
240 else if (typeStr == "color") {
242 prop.stringValue = propElement->Attribute("value") ? propElement->Attribute("value") : "";
243 }
244 else if (typeStr == "file") {
246 prop.stringValue = propElement->Attribute("value") ? propElement->Attribute("value") : "";
247 }
248 else {
250 prop.stringValue = propElement->Attribute("value") ? propElement->Attribute("value") : "";
251 }
252
253 properties[prop.name] = prop;
254 }
255 }
256
257 void TilesetParser::ParsePropertiesFromJSON(const json& j, std::map<std::string, TiledProperty>& properties)
258 {
259 if (!j.is_array()) return;
260
261 for (const auto& propJsonPair : j.items()) {
262 const auto& propJson = propJsonPair.second;
264 prop.name = GetString(propJson, "name");
265
266 std::string typeStr = GetString(propJson, "type", "string");
267
268 if (typeStr == "int") {
269 prop.type = PropertyType::Int;
270 prop.intValue = GetInt(propJson, "value");
271 }
272 else if (typeStr == "float") {
274 prop.floatValue = GetFloat(propJson, "value");
275 }
276 else if (typeStr == "bool") {
278 prop.boolValue = GetBool(propJson, "value");
279 }
280 else if (typeStr == "color") {
282 prop.stringValue = GetString(propJson, "value");
283 }
284 else if (typeStr == "file") {
286 prop.stringValue = GetString(propJson, "value");
287 }
288 else {
290 prop.stringValue = GetString(propJson, "value");
291 }
292
293 properties[prop.name] = prop;
294 }
295 }
296
298 {
299 // Not implemented - handled inline in ParseTSX
300 }
301
303 {
304 tile.id = GetInt(j, "id");
305 tile.type = GetString(j, "type");
306 tile.image = GetString(j, "image");
307 tile.imagewidth = GetInt(j, "imagewidth");
308 tile.imageheight = GetInt(j, "imageheight");
309
310 if (HasKey(j, "properties")) {
311 ParsePropertiesFromJSON(j["properties"], tile.properties);
312 }
313 }
314
315} // namespace Tiled
316} // namespace Olympe
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
void ParsePropertiesFromJSON(const nlohmann::json &j, std::map< std::string, TiledProperty > &properties)
void ParseTileFromJSON(const nlohmann::json &j, TiledTile &tile)
void ParseTileFromXML(void *element, TiledTile &tile)
bool ParseTSJ(const std::string &filepath, TiledTileset &tileset)
bool ParseTSX(const std::string &filepath, TiledTileset &tileset)
void ParsePropertiesFromXML(void *element, std::map< std::string, TiledProperty > &properties)
bool ParseFile(const std::string &filepath, TiledTileset &tileset)
nlohmann::json json
std::string GetString(const json &j, const std::string &key, const std::string &defaultValue="")
int GetInt(const json &j, const std::string &key, int defaultValue=0)
bool HasKey(const json &j, const std::string &key)
bool GetBool(const json &j, const std::string &key, bool defaultValue=false)
float GetFloat(const json &j, const std::string &key, float defaultValue=0.0f)
std::vector< TiledTile > tiles
std::map< std::string, TiledProperty > properties
#define SYSTEM_LOG