From cf471fa3f8b7aec3d44db4515536c78098a6011a Mon Sep 17 00:00:00 2001 From: Bjørn Lindeijer Date: Mon, 25 Jul 2005 00:19:33 +0000 Subject: Added modified Map, MapReader, ResourceManager classes and base64 functions from client to server. Nothing tested yet, just made sure it still compiled. --- src/.cvsignore | 1 + src/Makefile.am | 8 + src/being.cpp | 5 + src/being.h | 9 ++ src/connectionhandler.cpp | 6 +- src/map.cpp | 335 +++++++++++++++++++++++++++++++++++++++ src/map.h | 196 +++++++++++++++++++++++ src/mapreader.cpp | 393 ++++++++++++++++++++++++++++++++++++++++++++++ src/mapreader.h | 113 +++++++++++++ src/netsession.cpp | 2 +- src/resourcemanager.cpp | 201 ++++++++++++++++++++++++ src/resourcemanager.h | 89 +++++++++++ src/tests/.cvsignore | 2 + src/utils/base64.cpp | 152 ++++++++++++++++++ src/utils/base64.h | 44 ++++++ 15 files changed, 1553 insertions(+), 3 deletions(-) create mode 100644 src/map.cpp create mode 100644 src/map.h create mode 100644 src/mapreader.cpp create mode 100644 src/mapreader.h create mode 100644 src/resourcemanager.cpp create mode 100644 src/resourcemanager.h create mode 100644 src/tests/.cvsignore create mode 100644 src/utils/base64.cpp create mode 100644 src/utils/base64.h (limited to 'src') diff --git a/src/.cvsignore b/src/.cvsignore index 282522db..becb0e90 100644 --- a/src/.cvsignore +++ b/src/.cvsignore @@ -1,2 +1,3 @@ Makefile Makefile.in +tmwserv diff --git a/src/Makefile.am b/src/Makefile.am index affba32c..7ecc74ef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,10 @@ tmwserv_SOURCES = main.cpp \ debug.cpp \ defines.h \ items.h \ + map.cpp \ + map.h \ + mapreader.cpp \ + mapreader.h \ messagehandler.h \ messagehandler.cpp \ messagein.h \ @@ -32,6 +36,8 @@ tmwserv_SOURCES = main.cpp \ packet.cpp \ skill.h \ skill.cpp \ + resourcemanager.cpp \ + resourcemanager.h \ storage.h \ storage.cpp \ account.h \ @@ -49,6 +55,8 @@ tmwserv_SOURCES = main.cpp \ dal/dataproviderfactory.cpp \ dal/recordset.h \ dal/recordset.cpp \ + utils/base64.h \ + utils/base64.cpp \ utils/countedptr.h \ utils/functors.h \ utils/singleton.h \ diff --git a/src/being.cpp b/src/being.cpp index d3df77f5..0aa3d456 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -28,6 +28,11 @@ namespace tmwserv { +PATH_NODE::PATH_NODE(unsigned short x, unsigned short y): + x(x), y(y) +{ +} + /** * Constructor. */ diff --git a/src/being.h b/src/being.h index 2ebbd8cf..47766eaa 100644 --- a/src/being.h +++ b/src/being.h @@ -37,6 +37,15 @@ namespace tmwserv { +struct PATH_NODE { + /** + * Constructor. + */ + PATH_NODE(unsigned short x, unsigned short y); + + unsigned short x, y; +}; + /** * Structure type for the raw statistics of a Being. */ diff --git a/src/connectionhandler.cpp b/src/connectionhandler.cpp index 6a46b94e..2583e3fc 100644 --- a/src/connectionhandler.cpp +++ b/src/connectionhandler.cpp @@ -144,8 +144,10 @@ ConnectionHandler::startListen(ListenThreadData *ltd) } // Check client sockets - std::list::iterator i; - for (i = clients.begin(); i != clients.end(); ) { + NetComputers::iterator i; + + for (i = clients.begin(); i != clients.end(); ) + { NetComputer *comp = *i; TCPsocket s = (*i)->getSocket(); diff --git a/src/map.cpp b/src/map.cpp new file mode 100644 index 00000000..cb992de5 --- /dev/null +++ b/src/map.cpp @@ -0,0 +1,335 @@ +/* + * 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 + +namespace tmwserv +{ + + +MetaTile::MetaTile(): + whichList(0) +{ +} + + +Location::Location(int x, int y, MetaTile *tile): + x(x), y(y), tile(tile) +{ +} + +bool Location::operator< (const Location &loc) const +{ + return tile->Fcost > loc.tile->Fcost; +} + + +Map::Map(): + width(0), height(0), + tileWidth(32), tileHeight(32), + onClosedList(1), onOpenList(2) +{ + metaTiles = new MetaTile[width * height]; +} + +Map::Map(int width, int height): + width(width), height(height), + tileWidth(32), tileHeight(32), + onClosedList(1), onOpenList(2) +{ + metaTiles = new MetaTile[width * height]; +} + +Map::~Map() +{ + delete[] metaTiles; +} + +void +Map::setSize(int width, int height) +{ + this->width = width; + this->height = height; + delete[] metaTiles; + metaTiles = new MetaTile[width * height]; +} + +void +Map::setWalk(int x, int y, bool walkable) +{ + metaTiles[x + y * width].walkable = walkable; +} + +bool +Map::getWalk(int x, int y) +{ + // If walkable, check for colliding into a being + if (!tileCollides(x, y)) + { + /* + std::list::iterator i = beings.begin(); + while (i != beings.end()) { + Being *being = (*i); + // Collision when non-portal being is found at this location + if (being->x == x && being->y == y && being->job != 45) { + return false; + } + i++; + } + */ + return true; + } + else { + return false; + } +} + +bool +Map::tileCollides(int x, int y) +{ + // You can't walk outside of the map + if (x < 0 || y < 0 || x >= width || y >= height) { + return true; + } + + // Check if the tile is walkable + return !metaTiles[x + y * width].walkable; +} + +MetaTile* +Map::getMetaTile(int x, int y) +{ + return &metaTiles[x + y * width]; +} + +int +Map::getWidth() +{ + return width; +} + +int +Map::getHeight() +{ + return height; +} + +int +Map::getTileWidth() +{ + return tileWidth; +} + +int +Map::getTileHeight() +{ + return tileHeight; +} + +std::string +Map::getProperty(const std::string &name) +{ + std::map::iterator i = properties.find(name); + + if (i != properties.end()) + { + return (*i).second; + } + + return ""; +} + +bool +Map::hasProperty(const std::string &name) +{ + return (properties.find(name) != properties.end()); +} + +void +Map::setProperty(const std::string &name, const std::string &value) +{ + properties[name] = value; +} + +std::list +Map::findPath(int startX, int startY, int destX, int destY) +{ + // Path to be built up (empty by default) + std::list path; + + // Declare open list, a list with open tiles sorted on F cost + std::priority_queue openList; + + // Return when destination not walkable + if (!getWalk(destX, destY)) return path; + + // Reset starting tile's G cost to 0 + MetaTile *startTile = getMetaTile(startX, startY); + startTile->Gcost = 0; + + // Add the start point to the open list + openList.push(Location(startX, startY, startTile)); + + bool foundPath = false; + + // Keep trying new open tiles until no more tiles to try or target found + while (!openList.empty() && !foundPath) + { + // Take the location with the lowest F cost from the open list, and + // add it to the closed list. + Location curr = openList.top(); + openList.pop(); + + // If the tile is already on the closed list, this means it has already + // been processed with a shorter path to the start point (lower G cost) + if (curr.tile->whichList == onClosedList) + { + continue; + } + + // Put the current tile on the closed list + curr.tile->whichList = onClosedList; + + // Check the adjacent tiles + for (int dy = -1; dy <= 1; dy++) + { + for (int dx = -1; dx <= 1; dx++) + { + // Calculate location of tile to check + int x = curr.x + dx; + int y = curr.y + dy; + + // Skip if if we're checking the same tile we're leaving from, + // or if the new location falls outside of the map boundaries + if ((dx == 0 && dy == 0) || + (x < 0 || y < 0 || x >= width || y >= height)) + { + continue; + } + + MetaTile *newTile = getMetaTile(x, y); + + // Skip if the tile is on the closed list or is not walkable + if (newTile->whichList == onClosedList || !getWalk(x, y)) + { + continue; + } + + // When taking a diagonal step, verify that we can skip the + // corner. We allow skipping past beings but not past non- + // walkable tiles. + if (dx != 0 && dy != 0) + { + MetaTile *t1 = getMetaTile(curr.x, curr.y + dy); + MetaTile *t2 = getMetaTile(curr.x + dx, curr.y); + + if (!(t1->walkable && t2->walkable)) + { + continue; + } + } + + // Calculate G cost for this route, 10 for moving straight and + // 14 for moving diagonal + int Gcost = curr.tile->Gcost + ((dx == 0 || dy == 0) ? 10 : 14); + + // Skip if Gcost becomes too much + // Warning: probably not entirely accurate + if (Gcost > 200) + { + continue; + } + + if (newTile->whichList != onOpenList) + { + // Found a new tile (not on open nor on closed list) + // Update Hcost of the new tile using Manhatten distance + newTile->Hcost = 10 * (abs(x - destX) + abs(y - destY)); + + // Set the current tile as the parent of the new tile + newTile->parentX = curr.x; + newTile->parentY = curr.y; + + // Update Gcost and Fcost of new tile + newTile->Gcost = Gcost; + newTile->Fcost = newTile->Gcost + newTile->Hcost; + + if (x != destX || y != destY) { + // Add this tile to the open list + newTile->whichList = onOpenList; + openList.push(Location(x, y, newTile)); + } + else { + // Target location was found + foundPath = true; + } + } + else if (Gcost < newTile->Gcost) + { + // Found a shorter route. + // Update Gcost and Fcost of the new tile + newTile->Gcost = Gcost; + newTile->Fcost = newTile->Gcost + newTile->Hcost; + + // Set the current tile as the parent of the new tile + newTile->parentX = curr.x; + newTile->parentY = curr.y; + + // Add this tile to the open list (it's already + // there, but this instance has a lower F score) + openList.push(Location(x, y, newTile)); + } + } + } + } + + // Two new values to indicate whether a tile is on the open or closed list, + // this way we don't have to clear all the values between each pathfinding. + onClosedList += 2; + onOpenList += 2; + + // If a path has been found, iterate backwards using the parent locations + // to extract it. + if (foundPath) + { + int pathX = destX; + int pathY = destY; + + while (pathX != startX || pathY != startY) + { + // Add the new path node to the start of the path list + path.push_front(PATH_NODE(pathX, pathY)); + + // Find out the next parent + MetaTile *tile = getMetaTile(pathX, pathY); + pathX = tile->parentX; + pathY = tile->parentY; + } + } + + return path; +} + +} // namespace tmwserv diff --git a/src/map.h b/src/map.h new file mode 100644 index 00000000..99e0a2ad --- /dev/null +++ b/src/map.h @@ -0,0 +1,196 @@ +/* + * 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$ + */ + +#ifndef _TMW_MAP_H +#define _TMW_MAP_H + +#include "being.h" +#include +#include +#include + +namespace tmwserv +{ + + +struct PATH_NODE; + +/** + * A meta tile stores additional information about a location on a tile map. + * This is information that doesn't need to be repeated for each tile in each + * layer of the map. + */ +class MetaTile +{ + public: + /** + * Constructor. + */ + MetaTile(); + + // Pathfinding members + int Fcost; /**< Estimation of total path cost */ + int Gcost; /**< Cost from start to this location */ + int Hcost; /**< Estimated cost to goal */ + int whichList; /**< No list, open list or closed list */ + int parentX; /**< X coordinate of parent tile */ + int parentY; /**< Y coordinate of parent tile */ + bool walkable; /**< Can beings walk on this tile */ +}; + +/** + * A location on a tile map. Used for pathfinding, open list. + */ +class Location +{ + public: + /** + * Constructor. + */ + Location(int x, int y, MetaTile *tile); + + /** + * Comparison operator. + */ + bool operator< (const Location &loc) const; + + int x, y; + MetaTile *tile; +}; + +/** + * A tile map. + */ +class Map +{ + public: + /** + * Constructor. + */ + Map(); + + /** + * Constructor that takes initial map size as parameters. + */ + Map(int width, int height); + + /** + * Destructor. + */ + ~Map(); + + /** + * Sets the size of the map. This will destroy any existing map data. + */ + void + setSize(int width, int height); + + /** + * Get tile reference. + */ + MetaTile* + getMetaTile(int x, int y); + + /** + * Set walkability flag for a tile + */ + void + setWalk(int x, int y, bool walkable); + + /** + * Tell if a tile is walkable or not, includes checking beings. + */ + bool + getWalk(int x, int y); + + /** + * Tell if a tile collides, not including a check on beings. + */ + bool + tileCollides(int x, int y); + + /** + * Returns the width of this map. + */ + int + getWidth(); + + /** + * Returns the height of this map. + */ + int + getHeight(); + + /** + * Returns the tile width of this map. + */ + int + getTileWidth(); + + /** + * Returns the tile height used by this map. + */ + int + getTileHeight(); + + /** + * Find a path from one location to the next. + */ + std::list + findPath(int startX, int startY, + int destX, int destY); + + /** + * Get a map property. + * + * @return the value of the given property or an empty string when it + * doesn't exist. + */ + std::string + getProperty(const std::string &name); + + /** + * Returns whether a certain property is available. + */ + bool + hasProperty(const std::string &name); + + /** + * Set a map property. + */ + void + setProperty(const std::string &name, const std::string &value); + + private: + int width, height; + int tileWidth, tileHeight; + MetaTile *metaTiles; + std::map properties; + + // Pathfinding members + int onClosedList, onOpenList; +}; + +} // namespace tmwserv + +#endif diff --git a/src/mapreader.cpp b/src/mapreader.cpp new file mode 100644 index 00000000..15623b1b --- /dev/null +++ b/src/mapreader.cpp @@ -0,0 +1,393 @@ +/* + * 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 "utils/logger.h" +#include "utils/base64.h" +#include "mapreader.h" +#include "resourcemanager.h" + +#include + +#include +#include + +namespace tmwserv +{ + + +const unsigned int DEFAULT_TILE_WIDTH = 32; +const unsigned int DEFAULT_TILE_HEIGHT = 32; + +// MSVC libxml2 at the moment doesn't work right when using MinGW, missing this +// function at link time. +#ifdef WIN32 +#undef xmlFree +#define xmlFree(x) ; +#endif + +/** + * Inflates either zlib or gzip deflated memory. The inflated memory is + * expected to be freed by the caller. + */ +int +inflateMemory( + unsigned char *in, unsigned int inLength, + unsigned char *&out, unsigned int &outLength) +{ + int bufferSize = 256 * 1024; + int ret; + z_stream strm; + + out = (unsigned char*)malloc(bufferSize); + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.next_in = in; + strm.avail_in = inLength; + strm.next_out = out; + strm.avail_out = bufferSize; + + ret = inflateInit2(&strm, 15 + 32); + + if (ret != Z_OK) + return ret; + + do + { + if (strm.next_out == NULL) + { + inflateEnd(&strm); + return Z_MEM_ERROR; + } + + ret = inflate(&strm, Z_NO_FLUSH); + assert(ret != Z_STREAM_ERROR); + + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + return ret; + } + + if (ret != Z_STREAM_END) + { + out = (unsigned char*)realloc(out, bufferSize * 2); + + if (out == NULL) + { + inflateEnd(&strm); + return Z_MEM_ERROR; + } + + strm.next_out = out + bufferSize; + strm.avail_out = bufferSize; + bufferSize *= 2; + } + } + while (ret != Z_STREAM_END); + assert(strm.avail_in == 0); + + outLength = bufferSize - strm.avail_out; + (void)inflateEnd(&strm); + return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; +} + +std::vector MapReader::tilesets; + +Map *MapReader::readMap(const std::string &filename) +{ + // Load the file through resource manager + ResourceManager *resman = ResourceManager::getInstance(); + int fileSize; + void *buffer = resman->loadFile(filename, fileSize); + + if (buffer == NULL) + { + LOG_ERROR("Map file not found (" << filename.c_str() << ")"); + return NULL; + } + + // Inflate the gzipped map data + unsigned char *inflated; + unsigned int inflatedSize = 0; + int ret = inflateMemory((unsigned char*)buffer, fileSize, + inflated, inflatedSize); + free(buffer); + + if (ret == Z_MEM_ERROR) + { + LOG_ERROR("Error: Out of memory while decompressing map data!"); + return NULL; + } + else if (ret == Z_VERSION_ERROR) + { + LOG_ERROR("Error: Incompatible zlib version!"); + return NULL; + } + else if (ret == Z_DATA_ERROR) + { + LOG_ERROR("Error: Incorrect zlib compressed data!"); + return NULL; + } + else if (ret != Z_OK || inflated == NULL) + { + LOG_ERROR("Error: Unknown error while decompressing map data!"); + return NULL; + } + + xmlDocPtr doc = xmlParseMemory((char*)inflated, inflatedSize); + free(inflated); + + // Parse the inflated map data + if (doc) { + xmlNodePtr node = xmlDocGetRootElement(doc); + + if (!node || !xmlStrEqual(node->name, BAD_CAST "map")) { + LOG_ERROR("Error: Not a map file (" << filename << ")!"); + return NULL; + } + + return readMap(node, filename); + xmlFreeDoc(doc); + } else { + LOG_ERROR("Error while parsing map file (" << filename << ")!"); + } + + return NULL; +} + +Map* MapReader::readMap(xmlNodePtr node, const std::string &path) +{ + xmlChar *prop; + + // Take the filename off the path + std::string pathDir = path.substr(0, path.rfind("/") + 1); + + prop = xmlGetProp(node, BAD_CAST "version"); + xmlFree(prop); + + int w = getProperty(node, "width", 0); + int h = 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) + { + if (xmlStrEqual(node->name, BAD_CAST "property")) + { + // Example: + + 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")) + { + Tileset *tileset = readTileset(node, pathDir, map); + if (tileset) { + tilesets.push_back(tileset); + } + } + else if (xmlStrEqual(node->name, BAD_CAST "layer")) + { + LOG_INFO("- Loading layer " << layerNr); + readLayer(node, map, layerNr); + layerNr++; + } + } + + // Clean up tilesets + // TODO: Dereference them somewhere + tilesets.clear(); + + return map; +} + +void MapReader::readLayer(xmlNodePtr node, Map *map, int layer) +{ + 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")) + { + xmlChar *encoding = xmlGetProp(node, BAD_CAST "encoding"); + xmlChar *compression = xmlGetProp(node, BAD_CAST "compression"); + + if (encoding && xmlStrEqual(encoding, BAD_CAST "base64")) + { + xmlFree(encoding); + + if (compression) { + LOG_WARN("Warning: no layer compression supported!"); + xmlFree(compression); + return; + } + + // Read base64 encoded map file + xmlNodePtr dataChild = node->xmlChildrenNode; + if (!dataChild) continue; + + int len = strlen((const char*)dataChild->content) + 1; + unsigned char *charData = new unsigned char[len + 1]; + const char *charStart = (const char*)dataChild->content; + unsigned 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(charData, strlen((char*)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, layer, gid); + + x++; + 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 = getProperty(n2, "gid", -1); + setTileWithGid(map, x, y, layer, gid); + + x++; + if (x == w) {x = 0; y++;} + } + + n2 = n2->next; + } + } + + // There can be only one data element + break; + } + + node = node->next; + } +} + +Tileset* +MapReader::readTileset(xmlNodePtr node, const std::string &path, Map *map) +{ + if (xmlHasProp(node, BAD_CAST "source")) { + LOG_WARN("Warning: External tilesets not supported yet."); + return NULL; + } + + int firstGid = getProperty(node, "firstgid", 0); + int tw = getProperty(node, "tilewidth", map->getTileWidth()); + int th = getProperty(node, "tileheight", map->getTileHeight()); + + return new Tileset(tw, th, firstGid); +} + +int MapReader::getProperty(xmlNodePtr node, const char* name, int def) +{ + xmlChar *prop = xmlGetProp(node, BAD_CAST name); + if (prop) { + int val = atoi((char*)prop); + xmlFree(prop); + return val; + } + else { + return def; + } +} + +Tileset *MapReader::getTilesetWithGid(int gid) +{ + std::vector::iterator i; + Tileset *set = NULL; + + // Find the tileset with the highest firstGid below/eq to gid + for (i = tilesets.begin(); i != tilesets.end(); ++i) + { + if ((*i)->getFirstGid() <= gid) { + set = (*i); + } + else { + break; + } + } + + return set; +} + +void MapReader::setTileWithGid(Map *map, int x, int y, int layer, int gid) +{ + // Only load obstacle layer + if (layer == 3) + { + Tileset *set = getTilesetWithGid(gid); + map->setWalk(x, y, !set || (gid - set->getFirstGid() == 0)); + } +} + +} // namespace tmwserv diff --git a/src/mapreader.h b/src/mapreader.h new file mode 100644 index 00000000..7d89a8a8 --- /dev/null +++ b/src/mapreader.h @@ -0,0 +1,113 @@ +/* + * 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$ + */ + +#ifndef _INCLUDED_MAPREADER_H +#define _INCLUDED_MAPREADER_H + +#include "map.h" +#include +#include + +namespace tmwserv +{ + + +/** + * A tileset, which is on the server only used to store a firstgid. + */ +class Tileset +{ + public: + /** + * Constructor. + */ + Tileset(int w, int h, int firstGid): + mFirstGid(firstGid) + { + } + + /** + * Returns the first gid. + */ + int + getFirstGid() { return mFirstGid; } + + private: + int mFirstGid; +}; + +/** + * Reader for XML map files (*.tmx) + */ +class MapReader +{ + public: + /** + * Read an XML map from a file. + */ + static Map *readMap(const std::string &filename); + + /** + * Read an XML map from a parsed XML tree. The path is used to find the + * location of referenced tileset images. + */ + static Map *readMap(xmlNodePtr node, const std::string &path); + + private: + /** + * Reads a map layer. + */ + static void + readLayer(xmlNodePtr node, Map *map, int layer); + + /** + * Reads a tile set. + */ + static Tileset* + readTileset(xmlNodePtr node, const std::string &path, Map *map); + + /** + * Gets an integer property from an xmlNodePtr. + */ + static int + getProperty(xmlNodePtr node, const char* name, int def); + + /** + * Finds the tile set that a tile with the given global id is part of. + */ + static Tileset* + getTilesetWithGid(int gid); + + /** + * Sets a tile using a global tile id. Used by the layer loading + * routine. + */ + static void + setTileWithGid(Map *map, int x, int y, int layer, int gid); + + static std::vector tilesets; +}; + +} // namespace tmwserv + +#endif diff --git a/src/netsession.cpp b/src/netsession.cpp index f4f77214..130b18aa 100644 --- a/src/netsession.cpp +++ b/src/netsession.cpp @@ -116,7 +116,7 @@ NetComputer *NetSession::connect(const std::string &host, Uint16 port) // An asynchroneous wrapper could be created around this method. IPaddress address; - + if (!SDLNet_ResolveHost(&address, host.c_str(), port)) { TCPsocket tcpsock = SDLNet_TCP_Open(&address); diff --git a/src/resourcemanager.cpp b/src/resourcemanager.cpp new file mode 100644 index 00000000..64f45425 --- /dev/null +++ b/src/resourcemanager.cpp @@ -0,0 +1,201 @@ +/* + * 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 "resourcemanager.h" +#include "utils/logger.h" +#include +#include +#include + +#include + +#ifdef WIN32 +#include +#include +#else +#include +#include +#endif + +#define TMW_DATADIR "" + +ResourceManager *ResourceManager::instance = NULL; + +ResourceManager::ResourceManager() +{ + // Add zip files to PhysicsFS + searchAndAddZipFiles(); +} + +ResourceManager::~ResourceManager() +{ +} + +ResourceManager* +ResourceManager::getInstance() +{ + // Create a new instance if necessary. + if (instance == NULL) instance = new ResourceManager(); + return instance; +} + +void +ResourceManager::deleteInstance() +{ + if (instance != NULL) { + delete instance; + instance = NULL; + } +} + +void +ResourceManager::searchAndAddZipFiles() +{ + // Add the main data directory to our PhysicsFS search path + PHYSFS_addToSearchPath("data", 1); + PHYSFS_addToSearchPath(TMW_DATADIR "data", 1); + +#ifdef _WIN32 + // Define the path in which to search + std::string searchString = std::string("data/*.zip"); + + // Create our find file data structure + struct _finddata_t findFileInfo; + + // Find the first zipped file + long handle = + static_cast(::_findfirst(searchString.c_str(), &findFileInfo)); + long file = handle; + + // Loop until all files we're searching for are found + while (file >= 0) { + // Define the file path string + std::string filePath = std::string("data/") + + std::string(findFileInfo.name); + + LOG_INFO("Adding to PhysicsFS: " << findFileInfo.name); + + // Add the zip file to our PhysicsFS search path + PHYSFS_addToSearchPath(filePath.c_str(), 1); + + // Find the next file + file = ::_findnext(handle, &findFileInfo); + } + + // Shutdown findfile stuff + ::_findclose(handle); +#else + // Retrieve the current path + char programPath[256]; + getcwd(programPath, 256); + strncat(programPath, "/data", 256 - strlen(programPath) - 1); + + // Create our directory structure + DIR *dir = opendir(programPath); + + // Return if the directory is invalid + if (dir == NULL) { + return; + } + + struct dirent *direntry; + while ((direntry = readdir(dir)) != NULL) + { + char *ext = strstr(direntry->d_name, ".zip"); + + if (ext != NULL && strcmp(ext, ".zip") == 0) + { + // Define the file path string + std::string filePath = std::string(programPath) + + std::string("/") + std::string(direntry->d_name); + + LOG_INFO("Adding to PhysicsFS: " << filePath); + + // Add the zip file to our PhysicsFS search path + PHYSFS_addToSearchPath(filePath.c_str(), 1); + } + } + + closedir(dir); +#endif +} + +void* +ResourceManager::loadFile(const std::string &fileName, int &fileSize) +{ + // If the file doesn't exist indicate failure + if (!PHYSFS_exists(fileName.c_str())) { + LOG_WARN("Warning: " << fileName << " not found!"); + return NULL; + } + + // Attempt to open the specified file using PhysicsFS + PHYSFS_file* file = PHYSFS_openRead(fileName.c_str()); + + // If the handler is an invalid pointer indicate failure + if (file == NULL) { + LOG_WARN("Warning: " << fileName << " failed to load!"); + return NULL; + } + + // Get the size of the file + fileSize = PHYSFS_fileLength(file); + + // Allocate memory and load the file + void *buffer = malloc(fileSize); + PHYSFS_read(file, buffer, 1, fileSize); + + // Close the file and let the user deallocate the memory + PHYSFS_close(file); + + return buffer; +} + +std::vector +ResourceManager::loadTextFile(const std::string &fileName) +{ + int contentsLength; + char *fileContents = (char*)loadFile(fileName, contentsLength); + std::vector lines; + + if (!fileContents) + { + LOG_ERROR("Couldn't load text file: " << fileName); + return lines; + } + + // Reallocate and include terminating 0 character + fileContents = (char*)realloc(fileContents, contentsLength + 1); + fileContents[contentsLength] = '\0'; + + // Tokenize and add each line separately + char *line = strtok(fileContents, "\n"); + while (line != NULL) + { + lines.push_back(line); + line = strtok(NULL, "\n"); + } + + free(fileContents); + return lines; +} diff --git a/src/resourcemanager.h b/src/resourcemanager.h new file mode 100644 index 00000000..a7d37f54 --- /dev/null +++ b/src/resourcemanager.h @@ -0,0 +1,89 @@ +/* + * 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$ + */ + +#ifndef _TMW_RESOURCE_MANAGER_H +#define _TMW_RESOURCE_MANAGER_H + +#include +#include +#include + +/** + * A class for loading and managing resources. + */ +class ResourceManager +{ + public: + /** + * Constructor. + */ + ResourceManager(); + + /** + * Destructor. + */ + ~ResourceManager(); + + /** + * Allocates data into a buffer pointer for raw data loading. The + * returned data is expected to be freed using free(). + * + * @param fileName The name of the file to be loaded. + * @param fileSize The size of the file that was loaded. + * + * @return An allocated byte array containing the data that was loaded, + * or NULL on fail. + */ + void* + loadFile(const std::string &fileName, int &fileSize); + + /** + * Retrieves the contents of a text file. + */ + std::vector + loadTextFile(const std::string &fileName); + + /** + * Returns an instance of the class, creating one if it does not + * already exist. + */ + static ResourceManager* + getInstance(); + + /** + * Deletes the class instance if it exists. + */ + static void + deleteInstance(); + + private: + /** + * Searches for zip files and adds them to the PhysicsFS search path. + */ + void + searchAndAddZipFiles(); + + static ResourceManager *instance; +}; + +#endif diff --git a/src/tests/.cvsignore b/src/tests/.cvsignore new file mode 100644 index 00000000..3dda7298 --- /dev/null +++ b/src/tests/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/src/utils/base64.cpp b/src/utils/base64.cpp new file mode 100644 index 00000000..e2df07de --- /dev/null +++ b/src/utils/base64.cpp @@ -0,0 +1,152 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Jim Winstead (jimw@php.net) | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include +#include + +#include "base64.h" + +static char base64_table[] = +{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0' +}; +static char base64_pad = '='; + +unsigned char *php_base64_encode(const unsigned char *str, int length, int *ret_length) { + const unsigned char *current = str; + int i = 0; + unsigned char *result = (unsigned char *)malloc(((length + 3 - length % 3) * 4 / 3 + 1) * sizeof(char)); + + while (length > 2) { /* keep going until we have less than 24 bits */ + result[i++] = base64_table[current[0] >> 2]; + result[i++] = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)]; + result[i++] = base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)]; + result[i++] = base64_table[current[2] & 0x3f]; + + current += 3; + length -= 3; /* we just handle 3 octets of data */ + } + + /* now deal with the tail end of things */ + if (length != 0) { + result[i++] = base64_table[current[0] >> 2]; + if (length > 1) { + result[i++] = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)]; + result[i++] = base64_table[(current[1] & 0x0f) << 2]; + result[i++] = base64_pad; + } + else { + result[i++] = base64_table[(current[0] & 0x03) << 4]; + result[i++] = base64_pad; + result[i++] = base64_pad; + } + } + if(ret_length) { + *ret_length = i; + } + result[i] = '\0'; + return result; +} + +/* as above, but backwards. :) */ +unsigned char *php_base64_decode(const unsigned char *str, int length, int *ret_length) { + const unsigned char *current = str; + int ch, i = 0, j = 0, k; + /* this sucks for threaded environments */ + static short reverse_table[256]; + static int table_built; + unsigned char *result; + + if (++table_built == 1) { + char *chp; + for(ch = 0; ch < 256; ch++) { + chp = strchr(base64_table, ch); + if(chp) { + reverse_table[ch] = chp - base64_table; + } else { + reverse_table[ch] = -1; + } + } + } + + result = (unsigned char *)malloc(length + 1); + if (result == NULL) { + return NULL; + } + + /* run through the whole string, converting as we go */ + while ((ch = *current++) != '\0') { + if (ch == base64_pad) break; + + /* When Base64 gets POSTed, all pluses are interpreted as spaces. + This line changes them back. It's not exactly the Base64 spec, + but it is completely compatible with it (the spec says that + spaces are invalid). This will also save many people considerable + headache. - Turadg Aleahmad + */ + + if (ch == ' ') ch = '+'; + + ch = reverse_table[ch]; + if (ch < 0) continue; + + switch(i % 4) { + case 0: + result[j] = ch << 2; + break; + case 1: + result[j++] |= ch >> 4; + result[j] = (ch & 0x0f) << 4; + break; + case 2: + result[j++] |= ch >>2; + result[j] = (ch & 0x03) << 6; + break; + case 3: + result[j++] |= ch; + break; + } + i++; + } + + k = j; + /* mop things up if we ended on a boundary */ + if (ch == base64_pad) { + switch(i % 4) { + case 0: + case 1: + free(result); + return NULL; + case 2: + k++; + case 3: + result[k++] = 0; + } + } + if(ret_length) { + *ret_length = j; + } + result[k] = '\0'; + return result; +} + diff --git a/src/utils/base64.h b/src/utils/base64.h new file mode 100644 index 00000000..5b275c45 --- /dev/null +++ b/src/utils/base64.h @@ -0,0 +1,44 @@ +/* + +----------------------------------------------------------------------+ + | PHP HTML Embedded Scripting Language Version 3.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997,1998 PHP Development Team (See Credits file) | + +----------------------------------------------------------------------+ + | This program is free software; you can redistribute it and/or modify | + | it under the terms of one of the following licenses: | + | | + | A) the GNU General Public License as published by the Free Software | + | Foundation; either version 2 of the License, or (at your option) | + | any later version. | + | | + | B) the PHP License as published by the PHP Development Team and | + | included in the distribution in the file: LICENSE | + | | + | This program 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 both licenses referred to here. | + | If you did not, or have any questions about PHP licensing, please | + | contact core@php.net. | + +----------------------------------------------------------------------+ + | Author: Jim Winstead (jimw@php.net) | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#ifndef _TMW_BASE64_H +#define _TMW_BASE64_H + +extern unsigned char *php_base64_encode(const unsigned char *, int, int *); +extern unsigned char *php_base64_decode(const unsigned char *, int, int *); + +#endif /* _TMW_BASE64_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ -- cgit v1.2.3-60-g2f50