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