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-2017 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 ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
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").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").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, atoi(map->getProperty("actorsfix").c_str()));
510  map->reduce();
511  map->setWalkLayer(Loader::getWalkLayer(fileName, map));
513  map->updateDrawLayersList();
514  BLOCK_END("MapReader::readMap xml")
515  return map;
516 }
517 
518 void MapReader::readProperties(XmlNodeConstPtrConst node,
519  Properties *const props)
520 {
521  BLOCK_START("MapReader::readProperties")
522  if (node == nullptr)
523  {
524  BLOCK_END("MapReader::readProperties")
525  return;
526  }
527 
528  for_each_xml_child_node(childNode, node)
529  {
530  if (!xmlNameEqual(childNode, "property"))
531  continue;
532 
533  // Example: <property name="name" value="value"/>
534  const std::string name = XML::getProperty(childNode, "name", "");
535  const std::string value = XML::getProperty(childNode, "value", "");
536 
537  if (!name.empty() && !value.empty())
538  {
539  if (name == "name")
540  props->setProperty(name, translator->getStr(value));
541  else
542  props->setProperty(name, value);
543  }
544  }
545  BLOCK_END("MapReader::readProperties")
546 }
547 
548 inline static void setTile(Map *const map,
549  MapLayer *const layer,
550  const MapLayerTypeT &layerType,
551  MapHeights *const heights,
552  const int x, const int y,
553  const int gid) A_NONNULL(1);
554 
555 inline static void setTile(Map *const map,
556  MapLayer *const layer,
557  const MapLayerTypeT &layerType,
558  MapHeights *const heights,
559  const int x, const int y,
560  const int gid)
561 {
562  const Tileset * const set = map->getTilesetWithGid(gid);
563  switch (layerType)
564  {
565  case MapLayerType::TILES:
566  {
567  Image *const img = set != nullptr ?
568  set->get(gid - set->getFirstGid()) : nullptr;
569  if (layer != nullptr)
570  layer->setTile(x, y, img);
571  break;
572  }
573 
575  {
576  if (set != nullptr)
577  {
578  if (map->getVersion() >= 1)
579  {
580  const int collisionId = gid - set->getFirstGid();
581  CollisionTypeT type;
582  if (collisionId < 0 ||
583  collisionId >= CAST_S32(CollisionType::COLLISION_MAX))
584  {
586  }
587  else
588  {
589  type = static_cast<CollisionTypeT>(collisionId);
590  }
591  switch (type)
592  {
594  map->addBlockMask(x, y, BlockType::GROUND);
595  break;
597  map->addBlockMask(x, y, BlockType::WALL);
598  break;
600  map->addBlockMask(x, y, BlockType::AIR);
601  break;
603  map->addBlockMask(x, y, BlockType::WATER);
604  break;
607  break;
610  break;
613  break;
615  default:
616  break;
617  }
618  }
619  else
620  {
621  if (gid - set->getFirstGid() != 0)
622  map->addBlockMask(x, y, BlockType::WALL);
623  }
624  }
625  break;
626  }
627 
629  {
630  if (set == nullptr || heights == nullptr)
631  break;
632  if (map->getVersion() >= 2)
633  {
634  heights->setHeight(x, y, CAST_U8(
635  gid - set->getFirstGid() + 1));
636  }
637  else
638  {
639  Image *const img = set->get(gid - set->getFirstGid());
640  if (layer != nullptr)
641  layer->setTile(x, y, img);
642  }
643  break;
644  }
645 
646  default:
648  break;
649  }
650 }
651 
652 bool MapReader::readBase64Layer(XmlNodeConstPtrConst childNode,
653  Map *const map,
654  MapLayer *const layer,
655  const MapLayerTypeT &layerType,
656  MapHeights *const heights,
657  const std::string &compression,
658  int &restrict x, int &restrict y,
659  const int w, const int h)
660 {
661  if (childNode == nullptr)
662  return false;
663 
664  if (!compression.empty() && compression != "gzip"
665  && compression != "zlib")
666  {
667  reportAlways("Warning: only gzip and zlib layer"
668  " compression supported!");
669  return false;
670  }
671 
672  // Read base64 encoded map file
673  if (!XmlHaveChildContent(childNode))
674  return true;
675 
676  const size_t len = strlen(XmlChildContent(childNode)) + 1;
677  unsigned char *charData = new unsigned char[len + 1];
678  const char *const xmlChars = XmlChildContent(childNode);
679  const char *charStart = reinterpret_cast<const char*>(xmlChars);
680  if (charStart == nullptr)
681  {
682  delete [] charData;
683  return false;
684  }
685 
686  unsigned char *charIndex = charData;
687 
688  while (*charStart != 0)
689  {
690  if (*charStart != ' ' &&
691  *charStart != '\t' &&
692  *charStart != '\n')
693  {
694  *charIndex = *charStart;
695  charIndex++;
696  }
697  charStart++;
698  }
699  *charIndex = '\0';
700 
701  int binLen;
702  unsigned char *binData = php3_base64_decode(charData,
703  CAST_S32(strlen(reinterpret_cast<char*>(
704  charData))), &binLen);
705 
706  delete [] charData;
707 // XmlFree(const_cast<char*>(xmlChars));
708 
709  if (binData != nullptr)
710  {
711  if (compression == "gzip" || compression == "zlib")
712  {
713  // Inflate the gzipped layer data
714  unsigned char *inflated = nullptr;
715  const unsigned int inflatedSize =
716  inflateMemory(binData, binLen, inflated);
717 
718  free(binData);
719  binData = inflated;
720  binLen = inflatedSize;
721 
722  if (inflated == nullptr)
723  {
724  reportAlways("Error: Could not decompress layer!");
725  return false;
726  }
727  }
728 
729  const std::map<int, TileAnimation*> &tileAnimations
730  = map->getTileAnimations();
731 
732  const bool hasAnimations = !tileAnimations.empty();
733  if (hasAnimations)
734  {
735  for (int i = 0; i < binLen - 3; i += 4)
736  {
737  const int gid = binData[i] |
738  binData[i + 1] << 8 |
739  binData[i + 2] << 16 |
740  binData[i + 3] << 24;
741 
742  setTile(map, layer, layerType, heights, x, y, gid);
743  TileAnimationMapCIter it = tileAnimations.find(gid);
744  if (it != tileAnimations.end())
745  {
746  TileAnimation *const ani = it->second;
747  if (ani != nullptr)
748  ani->addAffectedTile(layer, x + y * w);
749  }
750 
751  x++;
752  if (x == w)
753  {
754  x = 0; y++;
755 
756  // When we're done, don't crash on too much data
757  if (y == h)
758  break;
759  }
760  }
761  }
762  else
763  {
764  for (int i = 0; i < binLen - 3; i += 4)
765  {
766  const int gid = binData[i] |
767  binData[i + 1] << 8 |
768  binData[i + 2] << 16 |
769  binData[i + 3] << 24;
770 
771  setTile(map, layer, layerType, heights, x, y, gid);
772 
773  x++;
774  if (x == w)
775  {
776  x = 0; y++;
777 
778  // When we're done, don't crash on too much data
779  if (y == h)
780  break;
781  }
782  }
783  }
784  free(binData);
785  }
786  return true;
787 }
788 
789 bool MapReader::readCsvLayer(XmlNodeConstPtrConst childNode,
790  Map *const map,
791  MapLayer *const layer,
792  const MapLayerTypeT &layerType,
793  MapHeights *const heights,
794  int &restrict x, int &restrict y,
795  const int w, const int h)
796 {
797  if (childNode == nullptr)
798  return false;
799 
800  if (!XmlHaveChildContent(childNode))
801  return true;
802 
803  const char *const xmlChars = XmlChildContent(childNode);
804  const char *const data = reinterpret_cast<const char*>(xmlChars);
805  if (data == nullptr)
806  return false;
807 
808  std::string csv(data);
809  size_t oldPos = 0;
810 
811  const std::map<int, TileAnimation*> &tileAnimations
812  = map->getTileAnimations();
813  const bool hasAnimations = !tileAnimations.empty();
814 
815  if (hasAnimations)
816  {
817  while (oldPos != std::string::npos)
818  {
819  const size_t pos = csv.find_first_of(',', oldPos);
820  if (pos == std::string::npos)
821  return false;
822 
823  const int gid = atoi(csv.substr(oldPos, pos - oldPos).c_str());
824  setTile(map, layer, layerType, heights, x, y, gid);
825  TileAnimationMapCIter it = tileAnimations.find(gid);
826  if (it != tileAnimations.end())
827  {
828  TileAnimation *const ani = it->second;
829  if (ani != nullptr)
830  ani->addAffectedTile(layer, x + y * w);
831  }
832 
833  x++;
834  if (x == w)
835  {
836  x = 0; y++;
837 
838  // When we're done, don't crash on too much data
839  if (y == h)
840  return false;
841  }
842  oldPos = pos + 1;
843  }
844  }
845  else
846  {
847  while (oldPos != std::string::npos)
848  {
849  const size_t pos = csv.find_first_of(',', oldPos);
850  if (pos == std::string::npos)
851  return false;
852 
853  const int gid = atoi(csv.substr(oldPos, pos - oldPos).c_str());
854  setTile(map, layer, layerType, heights, x, y, gid);
855 
856  x++;
857  if (x == w)
858  {
859  x = 0; y++;
860 
861  // When we're done, don't crash on too much data
862  if (y == h)
863  return false;
864  }
865  oldPos = pos + 1;
866  }
867  }
868  return true;
869 }
870 
871 void MapReader::readLayer(XmlNodeConstPtr node, Map *const map)
872 {
873  if (node == nullptr)
874  return;
875 
876  // Layers are not necessarily the same size as the map
877  const int w = XML::getProperty(node, "width", map->getWidth());
878  const int h = XML::getProperty(node, "height", map->getHeight());
879  const int offsetX = XML::getProperty(node, "x", 0);
880  const int offsetY = XML::getProperty(node, "y", 0);
881  std::string name = XML::getProperty(node, "name", "");
882  name = toLower(name);
883 
884  const bool isFringeLayer = (name.substr(0, 6) == "fringe");
885  const bool isCollisionLayer = (name.substr(0, 9) == "collision");
886  const bool isHeightLayer = (name.substr(0, 7) == "heights");
887  const bool isActionsLayer = (name.substr(0, 7) == "actions");
888  int mask = 1;
889  int tileCondition = -1;
890  int conditionLayer = 0;
891 
893  if (isCollisionLayer)
894  layerType = MapLayerType::COLLISION;
895  else if (isHeightLayer)
896  layerType = MapLayerType::HEIGHTS;
897  else if (isActionsLayer)
898  layerType = MapLayerType::ACTIONS;
899 
900  map->indexTilesets();
901 
902  MapLayer *layer = nullptr;
903  MapHeights *heights = nullptr;
904 
905  logger->log("- Loading layer \"%s\"", name.c_str());
906  int x = 0;
907  int y = 0;
908 
909  // Load the tile data
910  for_each_xml_child_node(childNode, node)
911  {
912  if (xmlNameEqual(childNode, "properties"))
913  {
914  for_each_xml_child_node(prop, childNode)
915  {
916  if (!xmlNameEqual(prop, "property"))
917  continue;
918  const std::string pname = XML::getProperty(prop, "name", "");
919  const std::string value = XML::getProperty(prop, "value", "");
920  // ignoring any layer if property Hidden is 1
921  if (pname == "Hidden")
922  {
923  if (value == "1")
924  return;
925  }
926  else if (pname == "Version")
927  {
928  if (value > CHECK_VERSION)
929  return;
930  }
931  else if (pname == "NotVersion")
932  {
933  if (value <= CHECK_VERSION)
934  return;
935  }
936  else if (pname == "Mask")
937  {
938  mask = atoi(value.c_str());
939  }
940  else if (pname == "TileCondition")
941  {
942  tileCondition = atoi(value.c_str());
943  }
944  else if (pname == "ConditionLayer")
945  {
946  conditionLayer = atoi(value.c_str());
947  }
948  else if (pname == "SideView")
949  {
950  if (value != "down")
951  return;
952  }
953  }
954  }
955 
956  if (!xmlNameEqual(childNode, "data"))
957  continue;
958 
959  // Disable for future usage "TileCondition" attribute
960  // if already set ConditionLayer to non zero
961  if (conditionLayer != 0)
962  tileCondition = -1;
963 
964  switch (layerType)
965  {
966  case MapLayerType::TILES:
967  {
968  layer = new MapLayer(name,
969  offsetX, offsetY,
970  w, h,
971  isFringeLayer,
972  mask,
973  tileCondition);
974  map->addLayer(layer);
975  break;
976  }
978  {
979  heights = new MapHeights(w, h);
980  map->addHeights(heights);
981  break;
982  }
983  default:
986  break;
987  }
988 
989  const std::string encoding =
990  XML::getProperty(childNode, "encoding", "");
991  const std::string compression =
992  XML::getProperty(childNode, "compression", "");
993 
994  if (encoding == "base64")
995  {
996  if (readBase64Layer(childNode, map, layer, layerType,
997  heights, compression, x, y, w, h))
998  {
999  continue;
1000  }
1001  else
1002  {
1003  return;
1004  }
1005  }
1006  else if (encoding == "csv")
1007  {
1008  if (readCsvLayer(childNode, map, layer, layerType,
1009  heights, x, y, w, h))
1010  {
1011  continue;
1012  }
1013  else
1014  {
1015  return;
1016  }
1017  }
1018  else
1019  {
1020  const std::map<int, TileAnimation*> &tileAnimations
1021  = map->getTileAnimations();
1022  const bool hasAnimations = !tileAnimations.empty();
1023 
1024  // Read plain XML map file
1025  for_each_xml_child_node(childNode2, childNode)
1026  {
1027  if (!xmlNameEqual(childNode2, "tile"))
1028  continue;
1029 
1030  const int gid = XML::getProperty(childNode2, "gid", -1);
1031  setTile(map, layer, layerType, heights, x, y, gid);
1032  if (hasAnimations)
1033  {
1034  TileAnimationMapCIter it = tileAnimations.find(gid);
1035  if (it != tileAnimations.end())
1036  {
1037  TileAnimation *const ani = it->second;
1038  if (ani != nullptr)
1039  ani->addAffectedTile(layer, x + y * w);
1040  }
1041  }
1042 
1043  x++;
1044  if (x == w)
1045  {
1046  x = 0; y++;
1047  if (y >= h)
1048  break;
1049  }
1050  }
1051  }
1052 
1053  if (y < h)
1054  std::cerr << "TOO SMALL!\n";
1055  if (x != 0)
1056  std::cerr << "TOO SMALL!\n";
1057 
1058  // There can be only one data element
1059  break;
1060  }
1061 }
1062 
1064  const std::string &path,
1065  Map *const map)
1066 {
1067  BLOCK_START("MapReader::readTileset")
1068  if (node == nullptr)
1069  {
1070  BLOCK_END("MapReader::readTileset")
1071  return nullptr;
1072  }
1073 
1074  const int firstGid = XML::getProperty(node, "firstgid", 0);
1075  const int margin = XML::getProperty(node, "margin", 0);
1076  const int spacing = XML::getProperty(node, "spacing", 0);
1077  XML::Document* doc = nullptr;
1078  Tileset *set = nullptr;
1079  std::string pathDir(path);
1080  std::map<std::string, std::string> props;
1081 
1082  if (XmlHasProp(node, "source"))
1083  {
1084  std::string filename = XML::getProperty(node, "source", "");
1085  filename = resolveRelativePath(path, filename);
1086 
1087  doc = new XML::Document(filename, UseVirtFs_true, SkipError_false);
1088  node = doc->rootNode();
1089  if (node == nullptr)
1090  {
1091  delete doc;
1092  BLOCK_END("MapReader::readTileset")
1093  return nullptr;
1094  }
1095 
1096  // Reset path to be realtive to the tsx file
1097  pathDir = filename.substr(0, filename.rfind('/') + 1);
1098  }
1099 
1100  const int tw = XML::getProperty(node, "tilewidth", map->getTileWidth());
1101  const int th = XML::getProperty(node, "tileheight", map->getTileHeight());
1102 
1103  for_each_xml_child_node(childNode, node)
1104  {
1105  if (xmlNameEqual(childNode, "image"))
1106  {
1107  // ignore second other <image> tags in tileset
1108  if (set != nullptr)
1109  continue;
1110 
1111  const std::string source = XML::getProperty(
1112  childNode, "source", "");
1113 
1114  if (!source.empty())
1115  {
1116  Image *const tilebmp = Loader::getImage(
1117  resolveRelativePath(pathDir, source));
1118 
1119  if (tilebmp != nullptr)
1120  {
1121  set = new Tileset(tilebmp,
1122  tw, th,
1123  firstGid,
1124  margin,
1125  spacing);
1126  tilebmp->decRef();
1127 #ifdef USE_OPENGL
1128  if (tilebmp->getType() == ImageType::Image &&
1129  map->haveAtlas() == true &&
1131  {
1132  reportAlways("Error: image '%s' not present in atlas",
1133  source.c_str());
1134  }
1135 #endif // USE_OPENGL
1136  }
1137  else
1138  {
1139  reportAlways("Error: Failed to load tileset (%s)",
1140  source.c_str());
1141  }
1142  }
1143  }
1144  else if (xmlNameEqual(childNode, "properties"))
1145  {
1146  for_each_xml_child_node(propertyNode, childNode)
1147  {
1148  if (!xmlNameEqual(propertyNode, "property"))
1149  continue;
1150  const std::string name = XML::getProperty(
1151  propertyNode, "name", "");
1152  if (!name.empty())
1153  props[name] = XML::getProperty(propertyNode, "value", "");
1154  }
1155  }
1156  else if (xmlNameEqual(childNode, "tile"))
1157  {
1158  bool haveAnimation(false);
1159 
1160  for_each_xml_child_node(tileNode, childNode)
1161  {
1162  const bool isProps = xmlNameEqual(tileNode, "properties");
1163  const bool isAnim = xmlNameEqual(tileNode, "animation");
1164  if (!isProps && !isAnim)
1165  continue;
1166 
1167  const int tileGID = firstGid + XML::getProperty(
1168  childNode, "id", 0);
1169 
1170  Animation *ani = new Animation("from map");
1171 
1172  if (isProps)
1173  {
1174  // read tile properties to a map for simpler handling
1175  StringIntMap tileProperties;
1176  for_each_xml_child_node(propertyNode, tileNode)
1177  {
1178  if (!xmlNameEqual(propertyNode, "property"))
1179  continue;
1180 
1181  haveAnimation = true;
1182  const std::string name = XML::getProperty(
1183  propertyNode, "name", "");
1184  const int value = XML::getProperty(
1185  propertyNode, "value", 0);
1186  if (!name.empty())
1187  {
1188  tileProperties[name] = value;
1189  logger->log("Tile Prop of %d \"%s\" = \"%d\"",
1190  tileGID, name.c_str(), value);
1191  }
1192  }
1193 
1194  // create animation
1195  if (set == nullptr ||
1196  !config.getBoolValue("playMapAnimations"))
1197  {
1198  delete ani;
1199  continue;
1200  }
1201 
1202  for (int i = 0; ; i++)
1203  {
1204  const std::string iStr(toString(i));
1205  StringIntMapCIter iFrame
1206  = tileProperties.find("animation-frame" + iStr);
1207  StringIntMapCIter iDelay
1208  = tileProperties.find("animation-delay" + iStr);
1209  // possible need add random attribute?
1210  if (iFrame != tileProperties.end()
1211  && iDelay != tileProperties.end())
1212  {
1213  ani->addFrame(set->get(iFrame->second),
1214  iDelay->second, 0, 0, 100);
1215  }
1216  else
1217  {
1218  break;
1219  }
1220  }
1221  }
1222  else if (isAnim && !haveAnimation)
1223  {
1224  for_each_xml_child_node(frameNode, tileNode)
1225  {
1226  if (!xmlNameEqual(frameNode, "frame"))
1227  continue;
1228 
1229  const int tileId = XML::getProperty(
1230  frameNode, "tileid", 0);
1231  const int duration = XML::getProperty(
1232  frameNode, "duration", 0) / 10;
1233 
1234  if (set != nullptr)
1235  {
1236  ani->addFrame(set->get(tileId),
1237  duration,
1238  0, 0, 100);
1239  }
1240  }
1241  }
1242 
1243  if (ani->getLength() > 0)
1244  map->addAnimation(tileGID, new TileAnimation(ani));
1245  else
1246  delete2(ani)
1247  }
1248  }
1249  }
1250 
1251  delete doc;
1252 
1253  if (set != nullptr)
1254  set->setProperties(props);
1255  BLOCK_END("MapReader::readTileset")
1256  return set;
1257 }
1258 
1259 Map *MapReader::createEmptyMap(const std::string &restrict filename,
1260  const std::string &restrict realFilename)
1261 {
1262  logger->log1("Creating empty map");
1263  Map *const map = new Map("empty map",
1264  300, 300,
1266  map->setProperty("_filename", realFilename);
1267  map->setProperty("_realfilename", filename);
1268  updateMusic(map);
1269  map->setCustom(true);
1270  MapLayer *layer = new MapLayer("nolayer",
1271  0, 0,
1272  300, 300,
1273  false,
1274  1,
1275  -1);
1276  map->addLayer(layer);
1277  layer = new MapLayer("nolayer",
1278  0, 0,
1279  300, 300,
1280  true,
1281  1,
1282  -1);
1283  map->addLayer(layer);
1284  map->updateDrawLayersList();
1285  map->updateConditionLayers();
1286  map->preCacheLayers();
1287 
1288  return map;
1289 }
1290 
1291 void MapReader::updateMusic(Map *const map)
1292 {
1293  std::string name = map->getProperty("shortName");
1294  const size_t p = name.rfind('.');
1295  if (p != std::string::npos)
1296  name = name.substr(0, p);
1297  name.append(".ogg");
1298  if (VirtFs::exists(pathJoin(paths.getStringValue("music"), name)))
1299  map->setProperty("music", name);
1300 }
1301 
1302 #ifdef USE_OPENGL
1304 {
1306  return;
1307 
1308  const MapInfo *const info = MapDB::getAtlas(
1309  paths.getStringValue("emptyAtlasName"));
1310  if (info != nullptr)
1311  {
1313  info->atlas,
1314  *info->files);
1315  delete info;
1316  }
1317 }
1318 
1320 {
1321  if (mEmptyAtlas != nullptr)
1322  mEmptyAtlas->decRef();
1323 }
1324 #endif // USE_OPENGL
#define FOR_EACH(type, iter, array)
Definition: foreach.h:24
void addTileset(Tileset *const tileset)
Definition: map.cpp:311
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:208
const bool SkipError_false
Definition: skiperror.h:29
void log1(const char *const log_text)
Definition: logger.cpp:222
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:695
static void readLayer(const xmlNodePtr node, Map *const map)
Definition: mapreader.cpp:871
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:1256
void setAtlas(Resource *const atlas)
Definition: map.h:330
std::string fileName
Definition: testmain.cpp:36
int getTileHeight() const
Definition: map.h:180
virtual void decRef()
Definition: resource.cpp:49
void updateConditionLayers()
Definition: map.cpp:1713
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:1606
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:171
static std::string resolveRelativePath(std::string base, std::string relative)
Definition: mapreader.cpp:93
void setVersion(const int n)
Definition: map.h:309
#define BLOCK_END(name)
Definition: perfomance.h:79
void setWalkLayer(WalkLayer *const layer)
Definition: map.h:339
const Tileset * getTilesetWithGid(const int gid) const
Definition: map.cpp:688
StringIntMap::const_iterator StringIntMapCIter
Definition: stringmap.h:29
const std::string getProperty(const std::string &name, const std::string &def="") const
Definition: properties.h:58
void addLayer(MapLayer *const layer)
Definition: map.cpp:304
#define delete2(var)
Definition: delete2.h:24
#define A_NONNULL(...)
Definition: localconsts.h:179
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:789
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:1319
std::set< XML::Document * >::iterator DocIterator
Definition: mapreader.cpp:72
void indexTilesets()
Definition: map.cpp:1392
int getHeight() const
Definition: map.h:168
Logger * logger
Definition: logger.cpp:95
void updateDrawLayersList()
Definition: map.cpp:1619
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:1291
#define CAST_S32
Definition: cast.h:29
void clearIndexedTilesets()
Definition: map.cpp:1440
static void loadReplaceLayer(LayerInfoIterator &it, Map *map)
Definition: mapreader.cpp:323
const MapInfo * getAtlas(const std::string &name)
Definition: mapdb.cpp:215
void setTile(const int x, const int y, Image *const img)
Definition: maplayer.cpp:99
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:1268
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:1063
#define CHECK_VERSION
Definition: main.h:44
bool info(InputEvent &event)
Definition: commands.cpp:56
int getVersion() const
Definition: map.h:306
void addParticleEffect(const std::string &effectFile, const int x, const int y, const int w=0, const int h=0)
Definition: map.cpp:1088
uint32_t data
std::string toLower(std::string const &s)
size_t getLength() const
Definition: animation.h:69
void preCacheLayers()
Definition: map.cpp:1727
void setCustom(const bool b)
Definition: map.h:320
void addAnimation(const int gid, TileAnimation *const animation)
Definition: map.cpp:1672
Definition: map.h:68
xmlNodePtr rootNode()
Definition: libxml.cpp:166
static Map * readMap(const std::string &filename, const std::string &realFilename)
CollisionType ::T CollisionTypeT
Definition: collisiontype.h:39
bool haveAtlas() const
Definition: map.h:365
static void loadEmptyAtlas()
Definition: mapreader.cpp:1303
const StringVect * files
Definition: mapinfo.h:40
int getWidth() const
Definition: map.h:162
Configuration paths
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
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:518
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:555
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:652
void initializeAmbientLayers()
Definition: map.cpp:225
int getTileWidth() const
Definition: map.h:174
static void unloadTempLayers()
Definition: mapreader.cpp:313
Definition: image.h:61
const std::map< int, TileAnimation * > & getTileAnimations() const
Definition: map.h:326
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:1259
#define restrict
Definition: localconsts.h:176
const MapInfo * getMapAtlas(const std::string &name)
Definition: mapdb.cpp:202
#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:1450
MapLayerType ::T MapLayerTypeT
Definition: maplayertype.h:37
void log(const char *const log_text,...)
Definition: logger.cpp:243
const std::string getStr(const std::string &str)
Definition: podict.cpp:44
Resource * getAtlas(const std::string &name, const StringVect &files)
Definition: atlasloader.cpp:55
#define PRAGMACLANG6GCC(str)
Definition: localconsts.h:249
PoDict * translator
Definition: podict.cpp:27
#define reportAlways(...)
Definition: checkutils.h:252
GraphicsManager graphicsManager
void decRef()
Definition: image.cpp:521
void setActorsFix(const int x, const int y)
Definition: map.cpp:1705
std::map< std::string, xmlNodePtr >::iterator LayerInfoIterator
Definition: mapreader.cpp:71
static void loadLayers(const std::string &path)
Definition: mapreader.cpp:306