/* * Copyright 2010-2016 OpenXcom Developers. * * This file is part of OpenXcom. * * OpenXcom 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 3 of the License, or * (at your option) any later version. * * OpenXcom 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 OpenXcom. If not, see . */ #include "ExtraSprites.h" #include "../Engine/Surface.h" #include "../Engine/SurfaceSet.h" #include "../Engine/FileMap.h" #include "../Engine/Logger.h" #include "../Engine/Exception.h" #include "Mod.h" namespace OpenXcom { /** * Creates a blank set of extra sprite data. */ ExtraSprites::ExtraSprites() : _current(0), _width(320), _height(200), _singleImage(false), _subX(0), _subY(0), _loaded(false) { } /** * Cleans up the extra sprite set. */ ExtraSprites::~ExtraSprites() { } /** * Loads the extra sprite set from YAML. * @param node YAML node. * @param modIndex the internal index of the associated mod. */ void ExtraSprites::load(const YAML::Node &node, const ModData* current) { _type = node["type"].as(_type); _sprites = node["files"].as< std::map >(_sprites); _width = node["width"].as(_width); _height = node["height"].as(_height); _singleImage = node["singleImage"].as(_singleImage); _subX = node["subX"].as(_subX); _subY = node["subY"].as(_subY); _current = current; } /** * Gets the filename that this sprite represents. * @return The sprite name. */ std::string ExtraSprites::getType() const { return _type; } /** * Gets the list of sprites defined my this mod. * @return The list of sprites. */ std::map *ExtraSprites::getSprites() { return &_sprites; } /** * Gets the width of the surfaces (used for single images and new spritesets). * @return The width of the surfaces. */ int ExtraSprites::getWidth() const { return _width; } /** * Gets the height of the surfaces (used for single images and new spritesets). * @return The height of the surfaces. */ int ExtraSprites::getHeight() const { return _height; } /** * Returns whether this is a single surface as opposed to a set of surfaces. * @return True if this is a single surface. */ bool ExtraSprites::getSingleImage() const { return _singleImage; } /** * Gets the x subdivision. * @return The x subdivision. */ int ExtraSprites::getSubX() const { return _subX; } /** * Gets the y subdivision. * @return The y subdivision. */ int ExtraSprites::getSubY() const { return _subY; } /** * Returns if the sprite is loaded. * @return True/false */ bool ExtraSprites::isLoaded() const { return _loaded; } /** * Determines if an image file is an acceptable format for the game. * @param filename Image filename. * @return True/false */ bool ExtraSprites::isImageFile(const std::string &filename) { static const std::string exts[] = { "PNG", "GIF", "BMP", "LBM", "IFF", "PCX", "TGA", "TIF", "TIFF" }; for (size_t i = 0; i < sizeof(exts) / sizeof(exts[0]); ++i) { if (CrossPlatform::compareExt(filename, exts[i])) return true; } return false; } /** * Loads the external sprite into a new or existing surface. * @param surface Existing surface. * @return New surface. */ Surface *ExtraSprites::loadSurface(Surface *surface) { if (!_singleImage) return surface; _loaded = true; if (surface == 0) { Log(LOG_VERBOSE) << "Creating new single image: " << _type; } else { Log(LOG_VERBOSE) << "Adding/Replacing single image: " << _type; delete surface; } surface = new Surface(_width, _height); surface->loadImage(FileMap::getFilePath(_sprites.begin()->second)); return surface; } /** * Loads the external sprite into a new or existing surface set. * @param set Existing surface set. * @return New surface set. */ SurfaceSet *ExtraSprites::loadSurfaceSet(SurfaceSet *set) { if (_singleImage) return set; _loaded = true; bool subdivision = (_subX != 0 && _subY != 0); int surfaceSetX = subdivision ? _subX : _width; int surfaceSetY = subdivision ? _subY : _height; if (set == 0) { Log(LOG_VERBOSE) << "Creating new surface set: " << _type; set = new SurfaceSet(surfaceSetX, surfaceSetY); } else { Log(LOG_VERBOSE) << "Adding/Replacing items in surface set: " << _type; if (set->getTotalFrames() == 0 && (set->getWidth() != surfaceSetX || set->getHeight() != surfaceSetY)) { Log(LOG_VERBOSE) << "Resize empty set to: " << surfaceSetX << " x " << surfaceSetY; int shared = set->getMaxSharedFrames(); delete set; set = new SurfaceSet(surfaceSetX, surfaceSetY); set->setMaxSharedFrames(shared); } } for (std::map::const_iterator j = _sprites.begin(); j != _sprites.end(); ++j) { int startFrame = j->first; std::string fileName = j->second; if (fileName[fileName.length() - 1] == '/') { Log(LOG_VERBOSE) << "Loading surface set from folder: " << fileName << " starting at frame: " << startFrame; int offset = startFrame; const std::set &contents = FileMap::getVFolderContents(fileName); for (std::set::iterator k = contents.begin(); k != contents.end(); ++k) { if (!isImageFile(*k)) continue; try { const std::string &fullPath = FileMap::getFilePath(fileName + *k); getFrame(set, offset)->loadImage(fullPath); offset++; } catch (Exception &e) { Log(LOG_WARNING) << e.what(); } } } else { const std::string &fullPath = FileMap::getFilePath(fileName); if (!subdivision) { getFrame(set, startFrame)->loadImage(fullPath); } else { Surface temp = Surface(_width, _height); temp.loadImage(fullPath); int xDivision = _width / _subX; int yDivision = _height / _subY; int frames = xDivision * yDivision; Log(LOG_VERBOSE) << "Subdividing into " << frames << " frames."; int offset = startFrame; for (int y = 0; y != yDivision; ++y) { for (int x = 0; x != xDivision; ++x) { Surface* frame = getFrame(set, offset); // for some reason regular blit() doesn't work here how i want it, so i use this function instead. temp.blitNShade(frame, 0 - (x * _subX), 0 - (y * _subY), 0); ++offset; } } } } } return set; } Surface *ExtraSprites::getFrame(SurfaceSet *set, int index) const { int indexWithOffset = index; if (indexWithOffset >= set->getMaxSharedFrames()) { if ((size_t)indexWithOffset >= _current->size) { std::ostringstream err; err << "ExtraSprites '" << _type << "' frame '" << indexWithOffset << "' exceeds mod '"<< _current->name <<"' size limit " << _current->size; throw Exception(err.str()); } indexWithOffset += _current->offset; } else if (indexWithOffset < 0) { std::ostringstream err; err << "ExtraSprites '" << _type << "' frame '" << indexWithOffset << "' in mod '" << _current->name << "' is not allowed."; throw Exception(err.str()); } Surface *frame = set->getFrame(indexWithOffset); if (frame) { Log(LOG_VERBOSE) << "Replacing frame: " << index << ", using index: " << indexWithOffset; frame->clear(); } else { Log(LOG_VERBOSE) << "Adding frame: " << index << ", using index: " << indexWithOffset; frame = set->addFrame(indexWithOffset); } return frame; } }