/*
* 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;
}
}