summaryrefslogtreecommitdiff
path: root/src/game-server/mapreader.cpp
diff options
context:
space:
mode:
authorGuillaume Melquiond <guillaume.melquiond@gmail.com>2007-01-02 22:41:02 +0000
committerGuillaume Melquiond <guillaume.melquiond@gmail.com>2007-01-02 22:41:02 +0000
commit6b7441516002d6e7cca424416bb67c6bc7d7c9d2 (patch)
tree6ce7822b0ca6e0abdfbbd938575a3c5bcf7b5614 /src/game-server/mapreader.cpp
parent59df651232df73c56b5dae587fa68e1e9f824755 (diff)
downloadmanaserv-6b7441516002d6e7cca424416bb67c6bc7d7c9d2.tar.gz
manaserv-6b7441516002d6e7cca424416bb67c6bc7d7c9d2.tar.bz2
manaserv-6b7441516002d6e7cca424416bb67c6bc7d7c9d2.tar.xz
manaserv-6b7441516002d6e7cca424416bb67c6bc7d7c9d2.zip
Simplified MapReader interface and code. Fixed some memory leaks.
Diffstat (limited to 'src/game-server/mapreader.cpp')
-rw-r--r--src/game-server/mapreader.cpp262
1 files changed, 262 insertions, 0 deletions
diff --git a/src/game-server/mapreader.cpp b/src/game-server/mapreader.cpp
new file mode 100644
index 00000000..d3a49a3c
--- /dev/null
+++ b/src/game-server/mapreader.cpp
@@ -0,0 +1,262 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+#include "map.h"
+#include "resourcemanager.h"
+#include "game-server/mapreader.hpp"
+#include "utils/base64.h"
+#include "utils/logger.h"
+#include "utils/xml.hpp"
+#include "utils/zlib.hpp"
+
+static std::vector< int > tilesetFirstGids;
+
+static Map* readMap(xmlNodePtr node, std::string const &path);
+static void readLayer(xmlNodePtr node, Map *map);
+static void setTileWithGid(Map *map, int x, int y, int gid);
+
+Map *MapReader::readMap(const std::string &filename)
+{
+ // Load the file through resource manager.
+ ResourceManager *resman = ResourceManager::getInstance();
+ int fileSize;
+ char *buffer = (char *)resman->loadFile(filename, fileSize);
+
+ if (buffer == NULL)
+ {
+ LOG_ERROR("Error: Map file not found (" << filename.c_str() << ")", 0);
+ return NULL;
+ }
+
+ // Inflate the gzipped map data.
+ char *inflated;
+ unsigned inflatedSize = 0;
+ bool ret = inflateMemory(buffer, fileSize, inflated, inflatedSize);
+ free(buffer);
+
+ xmlDocPtr doc = NULL;
+
+ if (ret)
+ {
+ doc = xmlParseMemory(inflated, inflatedSize);
+ free(inflated);
+ }
+
+ if (!doc)
+ {
+ LOG_ERROR("Error while parsing map file (" << filename << ")!", 0);
+ return NULL;
+ }
+
+ Map *map = NULL;
+ xmlNodePtr node = xmlDocGetRootElement(doc);
+
+ // Parse the inflated map data.
+ if (node && xmlStrEqual(node->name, BAD_CAST "map"))
+ {
+ map = ::readMap(node, filename);
+ }
+ else
+ {
+ LOG_ERROR("Error: Not a map file (" << filename << ")!", 0);
+ }
+
+ xmlFreeDoc(doc);
+ return map;
+}
+
+static Map *readMap(xmlNodePtr node, std::string const &path)
+{
+ // Take the filename off the path
+ std::string pathDir = path.substr(0, path.rfind("/") + 1);
+
+ int w = XML::getProperty(node, "width", 0);
+ int h = XML::getProperty(node, "height", 0);
+ // We only support tile width of 32 at the moment
+ //int tilew = getProperty(node, "tilewidth", DEFAULT_TILE_WIDTH);
+ //int tileh = getProperty(node, "tileheight", DEFAULT_TILE_HEIGHT);
+ int layerNr = 0;
+ Map* map = new Map(w, h);
+
+ for (node = node->xmlChildrenNode; node != NULL; node = node->next)
+ {
+ /* // Properties are useless server-side.
+ if (xmlStrEqual(node->name, BAD_CAST "property"))
+ {
+ // Example: <property name="name" value="value"/>
+
+ xmlChar *name = xmlGetProp(node, BAD_CAST "name");
+ xmlChar *value = xmlGetProp(node, BAD_CAST "value");
+
+ if (name && value)
+ {
+ map->setProperty((const char*)name, (const char*)value);
+ }
+
+ if (name) xmlFree(name);
+ if (value) xmlFree(value);
+ }
+ else
+ */
+ if (xmlStrEqual(node->name, BAD_CAST "tileset"))
+ {
+ if (xmlHasProp(node, BAD_CAST "source"))
+ {
+ LOG_WARN("Warning: External tilesets not supported yet.", 0);
+ }
+ else
+ {
+ tilesetFirstGids.push_back(XML::getProperty(node, "firstgid", 0));
+ }
+ }
+ else if (xmlStrEqual(node->name, BAD_CAST "layer"))
+ {
+ // Layer 3 is collision layer.
+ if (layerNr++ == 3)
+ {
+ readLayer(node, map);
+ }
+ }
+ }
+
+ // Clean up tilesets
+ tilesetFirstGids.clear();
+
+ return map;
+}
+
+static void readLayer(xmlNodePtr node, Map *map)
+{
+ node = node->xmlChildrenNode;
+ int h = map->getHeight();
+ int w = map->getWidth();
+ int x = 0;
+ int y = 0;
+
+ // Load the tile data. Layers are assumed to be map size, with (0,0) as
+ // origin.
+ while (node != NULL)
+ {
+ if (xmlStrEqual(node->name, BAD_CAST "data"))
+ {
+ if (XML::getProperty(node, "encoding", std::string()) == "base64")
+ {
+ if (xmlHasProp(node, BAD_CAST "compression"))
+ {
+ LOG_WARN("Warning: no layer compression supported!", 0);
+ return;
+ }
+
+ // Read base64 encoded map file
+ xmlNodePtr dataChild = node->xmlChildrenNode;
+ if (!dataChild) continue;
+
+ int len = strlen((char const *)dataChild->content) + 1;
+ char *charData = new char[len + 1];
+ char const *charStart = (char const *)dataChild->content;
+ char *charIndex = charData;
+
+ while (*charStart)
+ {
+ if (*charStart != ' ' && *charStart != '\t' && *charStart != '\n')
+ {
+ *charIndex = *charStart;
+ ++charIndex;
+ }
+ ++charStart;
+ }
+ *charIndex = '\0';
+
+ int binLen;
+ unsigned char *binData =
+ php_base64_decode((unsigned char *)charData, strlen(charData), &binLen);
+
+ delete[] charData;
+
+ if (binData)
+ {
+ for (int i = 0; i < binLen - 3; i += 4)
+ {
+ int gid = binData[i] |
+ (binData[i + 1] << 8) |
+ (binData[i + 2] << 16) |
+ (binData[i + 3] << 24);
+
+ setTileWithGid(map, x, y, gid);
+
+ if (++x == w)
+ {
+ x = 0;
+ ++y;
+ }
+ }
+ free(binData);
+ }
+ }
+ else
+ {
+ // Read plain XML map file
+ xmlNodePtr n2 = node->xmlChildrenNode;
+
+ while (n2 != NULL)
+ {
+ if (xmlStrEqual(n2->name, BAD_CAST "tile") && y < h)
+ {
+ int gid = XML::getProperty(n2, "gid", -1);
+ setTileWithGid(map, x, y, gid);
+
+ if (++x == w)
+ {
+ x = 0;
+ ++y;
+ }
+ }
+
+ n2 = n2->next;
+ }
+ }
+
+ // There can be only one data element
+ break;
+ }
+
+ node = node->next;
+ }
+}
+
+static void setTileWithGid(Map *map, int x, int y, int gid)
+{
+ // Find the tileset with the highest firstGid below/eq to gid
+ int set = gid;
+ for (std::vector< int >::const_iterator i = tilesetFirstGids.begin(),
+ i_end = tilesetFirstGids.end(); i != i_end; ++i)
+ {
+ if (gid < *i)
+ {
+ break;
+ }
+ set = *i;
+ }
+
+ map->setWalk(x, y, gid == set);
+}