5#include "../include/TiledLevelLoader.h"
6#include "../include/TiledDecoder.h"
7#include "../include/TilesetCache.h"
8#include "../include/TilesetParser.h"
9#include "../../system/system_utils.h"
12#include "../../third_party/nlohmann/json.hpp"
13#include "../third_party/tinyxml2/tinyxml2.h"
30 SYSTEM_LOG <<
"TiledLevelLoader: Loading map from " << filepath << std::endl;
33 size_t dotPos = filepath.find_last_of(
'.');
34 if (
dotPos == std::string::npos) {
35 lastError_ =
"No file extension in " + filepath;
40 std::string
ext = filepath.substr(
dotPos);
47 tinyxml2::XMLDocument
doc;
48 if (
doc.LoadFile(filepath.c_str()) != tinyxml2::XML_SUCCESS) {
49 lastError_ =
"Failed to load TMX file: " + filepath;
56 else if (
ext ==
".tmj" ||
ext ==
".json") {
60 lastError_ =
"Failed to read file: " + filepath;
67 }
catch (
const std::exception&
e) {
68 lastError_ = std::string(
"JSON parse error: ") +
e.what();
76 lastError_ =
"Unknown map format: " +
ext +
" (expected .tmx, .tmj, or .json)";
82 SYSTEM_LOG <<
"TiledLevelLoader: Failed to parse map" << std::endl;
87 for (
auto& tileset :
outMap.tilesets) {
88 if (!tileset.source.empty()) {
99 outMap.CalculateAllLastGids();
102 for (
const auto& tileset :
outMap.tilesets) {
103 SYSTEM_LOG <<
"TiledLevelLoader: Tileset '" << tileset.name
104 <<
"' - firstgid=" << tileset.firstgid
105 <<
", lastgid=" << tileset.lastgid
106 <<
", tilecount=" << tileset.tilecount << std::endl;
109 SYSTEM_LOG <<
"TiledLevelLoader: Successfully loaded map" << std::endl;
133 map.compressionlevel =
GetInt(
j,
"compressionlevel", -1);
140 map.nextlayerid =
GetInt(
j,
"nextlayerid", 1);
141 map.nextobjectid =
GetInt(
j,
"nextobjectid", 1);
145 for (
size_t i = 0;
i <
j[
"tilesets"].size(); ++
i) {
149 map.tilesets.push_back(tileset);
156 for (
size_t i = 0;
i <
j[
"layers"].size(); ++
i) {
158 std::shared_ptr<TiledLayer> layer;
160 map.layers.push_back(layer);
175 layer = std::make_shared<TiledLayer>();
179 layer->visible =
GetBool(
j,
"visible",
true);
180 layer->opacity =
GetFloat(
j,
"opacity", 1.0f);
181 layer->offsetx =
GetFloat(
j,
"offsetx", 0.0f);
182 layer->offsety =
GetFloat(
j,
"offsety", 0.0f);
183 layer->parallaxx =
GetFloat(
j,
"parallaxx", 1.0f);
184 layer->parallaxy =
GetFloat(
j,
"parallaxy", 1.0f);
196 else if (
typeStr ==
"objectgroup") {
200 else if (
typeStr ==
"imagelayer") {
228 for (
size_t i = 0;
i <
j[
"chunks"].size(); ++
i) {
250 for (
size_t i = 0;
i <
j[
"objects"].size(); ++
i) {
254 layer.
objects.push_back(
object);
273 for (
size_t i = 0;
i <
j[
"layers"].size(); ++
i) {
294 object.rotation =
GetFloat(
j,
"rotation");
296 object.visible =
GetBool(
j,
"visible",
true);
305 else if (
HasKey(
j,
"polygon")) {
308 for (
size_t i = 0;
i <
j[
"polygon"].size(); ++
i) {
313 object.polygon.push_back(
pt);
317 else if (
HasKey(
j,
"polyline")) {
320 for (
size_t i = 0;
i <
j[
"polyline"].size(); ++
i) {
325 object.polyline.push_back(
pt);
331 object.text =
GetString(
j[
"text"],
"text",
"");
351 if (!tileset.
source.empty()) {
371 const auto&
offset =
j[
"tileoffset"];
374 SYSTEM_LOG <<
"TiledLevelLoader: Parsed embedded tileset tileoffset ("
376 <<
") for tileset '" << tileset.
name <<
"'" << std::endl;
392 for (
size_t i = 0;
i <
j[
"tiles"].size(); ++
i) {
419 SYSTEM_LOG <<
"TiledLevelLoader: Loading external tileset from " << filepath << std::endl;
426 std::string source = tileset.
source;
432 SYSTEM_LOG <<
"TiledLevelLoader: External tileset loaded successfully"
433 <<
" - firstgid=" << firstgid
440 SYSTEM_LOG <<
"TiledLevelLoader: Failed to load external tileset from " << filepath
441 <<
" - File may not exist, be corrupted, or have invalid format" << std::endl;
442 lastError_ =
"Failed to load or parse external tileset: " + filepath;
458 std::string
dataStr =
j[
"data"].get<std::string>();
463 SYSTEM_LOG <<
"TiledLevelLoader: ERROR - Failed to decode chunk data at ("
472 for (
size_t i = 0;
i <
j[
"data"].size(); ++
i) {
474 if (
val.is_number()) {
487 SYSTEM_LOG <<
"TiledLevelLoader: ERROR - Chunk data size mismatch at ("
500 catch (
const std::exception&
e) {
501 SYSTEM_LOG <<
"TiledLevelLoader: Failed to parse chunk at (" <<
chunk.x <<
", " <<
chunk.y <<
"): " <<
e.what() << std::endl;
508 if (!
j.is_array())
return;
510 for (
size_t i = 0;
i <
j.size(); ++
i) {
552 SYSTEM_LOG <<
"TiledLevelLoader: Layer '" << layer.
name <<
"' has no 'data' field" << std::endl;
558 std::string
dataStr =
j[
"data"].get<std::string>();
563 SYSTEM_LOG <<
"TiledLevelLoader: ERROR - Failed to decode tile data for layer '"
565 <<
", compression=" << layer.
compression <<
")" << std::endl;
566 lastError_ =
"Failed to decode tile data for layer: " + layer.
name;
572 for (
size_t i = 0;
i <
j[
"data"].size(); ++
i) {
574 if (
val.is_number()) {
587 SYSTEM_LOG <<
"TiledLevelLoader: ERROR - Data size mismatch for layer '" << layer.
name <<
"'"
590 <<
"\n Encoding: " << layer.
encoding
593 lastError_ =
"Data size mismatch for layer '" + layer.
name +
"': expected "
598 SYSTEM_LOG <<
"TiledLevelLoader: Successfully parsed layer '" << layer.
name
599 <<
"' with " <<
actualSize <<
" tiles" << std::endl;
610 tinyxml2::XMLDocument*
doc =
static_cast<tinyxml2::XMLDocument*
>(
docPtr);
611 tinyxml2::XMLElement*
mapElement =
doc->FirstChildElement(
"map");
644 map.compressionlevel =
mapElement->IntAttribute(
"compressionlevel", -1);
656 map.nextlayerid =
mapElement->IntAttribute(
"nextlayerid", 1);
657 map.nextobjectid =
mapElement->IntAttribute(
"nextobjectid", 1);
660 if (
map.width == 0 ||
map.height == 0 ||
map.tilewidth == 0 ||
map.tileheight == 0) {
673 map.tilesets.push_back(tileset);
686 std::shared_ptr<TiledLayer> layer;
688 map.layers.push_back(layer);
704 tinyxml2::XMLElement*
tsElement =
static_cast<tinyxml2::XMLElement*
>(
element);
729 SYSTEM_LOG <<
"TiledLevelLoader (TMX): Parsed tileoffset ("
731 <<
") for tileset '" << tileset.
name <<
"'" << std::endl;
790 layer = std::make_shared<TiledLayer>();
797 layer->visible =
layerElement->IntAttribute(
"visible", 1) != 0;
798 layer->opacity =
layerElement->FloatAttribute(
"opacity", 1.0f);
799 layer->offsetx =
layerElement->FloatAttribute(
"offsetx", 0.0f);
800 layer->offsety =
layerElement->FloatAttribute(
"offsety", 0.0f);
801 layer->parallaxx =
layerElement->FloatAttribute(
"parallaxx", 1.0f);
802 layer->parallaxy =
layerElement->FloatAttribute(
"parallaxy", 1.0f);
880 layer.
objects.push_back(
object);
930 object.id =
objElement->IntAttribute(
"id", 0);
933 object.x =
objElement->FloatAttribute(
"x", 0.0f);
934 object.y =
objElement->FloatAttribute(
"y", 0.0f);
935 object.width =
objElement->FloatAttribute(
"width", 0.0f);
936 object.height =
objElement->FloatAttribute(
"height", 0.0f);
937 object.rotation =
objElement->FloatAttribute(
"rotation", 0.0f);
938 object.gid =
objElement->IntAttribute(
"gid", 0);
939 object.visible =
objElement->IntAttribute(
"visible", 1) != 0;
964 if (
commaPos == std::string::npos)
break;
969 object.polygon.push_back(
Point(x, y));
971 if (
spacePos == std::string::npos)
break;
986 if (
commaPos == std::string::npos)
break;
991 object.polyline.push_back(
Point(x, y));
993 if (
spacePos == std::string::npos)
break;
1037 layer.
data.push_back(gid);
1045 size_t end =
dataStr.find_last_not_of(
" \t\n\r");
1046 if (
start != std::string::npos &&
end != std::string::npos) {
1065 if (!
token.empty()) {
1068 layer.
data.push_back(gid);
1069 }
catch (
const std::exception&
e) {
1070 SYSTEM_LOG <<
"TiledLevelLoader: Failed to parse CSV token: " <<
token <<
" Error : " <<
e.what() << std::endl;
1074 if (
commaPos == std::string::npos)
break;
1078 else if (layer.
encoding ==
"base64") {
1089 SYSTEM_LOG <<
"TiledLevelLoader: ERROR - Data size mismatch for layer '" << layer.
name <<
"'"
1091 <<
"\n Actual: " <<
actualSize <<
" tiles" << std::endl;
1092 lastError_ =
"Data size mismatch for layer '" + layer.
name +
"'";
1096 SYSTEM_LOG <<
"TiledLevelLoader: Successfully parsed TMX layer '" << layer.
name
1097 <<
"' with " <<
actualSize <<
" tiles" << std::endl;
1119 size_t end =
dataStr.find_last_not_of(
" \t\n\r");
1120 if (
start != std::string::npos &&
end != std::string::npos) {
1131 SYSTEM_LOG <<
"TiledLevelLoader: ERROR - Chunk data size mismatch at ("
1134 <<
"\n Actual: " <<
actualSize <<
" tiles" << std::endl;
1169 else if (
typeStr ==
"float") {
1177 else if (
typeStr ==
"color") {
1214 size_t pos = filepath.find_last_of(
"/\\");
1215 if (
pos == std::string::npos) {
1218 return filepath.substr(0,
pos);
1223 std::ifstream
file(filepath, std::ios::binary);
1224 if (!
file.is_open()) {
1225 SYSTEM_LOG <<
"TiledLevelLoader: Failed to open file: " << filepath << std::endl;
1229 std::stringstream
buffer;
ComponentTypeID GetComponentTypeID_Static()
static std::vector< uint32_t > DecodeTileData(const std::string &data, const std::string &encoding, const std::string &compression)
bool ReadFile(const std::string &filepath, std::string &outContent)
bool ParseMapXML(void *doc, TiledMap &map)
bool ParseImageLayer(const json &j, TiledLayer &layer)
bool ParseTilesetXML(void *element, TiledTileset &tileset, const std::string &mapDir)
bool ParseTileLayerXML(void *element, TiledLayer &layer)
bool LoadExternalTileset(const std::string &filepath, TiledTileset &tileset)
bool ParseTileData(const json &j, TiledLayer &layer)
void ParseProperties(const json &j, std::map< std::string, TiledProperty > &properties)
bool ParseMap(const json &j, TiledMap &map)
bool ParseObject(const json &j, TiledObject &object)
bool ParseLayer(const json &j, std::shared_ptr< TiledLayer > &layer)
bool ParseObjectLayerXML(void *element, TiledLayer &layer)
bool ParseImageLayerXML(void *element, TiledLayer &layer)
void ParsePropertiesXML(void *element, std::map< std::string, TiledProperty > &properties)
bool ParseChunk(const json &j, TiledChunk &chunk, const std::string &layerEncoding, const std::string &layerCompression)
bool ParseTileset(const json &j, TiledTileset &tileset, const std::string &mapDir)
void ParseProperty(const json &j, TiledProperty &prop)
bool ParseChunkXML(void *element, TiledChunk &chunk, const std::string &layerEncoding, const std::string &layerCompression)
std::string GetDirectory(const std::string &filepath)
std::string ResolvePath(const std::string &mapDir, const std::string &relativePath)
bool ParseTileLayer(const json &j, TiledLayer &layer)
bool ParseObjectXML(void *element, TiledObject &object)
void ParsePropertyXML(void *element, TiledProperty &prop)
bool ParseLayerXML(void *element, std::shared_ptr< TiledLayer > &layer)
bool ParseObjectLayer(const json &j, TiledLayer &layer)
bool LoadFromFile(const std::string &filepath, TiledMap &outMap)
bool ParseGroupLayer(const json &j, TiledLayer &layer)
bool ParseGroupLayerXML(void *element, TiledLayer &layer)
bool ParseTileDataXML(void *element, TiledLayer &layer)
std::shared_ptr< TiledTileset > GetTileset(const std::string &filepath)
static TilesetCache & GetInstance()
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)
int ParseColor(const std::string &colorStr)
std::vector< TiledObject > objects
std::vector< uint32_t > data
std::vector< TiledChunk > chunks
std::vector< std::shared_ptr< TiledLayer > > layers
std::string transparentcolor
std::vector< TiledTile > tiles
std::map< std::string, TiledProperty > properties