diff options
Diffstat (limited to 'tools/tmxcopy/map.cpp')
-rw-r--r-- | tools/tmxcopy/map.cpp | 679 |
1 files changed, 0 insertions, 679 deletions
diff --git a/tools/tmxcopy/map.cpp b/tools/tmxcopy/map.cpp deleted file mode 100644 index 3e1f9bdaf..000000000 --- a/tools/tmxcopy/map.cpp +++ /dev/null @@ -1,679 +0,0 @@ -/* - * TMXCopy - * Copyright (C) 2007 Philipp Sehmisch - * Copyright (C) 2009 Steve Cotton - * - * This program 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. - * - * 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 the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <cstring> -#include <iostream> -#include <list> - -#include <string.h> -#include <zlib.h> -#include <cassert> -#include <ctime> - -#include "xmlutils.h" -#include "zlibutils.h" -#include "base64.h" -#include "tostring.h" - -#include "map.hpp" - -Map::Map(std::string filename): - mMaxGid(0) -{ - std::cout<<"Loading map "<<filename<<std::endl; - //load XML tree - mXmlDoc = xmlReadFile(filename.c_str(), NULL, 0); - - if (!mXmlDoc) - { - std::cerr<<"Could not load "<<filename; - throw 1; - } - - xmlNodePtr rootNode = xmlDocGetRootElement(mXmlDoc); - - if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "map")) { - std::cerr<<filename<<"is not a Tiled map file!"; - throw 1; - } - - mWidth = XML::getProperty(rootNode, "width", 0); - mHeight = XML::getProperty(rootNode, "height", 0); - int layerNum = 0; - for_each_xml_child_node(node, rootNode) - { - if (xmlStrEqual(node->name, BAD_CAST "tileset")) - { - //add tilesets to vector of used tilesets - Tileset* tileset = new Tileset; - tileset->name = XML::getProperty(node, "name", "Unnamed"); - tileset->tilewidth = XML::getProperty(node, "tilewidth", 0); - tileset->tileheight = XML::getProperty(node, "tileheight", 0); - tileset->firstgid = XML::getProperty(node, "firstgid", 0); - for_each_xml_child_node(imageNode, node) - { - if (xmlStrEqual(imageNode->name, BAD_CAST "image")) - tileset->imagefile = XML::getProperty(imageNode, "source", ""); - } - - //add tileset to tileset list - Map::mTilesets.push_back(tileset); - } - } - - for_each_xml_child_node(node, rootNode) - { - if (xmlStrEqual(node->name, BAD_CAST "layer")) - { - //build layer information - std::string name = XML::getProperty(node, "name", ""); - Layer* layer = new Layer(name, mWidth * mHeight); - if ( - (mWidth != XML::getProperty(node, "width" , 0)) || - (mHeight != XML::getProperty(node, "height", 0)) || - (0 != XML::getProperty(node, "x" , 0)) || - (0 != XML::getProperty(node, "y" , 0))) - { - std::cerr<<"Error: layer size does not match map size for layer \""<<name<<"\" in "<<filename<<std::endl; - throw 1; - } - - for_each_xml_child_node(dataNode, node) - { - if (!xmlStrEqual(dataNode->name, BAD_CAST "data")) continue; - - std::string encoding = XML::getProperty(dataNode, "encoding", ""); - std::string compression = XML::getProperty(dataNode, "compression", ""); - - if (encoding != "base64") - { - std::cerr<<"Layers in "<<filename<<" are not base64 encoded!"; - return; - } - - // Read base64 encoded map file - xmlNodePtr dataChild = dataNode->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 = - php3_base64_decode(charData, strlen((char*)charData), &binLen); - - delete[] charData; - - if (binData) { - if (compression == "gzip") - { - unsigned char *inflated; - unsigned int inflatedSize = - inflateMemory(binData, binLen, inflated); - free(binData); - binData = inflated; - binLen = inflatedSize; - if (inflated == NULL) - { - std::cerr<<"Error: while decompressing layer "<<layerNum<<" in "<<filename; - throw 1; - } - } - - int c = 0; - 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; - - if (gid == 0) - { - layer->at(c).tileset = -1; - layer->at(c).index = 0; - } - else - { - for (int s = mTilesets.size()-1; s >= 0; s--) - { - if (mTilesets.at(s)->firstgid <= gid) - { - layer->at(c).tileset = s; - layer->at(c).index = gid - mTilesets.at(s)->firstgid; - if (mMaxGid < gid) mMaxGid = gid; - break; - } - } - } - - c++; - } - free(binData); - } else { - std::cerr<<"error processing layer data in "<<filename<<std::endl; - } - } - mLayers.push_back(layer); - layerNum++; - } - } - - std::cout<<"tilesets: "<<mTilesets.size()<<std::endl; - std::cout<<"layers:"<<mLayers.size()<<std::endl; - std::cout<<"largest GID:"<<mMaxGid<<std::endl<<std::endl; -} - -/** - * When copying tiles from another map, add new tilesets to this map, and return the translation table. - */ -std::map<int, int> Map::addAndTranslateTilesets(const Map* srcMap) -{ - std::map<int, int> translation; - translation[-1] = -1; - std::vector<Tileset*>* srcTilesets = const_cast<Map*>(srcMap)->getTilesets(); - - for (std::vector<Tileset*>::size_type a = 0; a < srcTilesets->size(); a++) - { - std::vector<Tileset*>::size_type b; - for (b = 0; b < mTilesets.size(); b++) - { - if (*srcTilesets->at(a) == *mTilesets.at(b)) - { - break; - } - } - if (b == mTilesets.size()) - { - mMaxGid += srcTilesets->at(a)->firstgid; - Tileset* destTileset = new Tileset(*srcTilesets->at(a)); - destTileset->firstgid = mMaxGid;//it is possible to get some holes in the gid index this way but who cares, we got 32bit. - mTilesets.push_back(destTileset); - } - translation[a] = b; - } - return translation; -} - -bool Map::overwrite( Map* srcMap, - int srcX, int srcY, int srcWidth, int srcHeight, - int destX, int destY, - const ConfigurationOptions& config) -{ - //plausibility check of coordinates - bool checkPassed = true; - if (srcX + srcWidth > srcMap->getWidth()) { - std::cerr<<"Error: Area exceeds right map border of source map!"<<std::endl; - checkPassed = false; - } - if (srcY + srcHeight > srcMap->getHeight()) { - std::cerr<<"Error: Area exceeds lower map border of source map!"<<std::endl; - checkPassed = false; - } - if (destX + srcWidth > mWidth) { - std::cerr<<"Error: Area exceeds right map border of target map!"<<std::endl; - checkPassed = false; - } - if (destY + srcHeight > mHeight) { - std::cerr<<"Error: Area exceeds lower map border of target map!"<<std::endl; - checkPassed = false; - } - if (!config.createMissingLayers) - { - if (config.copyLayersByOrdinal) - { - if (srcMap->getNumberOfLayers() > mLayers.size()) { - std::cerr<<"Error: Source has more layers than target map"<<std::endl - <<"(and the command-line \"create layers\" option was not used)"<<std::endl; - checkPassed = false; - } - } - else - { - for (size_t i = 0; i < srcMap->getNumberOfLayers(); i++) - { - Layer* srcLayer = srcMap->getLayer(i); - Layer* destLayer = getLayer(srcLayer->getName()); - if (!destLayer) - { - std::cerr<<"Error: target map has no layer named \""<<srcLayer->getName()<<"\""<<std::endl - <<"(and the command-line \"create layers\" option was not used)"<<std::endl; - checkPassed = false; - } - } - } - } - - if (!checkPassed) return false; - - std::map<int, int> translation = addAndTranslateTilesets(srcMap); - - - //combining layer information - for (size_t i = 0; i < srcMap->getNumberOfLayers(); i++) - { - Layer* srcLayer = srcMap->getLayer(i); - Layer* destLayer = NULL; - if (config.copyLayersByOrdinal) - { - if (i < mLayers.size()) - { - destLayer = mLayers.at(i); - } - } - else - { - destLayer = getLayer(srcLayer->getName()); - } - - if (!destLayer) - { - assert(config.createMissingLayers); /* Tested earlier, in the checkPassed section */ - /* Generate a name for the new layer, which must be - * unique in the target map, and should be unique in - * the source map (to avoid collisions later in the - * copying process). - * Start by trying the name of the source layer. - */ - std::string name = srcLayer->getName(); - if (getLayer(name)) - { - int k=0; - do - { - name = "Layer" + toString(k); - k++; - } while (getLayer(name) || srcMap->getLayer(name)); - } - - destLayer = new Layer(name, mWidth * mHeight); - mLayers.push_back(destLayer); - std::cout<<"Created new layer "<<name<<std::endl; - } - - for (int y=0; y<srcHeight; y++) - { - - for (int x=0; x<srcWidth; x++) - { - int srcIndex = srcMap->getWidth() * (y + srcY) + (x + srcX); - int tgtIndex = mWidth * (y + destY) + (x + destX); - Tile tmpTile = srcLayer->at(srcIndex); - tmpTile.tileset = translation[tmpTile.tileset]; - destLayer->at(tgtIndex) = tmpTile; - } - } - } - - std::clog<<"copying successful!"<<std::endl; - return true; -} - -bool Map::randomFill(Map* templateMap, const std::string& destLayerName, - int destX, int destY, int destWidth, int destHeight, - const ConfigurationOptions& config) -{ - //plausibility check of coordinates - bool checkPassed = true; - if (destX + destWidth > mWidth) - { - std::cerr<<"Error: Area exceeds right map border of target map!"<<std::endl; - checkPassed = false; - } - if (destY + destHeight > mHeight) - { - std::cerr<<"Error: Area exceeds lower map border of target map!"<<std::endl; - checkPassed = false; - } - if (destWidth < templateMap->getWidth()) - { - std::cerr<<"Error: Template is wider than target area"<<std::endl; - checkPassed = false; - } - if (destWidth < templateMap->getHeight()) - { - std::cerr<<"Error: Template is higher than target area"<<std::endl; - checkPassed = false; - } - if (templateMap->getNumberOfLayers() == 0) - { - std::cerr<<"Error: Template has no layers"<<std::endl; - checkPassed = false; - } - if (!config.createMissingLayers && !getLayer(destLayerName)) - { - std::cerr<<"Error: target map has no layer named \""<<destLayerName<<"\""<<std::endl - <<"(and the command-line \"create layers\" option was not used)"<<std::endl; - checkPassed = false; - } - if (!checkPassed) return false; - - std::map<int, int> translation = addAndTranslateTilesets(templateMap); - - - Layer* destLayer = getLayer(destLayerName); - if (!destLayer) - { - destLayer = new Layer(destLayerName, mWidth * mHeight); - mLayers.push_back(destLayer); - std::cout<<"Created new layer "<<destLayerName<<std::endl; - } - - /* Now generate extra tiles. - * - * After considering ways to specify the number of objects to add, I think - * the best user interface (without integrating it with Tiled) is to place - * a small number of objects each time, and have the user run the utility - * several times, reloading the map in Tiled each time until it looks - * right. Simpler than typing magic numbers in at a command prompt. - * - * This algorithm completes after a fixed number of attempts at placing an - * object; regardless of how many attempts are successful. - * For 2x1 trees, destWidth*destHeight/10 is very sparse, dW*dH/2 is dense. - */ - srand(time(NULL)); - int patternsGenerated = 0; - int occupiedAreas = 0; - for (int i = destWidth*destHeight / 10; i > 0; i--) - { - /* Pick a random location, with enough tiles left and down from it to - * fit the template in (the +1 is because it starts on tile (x,y)) - */ - int x = destX + (rand() % (destWidth - templateMap->getWidth () + 1)); - int y = destY + (rand() % (destHeight - templateMap->getHeight() + 1)); - - bool areaIsClear = true; - - for (int loop_y=0; loop_y<templateMap->getHeight(); loop_y++) - { - for (int loop_x=0; loop_x<templateMap->getWidth(); loop_x++) - { - if (! destLayer->getTile(x+loop_x, y+loop_y, mWidth).empty()) - { - areaIsClear = false; - } - } - } - - if (areaIsClear) - { - int p = rand() % templateMap->getNumberOfLayers(); - - Layer* srcLayer = templateMap->getLayer(p); - for (int loop_y=0; loop_y<templateMap->getHeight(); loop_y++) - { - for (int loop_x=0; loop_x<templateMap->getWidth(); loop_x++) - { - Tile& srcTile = srcLayer->getTile(loop_x, loop_y, templateMap->getWidth()); - Tile& destTile = destLayer->getTile(x+loop_x, y+loop_y, mWidth); - destTile.tileset = translation[srcTile.tileset]; - destTile.index = srcTile.index; - } - } - patternsGenerated++; - } - else - { - occupiedAreas++; - } - } - - std::clog<<"Generated " << patternsGenerated << " new objects" <<std::endl; - (void) occupiedAreas; // Unused at the moment, but keep it without a compiler warning about unused variables - return true; -} - -bool Map::translateAllLayers(Map* templateMap, const std::string& destLayerName, - const ConfigurationOptions& config) -{ - bool checkPassed = true; - if (templateMap->getNumberOfLayers() != 2) - { - std::cerr<<"Error: template should have exactly 2 layers"<<std::endl; - checkPassed = false; - } - if (!config.createMissingLayers && !getLayer(destLayerName)) - { - std::cerr<<"Error: target map has no layer named \""<<destLayerName<<"\""<<std::endl - <<"(and the command-line \"create layers\" option was not used)"<<std::endl; - checkPassed = false; - } - if (!checkPassed) return false; - - //TODO This will unnecessarily add tilesets that are in the template but - //not used in the main map - std::map<int, int> tilesetTranslation = addAndTranslateTilesets(templateMap); - - //Lacking a better name, we'll say this is translating visible layers to collision - std::map<Tile, Tile> collisionTranslation; - - Layer* fromLayer = templateMap->getLayer(0); - Layer* toLayer = templateMap->getLayer(1); - for (int xy = (templateMap->getWidth() * templateMap->getHeight() -1); - xy >= 0; xy--) - { - Tile fromTile = fromLayer->at(xy); - Tile toTile = toLayer->at(xy); - if (!fromTile.empty()) - { - fromTile.tileset = tilesetTranslation[fromTile.tileset]; - toTile.tileset = tilesetTranslation[toTile.tileset]; - - collisionTranslation[fromTile] = toTile; - } - } - - /* Now apply that template to the game map */ - Layer* destLayer = getLayer(destLayerName); - if (!destLayer) - { - destLayer = new Layer(destLayerName, mWidth * mHeight); - mLayers.push_back(destLayer); - std::cout<<"Created new layer "<<destLayerName<<std::endl; - } - - for (std::vector<Layer*>::iterator layer = mLayers.begin(); - layer != mLayers.end(); - layer++) - { - if ((*layer)->getName() == destLayerName) - continue; - - for (int xy = mWidth * mHeight -1; xy >= 0; xy--) - { - Tile& fromTile = (*layer)->at(xy); - Tile& toTile = destLayer->at(xy); - std::map<Tile,Tile>::iterator iteratedTile = collisionTranslation.find(fromTile); - - if (iteratedTile != collisionTranslation.end()) - { - toTile = (*iteratedTile).second; - } - } - - } - - return true; -} - - - -int Map::save(std::string filename) -{ - //remove old tileset and layer information in XML tree - xmlNodePtr rootNode = xmlDocGetRootElement(mXmlDoc); - std::list<xmlNodePtr> toRemove; - for_each_xml_child_node(node, rootNode) - { - if ( xmlStrEqual(node->name, BAD_CAST "tileset") - || xmlStrEqual(node->name, BAD_CAST "layer")) - { - toRemove.push_back(node); - } - } - - while (!toRemove.empty()) - { - xmlUnlinkNode(toRemove.back()); - xmlFreeNode(toRemove.back()); - toRemove.pop_back(); - } - - //TODO: reorganize GIDs - - //add new tileset information to XML tree - for (size_t i = 0; i< mTilesets.size(); i++) - { - xmlNodePtr newNode; - - xmlAddChild(rootNode, xmlNewDocText(mXmlDoc, BAD_CAST " ")); - newNode = xmlNewNode(NULL, BAD_CAST "tileset"); - xmlNewProp(newNode, BAD_CAST "name", BAD_CAST mTilesets.at(i)->name.c_str()); - xmlNewProp(newNode, BAD_CAST "firstgid", BAD_CAST toString(mTilesets.at(i)->firstgid).c_str()); - xmlNewProp(newNode, BAD_CAST "tilewidth", BAD_CAST toString(mTilesets.at(i)->tilewidth).c_str()); - xmlNewProp(newNode, BAD_CAST "tileheight", BAD_CAST toString(mTilesets.at(i)->tileheight).c_str()); - xmlAddChild(newNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n ")); - xmlNodePtr imageNode = xmlNewNode(NULL, BAD_CAST "image"); - xmlNewProp(imageNode, BAD_CAST "source", BAD_CAST mTilesets.at(i)->imagefile.c_str()); - xmlAddChild(newNode, imageNode); - xmlAddChild(newNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n ")); - xmlAddChild(rootNode, newNode); - xmlAddChild(rootNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n")); - } - - //add new layer information to XML tree - for (size_t i = 0; i < mLayers.size(); i++) - { - //lay out layer information in binary - unsigned char* binData = (unsigned char*)malloc(mWidth * mHeight * 4); - - for (int a=0; a < mWidth * mHeight; a++) - { - Tile tile = mLayers.at(i)->at(a); - int ldata; - if (tile.tileset != -1) - { - ldata = tile.index + mTilesets.at(tile.tileset)->firstgid; - } else { - ldata = 0; - } - - binData[a * 4 + 0] = (ldata & 0x000000ff); - binData[a * 4 + 1] = (ldata & 0x0000ff00) >> 8; - binData[a * 4 + 2] = (ldata & 0x00ff0000) >> 16; - binData[a * 4 + 3] = (ldata & 0xff000000) >> 24; - } - - - //GZIP layer information - /* - unsigned char* gzipData = (unsigned char*)malloc((mWidth * mHeight * 4) + 128); - unsigned int gzipLen; - compressMemory (binData, (mWidth * mHeight * 4) + 128, gzipData, gzipLen); - free (binData); - std::cout<<"GZIP length: "<<gzipLen<<std::endl; - */ - - //encode layer information in base64 - unsigned char* base64Data; - int base64len; - //base64Data = php3_base64_encode(gzipData, gzipLen, &base64len); - base64Data = php3_base64_encode(binData, (mWidth * mHeight * 4), &base64len); - //free(gzipData); - - xmlNodePtr newNode; - xmlAddChild(rootNode, xmlNewDocText(mXmlDoc, BAD_CAST " ")); - newNode = xmlNewNode(NULL, BAD_CAST "layer"); - xmlNewProp(newNode, BAD_CAST "name", BAD_CAST (mLayers.at(i)->getName()).c_str()); - xmlNewProp(newNode, BAD_CAST "width", BAD_CAST toString(mWidth).c_str()); - xmlNewProp(newNode, BAD_CAST "height", BAD_CAST toString(mHeight).c_str()); - xmlAddChild(newNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n ")); - xmlNodePtr dataNode = xmlNewNode(NULL, BAD_CAST "data"); - xmlNewProp(dataNode, BAD_CAST "encoding", BAD_CAST "base64"); - //xmlNewProp(dataNode, BAD_CAST "compression", BAD_CAST "gzip"); - xmlAddChild(dataNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n ")); - xmlAddChild(dataNode, xmlNewDocText(mXmlDoc, BAD_CAST base64Data)); - xmlAddChild(dataNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n ")); - xmlAddChild(newNode, dataNode); - xmlAddChild(newNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n ")); - xmlAddChild(rootNode, newNode); - xmlAddChild(rootNode, xmlNewDocText(mXmlDoc, BAD_CAST "\n")); - - free(base64Data); - free(binData); - } - - //save XML tree - int retval = xmlSaveFile(filename.c_str(), mXmlDoc); - - if (retval == -1) - { - std::cerr<<"Could not write outfile "<<filename<<std::endl; - return false; - } - else - { - std::cout<<"File saved successfully to "<<filename<<std::endl; - return true; - } -} - -Layer* Map::getLayer(std::string name) -{ - for (std::vector<Layer*>::iterator layer = mLayers.begin(); - layer != mLayers.end(); - layer++) - { - if ((*layer)->getName() == name) - return *layer; - } - return NULL; -} - -Map::~Map() -{ - for (std::vector<Layer*>::iterator layer = mLayers.begin(); - layer != mLayers.end(); - layer++) - { - delete *layer; - } - - for (std::vector<Tileset*>::iterator tileset = mTilesets.begin(); - tileset != mTilesets.end(); - tileset++) - { - delete *tileset; - } - - xmlFreeDoc(mXmlDoc); -} |