ManaPlus
mapreader.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2004-2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2018 The ManaPlus Developers
6  *
7  * This file is part of The ManaPlus Client.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "resources/mapreader.h"
24 
25 #include "configuration.h"
26 #ifdef USE_OPENGL
27 #include "graphicsmanager.h"
28 #endif // USE_OPENGL
29 #include "main.h"
30 
32 
35 
36 #include "fs/virtfs/fs.h"
37 
38 #include "resources/map/map.h"
40 #include "resources/map/maplayer.h"
41 #include "resources/map/tileset.h"
42 
43 #include "resources/beingcommon.h"
45 
46 #include "resources/image/image.h"
47 
48 #ifdef USE_OPENGL
49 #include "resources/db/mapdb.h"
52 #endif // USE_OPENGL
53 
55 
57 
59 
60 #include "utils/base64.h"
61 #include "utils/checkutils.h"
62 #include "utils/delete2.h"
63 #include "utils/stringmap.h"
64 
66 
67 #include <zlib.h>
68 
69 #include "debug.h"
70 
71 typedef std::map<std::string, XmlNodePtr>::iterator LayerInfoIterator;
72 typedef std::set<XML::Document*>::iterator DocIterator;
73 
74 #ifdef USE_OPENGL
76 #endif // USE_OPENGL
77 
78 namespace
79 {
80  std::map<std::string, XmlNodePtr> mKnownLayers;
81  std::set<XML::Document*> mKnownDocs;
82 } // namespace
83 
84 static int inflateMemory(unsigned char *restrict const in,
85  const unsigned int inLength,
86  unsigned char *&restrict out,
87  unsigned int &restrict outLength);
88 
89 static int inflateMemory(unsigned char *restrict const in,
90  const unsigned int inLength,
91  unsigned char *&restrict out);
92 
93 static std::string resolveRelativePath(std::string base, std::string relative)
94 {
95  // Remove trailing "/", if present
96  size_t i = base.length();
97  if (base.at(i - 1) == '/')
98  base.erase(i - 1, i);
99 
100  while (relative.substr(0, 3) == "../")
101  {
102  relative.erase(0, 3); // Remove "../"
103  if (!base.empty()) // If base is already empty, we can't trim anymore
104  {
105  i = base.find_last_of('/');
106  if (i == std::string::npos)
107  i = 0;
108  base.erase(i, base.length()); // Remove deepest folder in base
109  }
110  }
111 
112  // Re-add trailing slash, if needed
113  if (!base.empty() && base[base.length() - 1] != '/')
114  base.append("/");
115 
116  return base + relative;
117 }
118 
123 int inflateMemory(unsigned char *restrict const in,
124  const unsigned int inLength,
125  unsigned char *&restrict out,
126  unsigned int &restrict outLength)
127 {
128  int bufferSize = 256 * 1024;
129  out = static_cast<unsigned char*>(calloc(bufferSize, 1));
130 
131  z_stream strm;
132  strm.zalloc = nullptr;
133  strm.zfree = nullptr;
134  strm.opaque = nullptr;
135  strm.next_in = in;
136  strm.avail_in = inLength;
137  strm.next_out = out;
138  strm.avail_out = bufferSize;
139 
140 PRAGMACLANG6GCC(GCC diagnostic push)
141 PRAGMACLANG6GCC(GCC diagnostic ignored "-Wold-style-cast")
142  int ret = inflateInit2(&strm, 15 + 32);
143 PRAGMACLANG6GCC(GCC diagnostic pop)
144 
145  if (ret != Z_OK)
146  return ret;
147 
148  do
149  {
150  if (strm.next_out == nullptr)
151  {
152  inflateEnd(&strm);
153  return Z_MEM_ERROR;
154  }
155 
156  ret = inflate(&strm, Z_NO_FLUSH);
157  if (ret == Z_STREAM_ERROR)
158  return ret;
159 
160  switch (ret)
161  {
162  case Z_NEED_DICT:
163  ret = Z_DATA_ERROR;
165  case Z_DATA_ERROR:
166  case Z_MEM_ERROR:
167  (void) inflateEnd(&strm);
168  return ret;
169  default:
170  break;
171  }
172 
173  if (ret != Z_STREAM_END)
174  {
175  out = static_cast<unsigned char*>(realloc(out, bufferSize * 2));
176 
177  if (out == nullptr)
178  {
179  inflateEnd(&strm);
180  return Z_MEM_ERROR;
181  }
182 
183  strm.next_out = out + CAST_SIZE(bufferSize);
184  strm.avail_out = bufferSize;
185  bufferSize *= 2;
186  }
187  }
188  while (ret != Z_STREAM_END);
189 
190  outLength = bufferSize - strm.avail_out;
191  (void) inflateEnd(&strm);
192  return Z_OK;
193 }
194 
195 int inflateMemory(unsigned char *restrict const in,
196  const unsigned int inLength,
197  unsigned char *&restrict out)
198 {
199  unsigned int outLength = 0;
200  const int ret = inflateMemory(in, inLength, out, outLength);
201 
202  if (ret != Z_OK || (out == nullptr))
203  {
204  if (ret == Z_MEM_ERROR)
205  {
206  reportAlways("Error: Out of memory while decompressing map data!");
207  }
208  else if (ret == Z_VERSION_ERROR)
209  {
210  reportAlways("Error: Incompatible zlib version!");
211  }
212  else if (ret == Z_DATA_ERROR)
213  {
214  reportAlways("Error: Incorrect zlib compressed data!");
215  }
216  else
217  {
218  reportAlways("Error: Unknown error while decompressing map data!");
219  }
220 
221  free(out);
222  out = nullptr;
223  outLength = 0;
224  }
225 
226  return outLength;
227 }
228 
229 void MapReader::addLayerToList(const std::string &fileName,
230  const SkipError skipError)
231 {
232  XML::Document *doc = new XML::Document(fileName,
234  skipError);
235  XmlNodePtrConst node = doc->rootNode();
236  if (node == nullptr)
237  {
238  delete doc;
239  return;
240  }
241 
242  int cnt = 0;
243  for_each_xml_child_node(childNode, node)
244  {
245  if (!xmlNameEqual(childNode, "layer"))
246  continue;
247  std::string name = XML::getProperty(childNode, "name", "");
248  if (name.empty())
249  continue;
250  name = toLower(name);
251  logger->log("found patch layer: " + name);
252  mKnownLayers[name] = childNode;
253  mKnownDocs.insert(doc);
254  cnt ++;
255  }
256  if (cnt == 0)
257  delete doc;
258 }
259 
260 Map *MapReader::readMap(const std::string &restrict filename,
261  const std::string &restrict realFilename)
262 {
263  BLOCK_START("MapReader::readMap str")
264  logger->log("Attempting to read map %s", realFilename.c_str());
265 
266  XML::Document doc(realFilename, UseVirtFs_true, SkipError_false);
267  if (!doc.isLoaded())
268  {
269  BLOCK_END("MapReader::readMap str")
270  return createEmptyMap(filename, realFilename);
271  }
272 
273  XmlNodePtrConst node = doc.rootNode();
274 
275  Map *map = nullptr;
276  // Parse the inflated map data
277  if (node != nullptr)
278  {
279  if (!xmlNameEqual(node, "map"))
280  logger->log("Error: Not a map file (%s)!", realFilename.c_str());
281  else
282  map = readMap(node, realFilename);
283  }
284  else
285  {
286  reportAlways("Error while parsing map file (%s)!",
287  realFilename.c_str());
288  }
289 
290  if (map != nullptr)
291  {
292  map->setProperty("_filename", realFilename);
293  map->setProperty("_realfilename", filename);
294 
295  if (map->getProperty("music", std::string()).empty())
296  updateMusic(map);
297 
298  map->updateConditionLayers();
299  map->preCacheLayers();
300  }
301 
302  BLOCK_END("MapReader::readMap str")
303  return map;
304 }
305 
306 void MapReader::loadLayers(const std::string &path)
307 {
308  BLOCK_START("MapReader::loadLayers")
310  BLOCK_END("MapReader::loadLayers")
311 }
312 
314 {
316  delete (*it);
317  mKnownLayers.clear();
318  mKnownDocs.clear();
319 }
320 
321 static void loadReplaceLayer(const LayerInfoIterator &it,
322  Map *const map) A_NONNULL(2);
323 static void loadReplaceLayer(const LayerInfoIterator &it,
324  Map *const map)
325 {
326  MapReader::readLayer((*it).second, map);
327 }
328 
329 Map *MapReader::readMap(XmlNodePtrConst node, const std::string &path)
330 {
331  if (node == nullptr)
332  return nullptr;
333 
334  BLOCK_START("MapReader::readMap xml")
335  // Take the filename off the path
336  const std::string pathDir = path.substr(0, path.rfind('/') + 1);
337 
338  const int w = XML::getProperty(node, "width", 0);
339  const int h = XML::getProperty(node, "height", 0);
340  const int tilew = XML::getProperty(node, "tilewidth", -1);
341  const int tileh = XML::getProperty(node, "tileheight", -1);
342 
343  const bool showWarps = config.getBoolValue("warpParticle");
344  const std::string warpPath = pathJoin(paths.getStringValue("particles"),
345  paths.getStringValue("portalEffectFile"));
346 
347  if (tilew < 0 || tileh < 0)
348  {
349  reportAlways("MapReader: Warning: "
350  "Uninitialized tile width or height value for map: %s",
351  path.c_str());
352  BLOCK_END("MapReader::readMap xml")
353  return nullptr;
354  }
355 
356  logger->log("loading replace layer list");
357  loadLayers(path + "_replace.d");
358 
359  Map *const map = new Map(path,
360  w, h,
361  tilew, tileh);
362 
363  const std::string fileName = path.substr(path.rfind(dirSeparator) + 1);
364  map->setProperty("shortName", fileName);
365 
366 #ifdef USE_OPENGL
367  BLOCK_START("MapReader::readMap load atlas")
369  {
370  const MapInfo *const info = MapDB::getMapAtlas(fileName);
371  if (info != nullptr)
372  {
374  info->atlas,
375  *info->files));
376  }
377  else
378  {
379  reportAlways("Missing atlas for map: %s",
380  fileName.c_str());
381  }
382  }
383  BLOCK_END("MapReader::readMap load atlas")
384 #endif // USE_OPENGL
385 
386  for_each_xml_child_node(childNode, node)
387  {
388  if (xmlNameEqual(childNode, "tileset"))
389  {
390  Tileset *const tileset = readTileset(childNode, pathDir, map);
391  if (tileset != nullptr)
392  map->addTileset(tileset);
393  }
394  else if (xmlNameEqual(childNode, "layer"))
395  {
396  std::string name = XML::getProperty(childNode, "name", "");
397  name = toLower(name);
398  LayerInfoIterator it = mKnownLayers.find(name);
399  if (it == mKnownLayers.end())
400  {
401  readLayer(childNode, map);
402  }
403  else
404  {
405  logger->log("load replace layer: " + name);
406  loadReplaceLayer(it, map);
407  }
408  }
409  else if (xmlNameEqual(childNode, "properties"))
410  {
411  readProperties(childNode, map);
412  map->setVersion(atoi(map->getProperty(
413  "manaplus version", std::string()).c_str()));
414  }
415  else if (xmlNameEqual(childNode, "objectgroup"))
416  {
417  // The object group offset is applied to each object individually
418  const int tileOffsetX = XML::getProperty(childNode, "x", 0);
419  const int tileOffsetY = XML::getProperty(childNode, "y", 0);
420  const int offsetX = tileOffsetX * tilew;
421  const int offsetY = tileOffsetY * tileh;
422  const bool showParticles =
423  config.getBoolValue("mapparticleeffects");
424 
425  for_each_xml_child_node(objectNode, childNode)
426  {
427  if (xmlNameEqual(objectNode, "object"))
428  {
429  std::string objType = XML::getProperty(
430  objectNode, "type", "");
431 
432  objType = toUpper(objType);
433 
434 /*
435  if (objType == "NPC" ||
436  objType == "SCRIPT")
437  {
438  logger->log("hidden obj: " + objType);
439  // Silently skip server-side objects.
440  continue;
441  }
442 */
443 
444  const std::string objName = XML::getProperty(
445  objectNode, "name", "");
446  const int objX = XML::getProperty(objectNode, "x", 0);
447  const int objY = XML::getProperty(objectNode, "y", 0);
448  const int objW = XML::getProperty(objectNode, "width", 0);
449  const int objH = XML::getProperty(objectNode, "height", 0);
450 
451  logger->log("- Loading object name: %s type: %s at %d:%d"
452  " (%dx%d)", objName.c_str(), objType.c_str(),
453  objX, objY, objW, objH);
454 
455  if (objType == "PARTICLE_EFFECT")
456  {
457  if (objName.empty())
458  {
459  logger->log1(" Warning: No particle file given");
460  continue;
461  }
462 
463  if (showParticles)
464  {
465  map->addParticleEffect(objName,
466  objX + offsetX,
467  objY + offsetY,
468  objW,
469  objH);
470  }
471  else
472  {
473  logger->log("Ignore particle effect: " + objName);
474  }
475  }
476  else if (objType == "WARP")
477  {
478  if (showWarps)
479  {
480  map->addParticleEffect(warpPath,
481  objX, objY, objW, objH);
482  }
483  map->addPortal(objName, MapItemType::PORTAL,
484  objX, objY, objW, objH);
485  }
486  else if (objType == "SPAWN")
487  {
488  // TRANSLATORS: spawn name
489 // map->addPortal(_("Spawn: ") + objName,
490 // MapItemType::PORTAL,
491 // objX, objY, objW, objH);
492  }
493  else if (objType == "MUSIC")
494  {
495  map->addRange(objName, MapItemType::MUSIC,
496  objX, objY, objW, objH);
497  }
498  else
499  {
500  logger->log1(" Warning: Unknown object type");
501  }
502  }
503  }
504  }
505  }
506 
508  map->clearIndexedTilesets();
509  map->setActorsFix(0,
510  atoi(map->getProperty("actorsfix", std::string()).c_str()));
511  map->reduce();
512  map->setWalkLayer(Loader::getWalkLayer(fileName, map));
514  map->updateDrawLayersList();
515  BLOCK_END("MapReader::readMap xml")
516  return map;
517 }
518 
519 void MapReader::readProperties(XmlNodeConstPtrConst node,
520  Properties *const props)
521 {
522  BLOCK_START("MapReader::readProperties")
523  if (node == nullptr)
524  {
525  BLOCK_END("MapReader::readProperties")
526  return;
527  }
528 
529  for_each_xml_child_node(childNode, node)
530  {
531  if (!xmlNameEqual(childNode, "property"))
532  continue;
533 
534  // Example: <property name="name" value="value"/>
535  const std::string name = XML::getProperty(childNode, "name", "");
536  const std::string value = XML::getProperty(childNode, "value", "");
537 
538  if (!name.empty() && !value.empty())
539  {
540  if (name == "name")
541  props->setProperty(name, translator->getStr(value));
542  else
543  props->setProperty(name, value);
544  }
545  }
546  BLOCK_END("MapReader::readProperties")
547 }
548 
549 inline static void setTile(Map *const map,
550  MapLayer *const layer,
551  const MapLayerTypeT &layerType,
552  MapHeights *const heights,
553  const int x, const int y,
554  const int gid) A_NONNULL(1);
555 
556 inline static void setTile(Map *const map,
557  MapLayer *const layer,
558  const MapLayerTypeT &layerType,
559  MapHeights *const heights,
560  const int x, const int y,
561  const int gid)
562 {
563  const Tileset * const set = map->getTilesetWithGid(gid);
564  switch (layerType)
565  {
566  case MapLayerType::TILES:
567  {
568  if (layer == nullptr)
569  break;
570  if (set != nullptr &&
571  !set->isEmpty())
572  {
573  Image *const img = set->get(gid - set->getFirstGid());
574  layer->setTile(x, y, img);
575  }
576  else
577  {
578  layer->setTile(x, y, nullptr);
579  }
580  break;
581  }
582 
584  {
585  if (set != nullptr)
586  {
587  if (map->getVersion() >= 1)
588  {
589  const int collisionId = gid - set->getFirstGid();
590  CollisionTypeT type;
591  if (collisionId < 0 ||
592  collisionId >= CAST_S32(CollisionType::COLLISION_MAX))
593  {
595  }
596  else
597  {
598  type = static_cast<CollisionTypeT>(collisionId);
599  }
600  switch (type)
601  {
603  map->addBlockMask(x, y, BlockType::GROUND);
604  break;
606  map->addBlockMask(x, y, BlockType::WALL);
607  break;
609  map->addBlockMask(x, y, BlockType::AIR);
610  break;
612  map->addBlockMask(x, y, BlockType::WATER);
613  break;
616  break;
619  break;
622  break;
624  default:
625  break;
626  }
627  }
628  else
629  {
630  if (gid - set->getFirstGid() != 0)
631  map->addBlockMask(x, y, BlockType::WALL);
632  }
633  }
634  break;
635  }
636 
638  {
639  if (set == nullptr || heights == nullptr)
640  break;
641  if (map->getVersion() >= 2)
642  {
643  heights->setHeight(x, y, CAST_U8(
644  gid - set->getFirstGid() + 1));
645  }
646  else
647  {
648  Image *const img = set->get(gid - set->getFirstGid());
649  if (layer != nullptr)
650  layer->setTile(x, y, img);
651  }
652  break;
653  }
654 
655  default:
657  break;
658  }
659 }
660 
661 bool MapReader::readBase64Layer(XmlNodeConstPtrConst childNode,
662  Map *const map,
663  MapLayer *const layer,
664  const MapLayerTypeT &layerType,
665  MapHeights *const heights,
666  const std::string &compression,
667  int &restrict x, int &restrict y,
668  const int w, const int h)
669 {
670  if (childNode == nullptr)
671  return false;
672 
673  if (!compression.empty() && compression != "gzip"
674  && compression != "zlib")
675  {
676  reportAlways("Warning: only gzip and zlib layer"
677  " compression supported!");
678  return false;
679  }
680 
681  // Read base64 encoded map file
682  if (!XmlHaveChildContent(childNode))
683  return true;
684 
685  const size_t len = strlen(XmlChildContent(childNode)) + 1;
686  unsigned char *charData = new unsigned char[len + 1];
687  const char *const xmlChars = XmlChildContent(childNode);
688  const char *charStart = reinterpret_cast<const char*>(xmlChars);
689  if (charStart == nullptr)
690  {
691  delete [] charData;
692  return false;
693  }
694 
695  unsigned char *charIndex = charData;
696 
697  while (*charStart != 0)
698  {
699  if (*charStart != ' ' &&
700  *charStart != '\t' &&
701  *charStart != '\n')
702  {
703  *charIndex = *charStart;
704  charIndex++;
705  }
706  charStart++;
707  }
708  *charIndex = '\0';
709 
710  int binLen;
711  unsigned char *binData = php3_base64_decode(charData,
712  CAST_S32(strlen(reinterpret_cast<char*>(
713  charData))), &binLen);
714 
715  delete [] charData;
716 // XmlFree(const_cast<char*>(xmlChars));
717 
718  if (binData != nullptr)
719  {
720  if (compression == "gzip" || compression == "zlib")
721  {
722  // Inflate the gzipped layer data
723  unsigned char *inflated = nullptr;
724  const unsigned int inflatedSize =
725  inflateMemory(binData, binLen, inflated);
726 
727  free(binData);
728  binData = inflated;
729  binLen = inflatedSize;
730 
731  if (inflated == nullptr)
732  {
733  reportAlways("Error: Could not decompress layer!");
734  return false;
735  }
736  }
737 
738  const std::map<int, TileAnimation*> &tileAnimations
739  = map->getTileAnimations();
740 
741  const bool hasAnimations = !tileAnimations.empty();
742  if (hasAnimations)
743  {
744  for (int i = 0; i < binLen - 3; i += 4)
745  {
746  const int gid = binData[i] |
747  binData[i + 1] << 8 |
748  binData[i + 2] << 16 |
749  binData[i + 3] << 24;
750 
751  setTile(map, layer, layerType, heights, x, y, gid);
752  TileAnimationMapCIter it = tileAnimations.find(gid);
753  if (it != tileAnimations.end())
754  {
755  TileAnimation *const ani = it->second;
756  if (ani != nullptr)
757  ani->addAffectedTile(layer, x + y * w);
758  }
759 
760  x++;
761  if (x == w)
762  {
763  x = 0; y++;
764 
765  // When we're done, don't crash on too much data
766  if (y == h)
767  break;
768  }
769  }
770  }
771  else
772  {
773  for (int i = 0; i < binLen - 3; i += 4)
774  {
775  const int gid = binData[i] |
776  binData[i + 1] << 8 |
777  binData[i + 2] << 16 |
778  binData[i + 3] << 24;
779 
780  setTile(map, layer, layerType, heights, x, y, gid);
781 
782  x++;
783  if (x == w)
784  {
785  x = 0; y++;
786 
787  // When we're done, don't crash on too much data
788  if (y == h)
789  break;
790  }
791  }
792  }
793  free(binData);
794  }
795  return true;
796 }
797 
798 bool MapReader::readCsvLayer(XmlNodeConstPtrConst childNode,
799  Map *const map,
800  MapLayer *const layer,
801  const MapLayerTypeT &layerType,
802  MapHeights *const heights,
803  int &restrict x, int &restrict y,
804  const int w, const int h)
805 {
806  if (childNode == nullptr)
807  return false;
808 
809  if (!XmlHaveChildContent(childNode))
810  return true;
811 
812  const char *const xmlChars = XmlChildContent(childNode);
813  const char *const data = reinterpret_cast<const char*>(xmlChars);
814  if (data == nullptr)
815  return false;
816 
817  std::string csv(data);
818  size_t oldPos = 0;
819 
820  const std::map<int, TileAnimation*> &tileAnimations
821  = map->getTileAnimations();
822  const bool hasAnimations = !tileAnimations.empty();
823 
824  if (hasAnimations)
825  {
826  while (oldPos != std::string::npos)
827  {
828  const size_t pos = csv.find_first_of(',', oldPos);
829  if (pos == std::string::npos)
830  return false;
831 
832  const int gid = atoi(csv.substr(oldPos, pos - oldPos).c_str());
833  setTile(map, layer, layerType, heights, x, y, gid);
834  TileAnimationMapCIter it = tileAnimations.find(gid);
835  if (it != tileAnimations.end())
836  {
837  TileAnimation *const ani = it->second;
838  if (ani != nullptr)
839  ani->addAffectedTile(layer, x + y * w);
840  }
841 
842  x++;
843  if (x == w)
844  {
845  x = 0; y++;
846 
847  // When we're done, don't crash on too much data
848  if (y == h)
849  return false;
850  }
851  oldPos = pos + 1;
852  }
853  }
854  else
855  {
856  while (oldPos != std::string::npos)
857  {
858  const size_t pos = csv.find_first_of(',', oldPos);
859  if (pos == std::string::npos)
860  return false;
861 
862  const int gid = atoi(csv.substr(oldPos, pos - oldPos).c_str());
863  setTile(map, layer, layerType, heights, x, y, gid);
864 
865  x++;
866  if (x == w)
867  {
868  x = 0; y++;
869 
870  // When we're done, don't crash on too much data
871  if (y == h)
872  return false;
873  }
874  oldPos = pos + 1;
875  }
876  }
877  return true;
878 }
879 
880 void MapReader::readLayer(XmlNodeConstPtr node, Map *const map)
881 {
882  if (node == nullptr)
883  return;
884 
885  // Layers are not necessarily the same size as the map
886  const int w = XML::getProperty(node, "width", map->getWidth());
887  const int h = XML::getProperty(node, "height", map->getHeight());
888  const int offsetX = XML::getProperty(node, "x", 0);
889  const int offsetY = XML::getProperty(node, "y", 0);
890  std::string name = XML::getProperty(node, "name", "");
891  name = toLower(name);
892 
893  const bool isFringeLayer = (name.substr(0, 6) == "fringe");
894  const bool isCollisionLayer = (name.substr(0, 9) == "collision");
895  const bool isHeightLayer = (name.substr(0, 7) == "heights");
896  const bool isActionsLayer = (name.substr(0, 7) == "actions");
897  int mask = 1;
898  int tileCondition = -1;
899  int conditionLayer = 0;
900 
902  if (isCollisionLayer)
903  layerType = MapLayerType::COLLISION;
904  else if (isHeightLayer)
905  layerType = MapLayerType::HEIGHTS;
906  else if (isActionsLayer)
907  layerType = MapLayerType::ACTIONS;
908 
909  map->indexTilesets();
910 
911  MapLayer *layer = nullptr;
912  MapHeights *heights = nullptr;
913 
914  logger->log("- Loading layer \"%s\"", name.c_str());
915  int x = 0;
916  int y = 0;
917 
918  // Load the tile data
919  for_each_xml_child_node(childNode, node)
920  {
921  if (xmlNameEqual(childNode, "properties"))
922  {
923  for_each_xml_child_node(prop, childNode)
924  {
925  if (!xmlNameEqual(prop, "property"))
926  continue;
927  const std::string pname = XML::getProperty(prop, "name", "");
928  const std::string value = XML::getProperty(prop, "value", "");
929  // ignoring any layer if property Hidden is 1
930  if (pname == "Hidden")
931  {
932  if (value == "1")
933  return;
934  }
935  else if (pname == "Version")
936  {
937  if (value > CHECK_VERSION)
938  return;
939  }
940  else if (pname == "NotVersion")
941  {
942  if (value <= CHECK_VERSION)
943  return;
944  }
945  else if (pname == "Mask")
946  {
947  mask = atoi(value.c_str());
948  }
949  else if (pname == "TileCondition")
950  {
951  tileCondition = atoi(value.c_str());
952  }
953  else if (pname == "ConditionLayer")
954  {
955  conditionLayer = atoi(value.c_str());
956  }
957  else if (pname == "SideView")
958  {
959  if (value != "down")
960  return;
961  }
962  }
963  }
964 
965  if (!xmlNameEqual(childNode, "data"))
966  continue;
967 
968  // Disable for future usage "TileCondition" attribute
969  // if already set ConditionLayer to non zero
970  if (conditionLayer != 0)
971  tileCondition = -1;
972 
973  switch (layerType)
974  {
975  case MapLayerType::TILES:
976  {
977  layer = new MapLayer(name,
978  offsetX, offsetY,
979  w, h,
980  isFringeLayer,
981  mask,
982  tileCondition);
983  map->addLayer(layer);
984  break;
985  }
987  {
988  heights = new MapHeights(w, h);
989  map->addHeights(heights);
990  break;
991  }
992  default:
995  break;
996  }
997 
998  const std::string encoding =
999  XML::getProperty(childNode, "encoding", "");
1000  const std::string compression =
1001  XML::getProperty(childNode, "compression", "");
1002 
1003  if (encoding == "base64")
1004  {
1005  if (readBase64Layer(childNode, map, layer, layerType,
1006  heights, compression, x, y, w, h))
1007  {
1008  continue;
1009  }
1010  else
1011  {
1012  return;
1013  }
1014  }
1015  else if (encoding == "csv")
1016  {
1017  if (readCsvLayer(childNode, map, layer, layerType,
1018  heights, x, y, w, h))
1019  {
1020  continue;
1021  }
1022  else
1023  {
1024  return;
1025  }
1026  }
1027  else
1028  {
1029  const std::map<int, TileAnimation*> &tileAnimations
1030  = map->getTileAnimations();
1031  const bool hasAnimations = !tileAnimations.empty();
1032 
1033  // Read plain XML map file
1034  for_each_xml_child_node(childNode2, childNode)
1035  {
1036  if (!xmlNameEqual(childNode2, "tile"))
1037  continue;
1038 
1039  const int gid = XML::getProperty(childNode2, "gid", -1);
1040  setTile(map, layer, layerType, heights, x, y, gid);
1041  if (hasAnimations)
1042  {
1043  TileAnimationMapCIter it = tileAnimations.find(gid);
1044  if (it != tileAnimations.end())
1045  {
1046  TileAnimation *const ani = it->second;
1047  if (ani != nullptr)
1048  ani->addAffectedTile(layer, x + y * w);
1049  }
1050  }
1051 
1052  x++;
1053  if (x == w)
1054  {
1055  x = 0; y++;
1056  if (y >= h)
1057  break;
1058  }
1059  }
1060  }
1061 
1062  if (y < h)
1063  std::cerr << "TOO SMALL!\n";
1064  if (x != 0)
1065  std::cerr << "TOO SMALL!\n";
1066 
1067  // There can be only one data element
1068  break;
1069  }
1070 }
1071 
1073  const std::string &path,
1074  Map *const map)
1075 {
1076  BLOCK_START("MapReader::readTileset")
1077  if (node == nullptr)
1078  {
1079  BLOCK_END("MapReader::readTileset")
1080  return nullptr;
1081  }
1082 
1083  const int firstGid = XML::getProperty(node, "firstgid", 0);
1084  const int margin = XML::getProperty(node, "margin", 0);
1085  const int spacing = XML::getProperty(node, "spacing", 0);
1086  XML::Document* doc = nullptr;
1087  Tileset *set = nullptr;
1088  std::string pathDir(path);
1089  std::map<std::string, std::string> props;
1090 
1091  if (XmlHasProp(node, "source"))
1092  {
1093  std::string filename = XML::getProperty(node, "source", "");
1094  filename = resolveRelativePath(path, filename);
1095 
1096  doc = new XML::Document(filename, UseVirtFs_true, SkipError_false);
1097  node = doc->rootNode();
1098  if (node == nullptr)
1099  {
1100  delete doc;
1101  BLOCK_END("MapReader::readTileset")
1102  return nullptr;
1103  }
1104 
1105  // Reset path to be realtive to the tsx file
1106  pathDir = filename.substr(0, filename.rfind('/') + 1);
1107  }
1108 
1109  const int tw = XML::getProperty(node, "tilewidth", map->getTileWidth());
1110  const int th = XML::getProperty(node, "tileheight", map->getTileHeight());
1111 
1112  for_each_xml_child_node(childNode, node)
1113  {
1114  if (xmlNameEqual(childNode, "image"))
1115  {
1116  // ignore second other <image> tags in tileset
1117  if (set != nullptr)
1118  continue;
1119 
1120  const std::string source = XML::getProperty(
1121  childNode, "source", "");
1122 
1123  if (!source.empty())
1124  {
1125  const std::string sourceResolved = resolveRelativePath(pathDir,
1126  source);
1127 
1128  Image *const tilebmp = Loader::getImage(sourceResolved);
1129 
1130  if (tilebmp != nullptr)
1131  {
1132  set = new Tileset(tilebmp,
1133  tw, th,
1134  firstGid,
1135  margin,
1136  spacing);
1137  tilebmp->decRef();
1138 #ifdef USE_OPENGL
1139  if (MapDB::isEmptyTileset(sourceResolved))
1140  set->setEmpty(true);
1141  if (tilebmp->getType() == ImageType::Image &&
1142  map->haveAtlas() == true &&
1144  {
1145  reportAlways("Error: image '%s' not present in atlas",
1146  source.c_str());
1147  }
1148 #endif // USE_OPENGL
1149  }
1150  else
1151  {
1152  reportAlways("Error: Failed to load tileset (%s)",
1153  source.c_str());
1154  }
1155  }
1156  }
1157  else if (xmlNameEqual(childNode, "properties"))
1158  {
1159  for_each_xml_child_node(propertyNode, childNode)
1160  {
1161  if (!xmlNameEqual(propertyNode, "property"))
1162  continue;
1163  const std::string name = XML::getProperty(
1164  propertyNode, "name", "");
1165  if (!name.empty())
1166  props[name] = XML::getProperty(propertyNode, "value", "");
1167  }
1168  }
1169  else if (xmlNameEqual(childNode, "tile"))
1170  {
1171  bool haveAnimation(false);
1172 
1173  for_each_xml_child_node(tileNode, childNode)
1174  {
1175  const bool isProps = xmlNameEqual(tileNode, "properties");
1176  const bool isAnim = xmlNameEqual(tileNode, "animation");
1177  if (!isProps && !isAnim)
1178  continue;
1179 
1180  const int tileGID = firstGid + XML::getProperty(
1181  childNode, "id", 0);
1182 
1183  Animation *ani = new Animation("from map");
1184 
1185  if (isProps)
1186  {
1187  // read tile properties to a map for simpler handling
1188  StringIntMap tileProperties;
1189  for_each_xml_child_node(propertyNode, tileNode)
1190  {
1191  if (!xmlNameEqual(propertyNode, "property"))
1192  continue;
1193 
1194  haveAnimation = true;
1195  const std::string name = XML::getProperty(
1196  propertyNode, "name", "");
1197  const int value = XML::getProperty(
1198  propertyNode, "value", 0);
1199  if (!name.empty())
1200  {
1201  tileProperties[name] = value;
1202  logger->log("Tile Prop of %d \"%s\" = \"%d\"",
1203  tileGID, name.c_str(), value);
1204  }
1205  }
1206 
1207  // create animation
1208  if (set == nullptr ||
1209  !config.getBoolValue("playMapAnimations"))
1210  {
1211  delete ani;
1212  continue;
1213  }
1214 
1215  for (int i = 0; ; i++)
1216  {
1217  const std::string iStr(toString(i));
1218  StringIntMapCIter iFrame
1219  = tileProperties.find("animation-frame" + iStr);
1220  StringIntMapCIter iDelay
1221  = tileProperties.find("animation-delay" + iStr);
1222  // possible need add random attribute?
1223  if (iFrame != tileProperties.end()
1224  && iDelay != tileProperties.end())
1225  {
1226  ani->addFrame(set->get(iFrame->second),
1227  iDelay->second, 0, 0, 100);
1228  }
1229  else
1230  {
1231  break;
1232  }
1233  }
1234  }
1235  else if (isAnim && !haveAnimation)
1236  {
1237  for_each_xml_child_node(frameNode, tileNode)
1238  {
1239  if (!xmlNameEqual(frameNode, "frame"))
1240  continue;
1241 
1242  const int tileId = XML::getProperty(
1243  frameNode, "tileid", 0);
1244  const int duration = XML::getProperty(
1245  frameNode, "duration", 0) / 10;
1246 
1247  if (set != nullptr)
1248  {
1249  ani->addFrame(set->get(tileId),
1250  duration,
1251  0, 0, 100);
1252  }
1253  }
1254  }
1255 
1256  if (ani->getLength() > 0)
1257  map->addAnimation(tileGID, new TileAnimation(ani));
1258  else
1259  delete2(ani)
1260  }
1261  }
1262  }
1263 
1264  delete doc;
1265 
1266  if (set != nullptr)
1267  set->setProperties(props);
1268  BLOCK_END("MapReader::readTileset")
1269  return set;
1270 }
1271 
1272 Map *MapReader::createEmptyMap(const std::string &restrict filename,
1273  const std::string &restrict realFilename)
1274 {
1275  logger->log1("Creating empty map");
1276  Map *const map = new Map("empty map",
1277  300, 300,
1279  map->setProperty("_filename", realFilename);
1280  map->setProperty("_realfilename", filename);
1281  updateMusic(map);
1282  map->setCustom(true);
1283  MapLayer *layer = new MapLayer("nolayer",
1284  0, 0,
1285  300, 300,
1286  false,
1287  1,
1288  -1);
1289  map->addLayer(layer);
1290  layer = new MapLayer("nolayer",
1291  0, 0,
1292  300, 300,
1293  true,
1294  1,
1295  -1);
1296  map->addLayer(layer);
1297  map->updateDrawLayersList();
1298  map->updateConditionLayers();
1299  map->preCacheLayers();
1300 
1301  return map;
1302 }
1303 
1304 void MapReader::updateMusic(Map *const map)
1305 {
1306  std::string name = map->getProperty("shortName", std::string());
1307  const size_t p = name.rfind('.');
1308  if (p != std::string::npos)
1309  name = name.substr(0, p);
1310  name.append(".ogg");
1311  if (VirtFs::exists(pathJoin(paths.getStringValue("music"), name)))
1312  map->setProperty("music", name);
1313 }
1314 
1315 #ifdef USE_OPENGL
1317 {
1319  return;
1320 
1321  const MapInfo *const info = MapDB::getAtlas(
1322  paths.getStringValue("emptyAtlasName"));
1323  if (info != nullptr)
1324  {
1326  info->atlas,
1327  *info->files);
1328  delete info;
1329  }
1330 }
1331 
1333 {
1334  if (mEmptyAtlas != nullptr)
1335  mEmptyAtlas->decRef();
1336 }
1337 #endif // USE_OPENGL
#define FOR_EACH(type, iter, array)
Definition: foreach.h:24
void addTileset(Tileset *const tileset)
Definition: map.cpp:318
unsigned char * php3_base64_decode(const unsigned char *const string, const int length, int *const ret_length)
Definition: base64.cpp:100
std::string getStringValue(const std::string &key) const
#define A_FALLTHROUGH
Definition: localconsts.h:192
const bool SkipError_false
Definition: skiperror.h:29
void log1(const char *const log_text)
Definition: logger.cpp:233
std::map< std::string, int > StringIntMap
Definition: stringmap.h:27
#define CAST_U8
Definition: cast.h:26
WalkLayer * getWalkLayer(const std::string &name, Map *const map)
void addBlockMask(const int x, const int y, const BlockTypeT type)
Definition: map.cpp:702
static void readLayer(const xmlNodePtr node, Map *const map)
Definition: mapreader.cpp:880
const char * dirSeparator
Definition: fs.cpp:42
static int inflateMemory(unsigned char *in, unsigned int inLength, unsigned char *&out, unsigned int &outLength)
Definition: mapreader.cpp:123
TileAnimationMap::const_iterator TileAnimationMapCIter
Definition: tileanimation.h:64
void addRange(const std::string &name, const int type, const int x, const int y, const int dx, const int dy)
Definition: map.cpp:1274
std::string fileName
Definition: testmain.cpp:38
int getTileHeight() const
Definition: map.h:183
virtual void decRef()
Definition: resource.cpp:49
void updateConditionLayers()
Definition: map.cpp:1727
bool getUseAtlases() const
std::string pathJoin(std::string str1, const std::string &str2)
#define BLOCK_START(name)
Definition: perfomance.h:78
void addHeights(const MapHeights *const heights)
Definition: map.cpp:1620
const std::string getProperty(const std::string &name, const std::string &def) const
Definition: properties.h:58
Configuration config
void addAffectedTile(MapLayer *const layer, const int index)
Definition: tileanimation.h:54
int getProperty(const xmlNodePtr node, const char *const name, int def)
Definition: libxml.cpp:173
static std::string resolveRelativePath(std::string base, std::string relative)
Definition: mapreader.cpp:93
void setVersion(const int n)
Definition: map.h:312
#define BLOCK_END(name)
Definition: perfomance.h:79
void setWalkLayer(WalkLayer *const layer)
Definition: map.h:349
const Tileset * getTilesetWithGid(const int gid) const
Definition: map.cpp:695
StringIntMap::const_iterator StringIntMapCIter
Definition: stringmap.h:29
void addLayer(MapLayer *const layer)
Definition: map.cpp:311
#define delete2(var)
Definition: delete2.h:24
#define A_NONNULL(...)
Definition: localconsts.h:167
static bool readCsvLayer(const xmlNode *const childNode, Map *const map, MapLayer *const layer, const MapLayerTypeT &layerType, MapHeights *const heights, int &x, int &y, const int w, const int h)
Definition: mapreader.cpp:798
std::set< XML::Document * > mKnownDocs
Definition: mapreader.cpp:81
#define loadXmlDir2(name, function, ext, skipError)
Definition: beingcommon.h:49
static void unloadEmptyAtlas()
Definition: mapreader.cpp:1332
std::set< XML::Document * >::iterator DocIterator
Definition: mapreader.cpp:72
void indexTilesets()
Definition: map.cpp:1410
int getHeight() const
Definition: map.h:171
Logger * logger
Definition: logger.cpp:88
void setAtlas(AtlasResource *const atlas)
Definition: map.h:336
void updateDrawLayersList()
Definition: map.cpp:1633
bool getBoolValue(const std::string &key) const
const bool UseVirtFs_true
Definition: usevirtfs.h:29
virtual ImageTypeT getType() const
Definition: image.h:207
static void updateMusic(Map *const map)
Definition: mapreader.cpp:1304
#define CAST_S32
Definition: cast.h:29
void clearIndexedTilesets()
Definition: map.cpp:1458
static void loadReplaceLayer(LayerInfoIterator &it, Map *map)
Definition: mapreader.cpp:323
const MapInfo * getAtlas(const std::string &name)
Definition: mapdb.cpp:225
void setTile(const int x, const int y, Image *const img)
Definition: maplayer.h:77
Resource * getEmptyAtlas(const std::string &name, const StringVect &files)
void addPortal(const std::string &name, const int type, const int x, const int y, const int dx, const int dy)
Definition: map.cpp:1286
void setProperty(const std::string &name, const std::string &value)
Definition: properties.h:127
Image * getImage(const std::string &idPath)
Definition: imageloader.cpp:85
static Resource * mEmptyAtlas
Definition: mapreader.h:119
static Tileset * readTileset(xmlNodePtr node, const std::string &path, Map *const map)
Definition: mapreader.cpp:1072
#define CHECK_VERSION
Definition: main.h:44
bool info(InputEvent &event)
Definition: commands.cpp:56
int getVersion() const
Definition: map.h:309
uint32_t data
std::string toLower(std::string const &s)
size_t getLength() const
Definition: animation.h:69
void preCacheLayers()
Definition: map.cpp:1741
void setCustom(const bool b)
Definition: map.h:323
void addAnimation(const int gid, TileAnimation *const animation)
Definition: map.cpp:1686
bool isEmptyTileset(const std::string &name)
Definition: mapdb.cpp:237
Definition: map.h:71
xmlNodePtr rootNode()
Definition: libxml.cpp:168
static Map * readMap(const std::string &filename, const std::string &realFilename)
CollisionType ::T CollisionTypeT
Definition: collisiontype.h:39
bool haveAtlas() const
Definition: map.h:339
static void loadEmptyAtlas()
Definition: mapreader.cpp:1316
const StringVect * files
Definition: mapinfo.h:40
int getWidth() const
Definition: map.h:165
Configuration paths
void addParticleEffect(const std::string &effectFile, const int x, const int y, const int w, const int h)
Definition: map.cpp:1096
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:1774
std::map< std::string, xmlNodePtr > mKnownLayers
Definition: mapreader.cpp:80
std::string atlas
Definition: mapinfo.h:39
#define for_each_xml_child_node(var, parent)
Definition: libxml.h:160
AtlasResource * getAtlas(const std::string &name, const StringVect &files)
Definition: atlasloader.cpp:55
bool isLoaded() const
Definition: libxml.h:85
static void addLayerToList(const std::string &fileName, const SkipError skipError)
Definition: mapreader.cpp:229
static void readProperties(const xmlNode *const node, Properties *const props)
Definition: mapreader.cpp:519
bool exists(std::string name)
Definition: fs.cpp:123
static void setTile(Map *map, MapLayer *layer, MapLayerTypeT &layerType, MapHeights *heights, int x, int y, int gid)
Definition: mapreader.cpp:556
static bool readBase64Layer(const xmlNode *const childNode, Map *const map, MapLayer *const layer, const MapLayerTypeT &layerType, MapHeights *const heights, const std::string &compression, int &x, int &y, const int w, const int h)
Definition: mapreader.cpp:661
void initializeAmbientLayers()
Definition: map.cpp:232
int getTileWidth() const
Definition: map.h:177
static void unloadTempLayers()
Definition: mapreader.cpp:313
Definition: image.h:61
const std::map< int, TileAnimation * > & getTileAnimations() const
Definition: map.h:329
std::string & toUpper(std::string &str)
Definition: stringutils.cpp:71
void addFrame(Image *const image, const int delay, const int offsetX, const int offsetY, const int rand)
Definition: animation.cpp:45
static const int mapTileSize
Definition: map.h:26
static Map * createEmptyMap(const std::string &filename, const std::string &realFilename)
Definition: mapreader.cpp:1272
#define restrict
Definition: localconsts.h:164
const MapInfo * getMapAtlas(const std::string &name)
Definition: mapdb.cpp:212
#define CAST_SIZE
Definition: cast.h:33
void setHeight(const int x, const int y, const uint8_t height)
Definition: mapheights.cpp:40
void reduce()
Definition: map.cpp:1468
MapLayerType ::T MapLayerTypeT
Definition: maplayertype.h:37
void log(const char *const log_text,...)
Definition: logger.cpp:264
const std::string getStr(const std::string &str)
Definition: podict.cpp:44
#define PRAGMACLANG6GCC(str)
Definition: localconsts.h:239
PoDict * translator
Definition: podict.cpp:27
#define reportAlways(...)
Definition: checkutils.h:252
GraphicsManager graphicsManager
void decRef()
Definition: image.cpp:522
void setActorsFix(const int x, const int y)
Definition: map.cpp:1719
std::map< std::string, xmlNodePtr >::iterator LayerInfoIterator
Definition: mapreader.cpp:71
static void loadLayers(const std::string &path)
Definition: mapreader.cpp:306