00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "resources/animation.h"
00023 #include "resources/image.h"
00024 #include "resources/mapreader.h"
00025 #include "resources/resourcemanager.h"
00026
00027 #include "log.h"
00028 #include "map.h"
00029 #include "tileset.h"
00030
00031 #include "utils/base64.h"
00032 #include "utils/stringutils.h"
00033 #include "utils/xml.h"
00034
00035 #include <cassert>
00036 #include <iostream>
00037 #include <zlib.h>
00038
00039 const unsigned int DEFAULT_TILE_WIDTH = 32;
00040 const unsigned int DEFAULT_TILE_HEIGHT = 32;
00041
00046 int inflateMemory(unsigned char *in, unsigned int inLength,
00047 unsigned char *&out, unsigned int &outLength)
00048 {
00049 int bufferSize = 256 * 1024;
00050 int ret;
00051 z_stream strm;
00052
00053 out = (unsigned char*) malloc(bufferSize);
00054
00055 strm.zalloc = Z_NULL;
00056 strm.zfree = Z_NULL;
00057 strm.opaque = Z_NULL;
00058 strm.next_in = in;
00059 strm.avail_in = inLength;
00060 strm.next_out = out;
00061 strm.avail_out = bufferSize;
00062
00063 ret = inflateInit2(&strm, 15 + 32);
00064
00065 if (ret != Z_OK)
00066 return ret;
00067
00068 do
00069 {
00070 if (strm.next_out == NULL)
00071 {
00072 inflateEnd(&strm);
00073 return Z_MEM_ERROR;
00074 }
00075
00076 ret = inflate(&strm, Z_NO_FLUSH);
00077 assert(ret != Z_STREAM_ERROR);
00078
00079 switch (ret) {
00080 case Z_NEED_DICT:
00081 ret = Z_DATA_ERROR;
00082 case Z_DATA_ERROR:
00083 case Z_MEM_ERROR:
00084 (void) inflateEnd(&strm);
00085 return ret;
00086 }
00087
00088 if (ret != Z_STREAM_END)
00089 {
00090 out = (unsigned char*) realloc(out, bufferSize * 2);
00091
00092 if (out == NULL)
00093 {
00094 inflateEnd(&strm);
00095 return Z_MEM_ERROR;
00096 }
00097
00098 strm.next_out = out + bufferSize;
00099 strm.avail_out = bufferSize;
00100 bufferSize *= 2;
00101 }
00102 }
00103 while (ret != Z_STREAM_END);
00104 assert(strm.avail_in == 0);
00105
00106 outLength = bufferSize - strm.avail_out;
00107 (void) inflateEnd(&strm);
00108 return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
00109 }
00110
00111 int inflateMemory(unsigned char *in, unsigned int inLength,
00112 unsigned char *&out)
00113 {
00114 unsigned int outLength = 0;
00115 int ret = inflateMemory(in, inLength, out, outLength);
00116
00117 if (ret != Z_OK || out == NULL)
00118 {
00119 if (ret == Z_MEM_ERROR)
00120 {
00121 logger->log("Error: Out of memory while decompressing map data!");
00122 }
00123 else if (ret == Z_VERSION_ERROR)
00124 {
00125 logger->log("Error: Incompatible zlib version!");
00126 }
00127 else if (ret == Z_DATA_ERROR)
00128 {
00129 logger->log("Error: Incorrect zlib compressed data!");
00130 }
00131 else
00132 {
00133 logger->log("Error: Unknown error while decompressing map data!");
00134 }
00135
00136 free(out);
00137 out = NULL;
00138 outLength = 0;
00139 }
00140
00141 return outLength;
00142 }
00143
00144 Map *MapReader::readMap(const std::string &filename)
00145 {
00146 logger->log("Attempting to read map %s", filename.c_str());
00147
00148 ResourceManager *resman = ResourceManager::getInstance();
00149 int fileSize;
00150 void *buffer = resman->loadFile(filename, fileSize);
00151 Map *map = NULL;
00152
00153 if (buffer == NULL)
00154 {
00155 logger->log("Map file not found (%s)", filename.c_str());
00156 return NULL;
00157 }
00158
00159 unsigned char *inflated;
00160 unsigned int inflatedSize;
00161
00162 if (filename.find(".gz", filename.length() - 3) != std::string::npos)
00163 {
00164
00165 inflatedSize =
00166 inflateMemory((unsigned char*) buffer, fileSize, inflated);
00167 free(buffer);
00168
00169 if (inflated == NULL)
00170 {
00171 logger->log("Could not decompress map file (%s)",
00172 filename.c_str());
00173 return NULL;
00174 }
00175 }
00176 else
00177 {
00178 inflated = (unsigned char*) buffer;
00179 inflatedSize = fileSize;
00180 }
00181
00182 XML::Document doc((char*) inflated, inflatedSize);
00183 free(inflated);
00184
00185 xmlNodePtr node = doc.rootNode();
00186
00187
00188 if (node) {
00189 if (!xmlStrEqual(node->name, BAD_CAST "map")) {
00190 logger->log("Error: Not a map file (%s)!", filename.c_str());
00191 }
00192 else {
00193 map = readMap(node, filename);
00194 }
00195 } else {
00196 logger->log("Error while parsing map file (%s)!", filename.c_str());
00197 }
00198
00199 if (map) map->setProperty("_filename", filename);
00200
00201 return map;
00202 }
00203
00204 Map *MapReader::readMap(xmlNodePtr node, const std::string &path)
00205 {
00206
00207 const std::string pathDir = path.substr(0, path.rfind("/") + 1);
00208
00209 const int w = XML::getProperty(node, "width", 0);
00210 const int h = XML::getProperty(node, "height", 0);
00211 const int tilew = XML::getProperty(node, "tilewidth", DEFAULT_TILE_WIDTH);
00212 const int tileh = XML::getProperty(node, "tileheight", DEFAULT_TILE_HEIGHT);
00213 Map *map = new Map(w, h, tilew, tileh);
00214
00215 for_each_xml_child_node(childNode, node)
00216 {
00217 if (xmlStrEqual(childNode->name, BAD_CAST "tileset"))
00218 {
00219 Tileset *tileset = readTileset(childNode, pathDir, map);
00220 if (tileset) {
00221 map->addTileset(tileset);
00222 }
00223 }
00224 else if (xmlStrEqual(childNode->name, BAD_CAST "layer"))
00225 {
00226 readLayer(childNode, map);
00227 }
00228 else if (xmlStrEqual(childNode->name, BAD_CAST "properties"))
00229 {
00230 readProperties(childNode, map);
00231 }
00232 else if (xmlStrEqual(childNode->name, BAD_CAST "objectgroup"))
00233 {
00234
00235 const int tileOffsetX = XML::getProperty(childNode, "x", 0);
00236 const int tileOffsetY = XML::getProperty(childNode, "y", 0);
00237 const int offsetX = tileOffsetX * tilew;
00238 const int offsetY = tileOffsetY * tileh;
00239
00240 for_each_xml_child_node(objectNode, childNode)
00241 {
00242 if (xmlStrEqual(objectNode->name, BAD_CAST "object"))
00243 {
00244 const std::string objType =
00245 XML::getProperty(objectNode, "type", "");
00246
00247 if (objType == "WARP" || objType == "NPC" ||
00248 objType == "SCRIPT" || objType == "SPAWN")
00249 {
00250
00251 continue;
00252 }
00253
00254 const std::string objName =
00255 XML::getProperty(objectNode, "name", "");
00256 const int objX = XML::getProperty(objectNode, "x", 0);
00257 const int objY = XML::getProperty(objectNode, "y", 0);
00258
00259 logger->log("- Loading object name: %s type: %s at %d:%d",
00260 objName.c_str(), objType.c_str(),
00261 objX, objY);
00262
00263 if (objType == "PARTICLE_EFFECT")
00264 {
00265 if (objName.empty()) {
00266 logger->log(" Warning: No particle file given");
00267 continue;
00268 }
00269
00270 map->addParticleEffect(objName,
00271 objX + offsetX,
00272 objY + offsetY);
00273 }
00274 else
00275 {
00276 logger->log(" Warning: Unknown object type");
00277 }
00278 }
00279 }
00280 }
00281 }
00282
00283 map->initializeOverlays();
00284
00285 return map;
00286 }
00287
00288 void MapReader::readProperties(xmlNodePtr node, Properties *props)
00289 {
00290 for_each_xml_child_node(childNode, node)
00291 {
00292 if (!xmlStrEqual(childNode->name, BAD_CAST "property"))
00293 continue;
00294
00295
00296 const std::string name = XML::getProperty(childNode, "name", "");
00297 const std::string value = XML::getProperty(childNode, "value", "");
00298
00299 if (!name.empty() && !value.empty())
00300 props->setProperty(name, value);
00301 }
00302 }
00303
00304 static void setTile(Map *map, MapLayer *layer, int x, int y, int gid)
00305 {
00306 const Tileset * const set = map->getTilesetWithGid(gid);
00307 if (layer)
00308 {
00309
00310 Image * const img = set ? set->get(gid - set->getFirstGid()) : 0;
00311 layer->setTile(x, y, img);
00312 } else {
00313
00314 if (set && (gid - set->getFirstGid() != 0))
00315 map->blockTile(x, y, Map::BLOCKTYPE_WALL);
00316 }
00317 }
00318
00319 void MapReader::readLayer(xmlNodePtr node, Map *map)
00320 {
00321
00322 const int w = XML::getProperty(node, "width", map->getWidth());
00323 const int h = XML::getProperty(node, "height", map->getHeight());
00324 const int offsetX = XML::getProperty(node, "x", 0);
00325 const int offsetY = XML::getProperty(node, "y", 0);
00326 std::string name = XML::getProperty(node, "name", "");
00327 name = toLower(name);
00328
00329 const bool isFringeLayer = (name.substr(0,6) == "fringe");
00330 const bool isCollisionLayer = (name.substr(0,9) == "collision");
00331
00332 MapLayer *layer = 0;
00333
00334 if (!isCollisionLayer) {
00335 layer = new MapLayer(offsetX, offsetY, w, h, isFringeLayer);
00336 map->addLayer(layer);
00337 }
00338
00339 logger->log("- Loading layer \"%s\"", name.c_str());
00340 int x = 0;
00341 int y = 0;
00342
00343
00344 for_each_xml_child_node(childNode, node)
00345 {
00346 if (!xmlStrEqual(childNode->name, BAD_CAST "data"))
00347 continue;
00348
00349 const std::string encoding =
00350 XML::getProperty(childNode, "encoding", "");
00351 const std::string compression =
00352 XML::getProperty(childNode, "compression", "");
00353
00354 if (encoding == "base64")
00355 {
00356 if (!compression.empty() && compression != "gzip") {
00357 logger->log("Warning: only gzip layer compression supported!");
00358 return;
00359 }
00360
00361
00362 xmlNodePtr dataChild = childNode->xmlChildrenNode;
00363 if (!dataChild)
00364 continue;
00365
00366 int len = strlen((const char*)dataChild->content) + 1;
00367 unsigned char *charData = new unsigned char[len + 1];
00368 const char *charStart = (const char*)dataChild->content;
00369 unsigned char *charIndex = charData;
00370
00371 while (*charStart) {
00372 if (*charStart != ' ' && *charStart != '\t' &&
00373 *charStart != '\n')
00374 {
00375 *charIndex = *charStart;
00376 charIndex++;
00377 }
00378 charStart++;
00379 }
00380 *charIndex = '\0';
00381
00382 int binLen;
00383 unsigned char *binData =
00384 php3_base64_decode(charData, strlen((char*)charData), &binLen);
00385
00386 delete[] charData;
00387
00388 if (binData) {
00389 if (compression == "gzip") {
00390
00391 unsigned char *inflated;
00392 unsigned int inflatedSize =
00393 inflateMemory(binData, binLen, inflated);
00394
00395 free(binData);
00396 binData = inflated;
00397 binLen = inflatedSize;
00398
00399 if (!inflated) {
00400 logger->log("Error: Could not decompress layer!");
00401 return;
00402 }
00403 }
00404
00405 for (int i = 0; i < binLen - 3; i += 4) {
00406 const int gid = binData[i] |
00407 binData[i + 1] << 8 |
00408 binData[i + 2] << 16 |
00409 binData[i + 3] << 24;
00410
00411 setTile(map, layer, x, y, gid);
00412
00413 TileAnimation* ani = map->getAnimationForGid(gid);
00414 if (ani)
00415 {
00416 ani->addAffectedTile(layer, x + y * w);
00417 }
00418
00419 x++;
00420 if (x == w) {
00421 x = 0; y++;
00422
00423
00424 if (y == h)
00425 break;
00426 }
00427 }
00428 free(binData);
00429 }
00430 }
00431 else {
00432
00433 for_each_xml_child_node(childNode2, childNode)
00434 {
00435 if (!xmlStrEqual(childNode2->name, BAD_CAST "tile"))
00436 continue;
00437
00438 const int gid = XML::getProperty(childNode2, "gid", -1);
00439 setTile(map, layer, x, y, gid);
00440
00441 x++;
00442 if (x == w) {
00443 x = 0; y++;
00444 if (y >= h)
00445 break;
00446 }
00447 }
00448 }
00449
00450 if (y < h)
00451 std::cerr << "TOO SMALL!\n";
00452 if (x)
00453 std::cerr << "TOO SMALL!\n";
00454
00455
00456 break;
00457 }
00458 }
00459
00460 Tileset *MapReader::readTileset(xmlNodePtr node,
00461 const std::string &path,
00462 Map *map)
00463 {
00464 int firstGid = XML::getProperty(node, "firstgid", 0);
00465 XML::Document* doc = NULL;
00466 Tileset *set = NULL;
00467
00468 if (xmlHasProp(node, BAD_CAST "source"))
00469 {
00470 std::string filename = XML::getProperty(node, "source", "");
00471 while (filename.substr(0, 3) == "../")
00472 filename.erase(0, 3);
00473 doc = new XML::Document(filename);
00474 node = doc->rootNode();
00475 firstGid += XML::getProperty(node, "firstgid", 0);
00476 }
00477
00478 const int tw = XML::getProperty(node, "tilewidth", map->getTileWidth());
00479 const int th = XML::getProperty(node, "tileheight", map->getTileHeight());
00480
00481 for_each_xml_child_node(childNode, node)
00482 {
00483 if (xmlStrEqual(childNode->name, BAD_CAST "image"))
00484 {
00485 const std::string source = XML::getProperty(childNode, "source", "");
00486
00487 if (!source.empty())
00488 {
00489 std::string sourceStr = source;
00490 sourceStr.erase(0, 3);
00491
00492 ResourceManager *resman = ResourceManager::getInstance();
00493 Image* tilebmp = resman->getImage(sourceStr);
00494
00495 if (tilebmp)
00496 {
00497 set = new Tileset(tilebmp, tw, th, firstGid);
00498 tilebmp->decRef();
00499 }
00500 else {
00501 logger->log("Warning: Failed to load tileset (%s)",
00502 source.c_str());
00503 }
00504 }
00505 }
00506 else if (xmlStrEqual(childNode->name, BAD_CAST "tile"))
00507 {
00508 for_each_xml_child_node(tileNode, childNode)
00509 {
00510 if (!xmlStrEqual(tileNode->name, BAD_CAST "properties")) continue;
00511
00512 int tileGID = firstGid + XML::getProperty(childNode, "id", 0);
00513
00514
00515 std::map<std::string, int> tileProperties;
00516 for_each_xml_child_node(propertyNode, tileNode)
00517 {
00518 if (!xmlStrEqual(propertyNode->name, BAD_CAST "property")) continue;
00519 std::string name = XML::getProperty(propertyNode, "name", "");
00520 int value = XML::getProperty(propertyNode, "value", 0);
00521 tileProperties[name] = value;
00522 logger->log("Tile Prop of %d \"%s\" = \"%d\"", tileGID, name.c_str(), value);
00523 }
00524
00525
00526 if (!set) continue;
00527
00528 Animation *ani = new Animation;
00529 for (int i = 0; ;i++)
00530 {
00531 std::map<std::string, int>::iterator iFrame, iDelay;
00532 iFrame = tileProperties.find("animation-frame" + toString(i));
00533 iDelay = tileProperties.find("animation-delay" + toString(i));
00534 if (iFrame != tileProperties.end() && iDelay != tileProperties.end())
00535 {
00536 ani->addFrame(set->get(iFrame->second), iDelay->second, 0, 0);
00537 } else {
00538 break;
00539 }
00540 }
00541
00542 if (ani->getLength() > 0)
00543 {
00544 map->addAnimation(tileGID, new TileAnimation(ani));
00545 logger->log("Animation length: %d", ani->getLength());
00546 } else {
00547 delete ani;
00548 }
00549 }
00550 }
00551 }
00552
00553 delete doc;
00554
00555 return set;
00556 }