scummvm/engines/stark/formats/dds.cpp
2021-12-26 21:19:38 +01:00

198 lines
5.6 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 of the License, or
* (at your option) 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 "engines/stark/formats/dds.h"
#include "common/textconsole.h"
namespace Stark {
namespace Formats {
// Based on xoreos' DDS code
static const uint32 kDDSID = MKTAG('D', 'D', 'S', ' ');
static const uint32 kHeaderFlagsHasMipMaps = 0x00020000;
static const uint32 kPixelFlagsHasAlpha = 0x00000001;
static const uint32 kPixelFlagsHasFourCC = 0x00000004;
static const uint32 kPixelFlagsIsIndexed = 0x00000020;
static const uint32 kPixelFlagsIsRGB = 0x00000040;
DDS::~DDS() {
for (uint i = 0; i < _mipmaps.size(); i++) {
_mipmaps[i].free();
}
}
bool DDS::load(Common::SeekableReadStream &dds, const Common::String &name) {
assert(_mipmaps.empty());
_name = name;
if (!readHeader(dds)) {
return false;
}
return readData(dds);
}
const DDS::MipMaps &DDS::getMipMaps() const {
return _mipmaps;
}
bool DDS::readHeader(Common::SeekableReadStream &dds) {
// We found the FourCC of a standard DDS
uint32 magic = dds.readUint32BE();
if (magic != kDDSID) {
warning("Invalid DDS magic number: %d for %s", magic, _name.c_str());
return false;
}
// All DDS header should be 124 bytes (+ 4 for the FourCC)
uint32 headerSize = dds.readUint32LE();
if (headerSize != 124) {
warning("Invalid DDS header size: %d for %s", headerSize, _name.c_str());
return false;
}
// DDS features
uint32 flags = dds.readUint32LE();
// Image dimensions
uint32 height = dds.readUint32LE();
uint32 width = dds.readUint32LE();
if ((width >= 0x8000) || (height >= 0x8000)) {
warning("Unsupported DDS image dimensions (%ux%u) for %s", width, height, _name.c_str());
return false;
}
dds.skip(4 + 4); // Pitch + Depth
//uint32 pitchOrLineSize = dds.readUint32LE();
//uint32 depth = dds.readUint32LE();
uint32 mipMapCount = dds.readUint32LE();
// DDS doesn't provide any mip maps, only one full-size image
if ((flags & kHeaderFlagsHasMipMaps) == 0) {
mipMapCount = 1;
}
dds.skip(44); // Reserved
// Read the pixel data format
DDSPixelFormat format;
format.size = dds.readUint32LE();
format.flags = dds.readUint32LE();
format.fourCC = dds.readUint32BE();
format.bitCount = dds.readUint32LE();
format.rBitMask = dds.readUint32LE();
format.gBitMask = dds.readUint32LE();
format.bBitMask = dds.readUint32LE();
format.aBitMask = dds.readUint32LE();
// Detect which specific format it describes
if (!detectFormat(format)) {
return false;
}
dds.skip(16 + 4); // DDCAPS2 + Reserved
_mipmaps.resize(mipMapCount);
for (uint32 i = 0; i < mipMapCount; i++) {
_mipmaps[i].create(width, height, _format);
width >>= 1;
height >>= 1;
}
return true;
}
bool DDS::readData(Common::SeekableReadStream &dds) {
for (uint i = 0; i < _mipmaps.size(); i++) {
Graphics::Surface &mipmap = _mipmaps[i];
uint32 size = mipmap.pitch * mipmap.h;
uint32 readSize = dds.read(mipmap.getPixels(), size);
if (readSize != size) {
warning("Inconsistent read size in DDS file: %d, expected %d for %s level %d",
readSize, size, _name.c_str(), i);
return false;
}
}
return true;
}
bool DDS::detectFormat(const DDSPixelFormat &format) {
if (format.flags & kPixelFlagsHasFourCC) {
warning("Unsupported DDS feature: FourCC pixel format %d for %s", format.fourCC, _name.c_str());
return false;
}
if (format.flags & kPixelFlagsIsIndexed) {
warning("Unsupported DDS feature: Indexed %d-bits pixel format for %s", format.bitCount, _name.c_str());
return false;
}
if (!(format.flags & kPixelFlagsIsRGB)) {
warning("Only RGB DDS files are supported for %s", _name.c_str());
return false;
}
if (format.bitCount != 24 && format.bitCount != 32) {
warning("Only 24-bits and 32-bits DDS files are supported for %s", _name.c_str());
return false;
}
if ((format.flags & kPixelFlagsHasAlpha) &&
(format.bitCount == 32) &&
(format.rBitMask == 0x00FF0000) && (format.gBitMask == 0x0000FF00) &&
(format.bBitMask == 0x000000FF) && (format.aBitMask == 0xFF000000)) {
#ifdef SCUMM_BIG_ENDIAN
_format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 0, 8, 16);
#else
_format = Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24);
#endif
return true;
} else if (!(format.flags & kPixelFlagsHasAlpha) &&
(format.bitCount == 24) &&
(format.rBitMask == 0x00FF0000) && (format.gBitMask == 0x0000FF00) &&
(format.bBitMask == 0x000000FF)) {
#ifdef SCUMM_BIG_ENDIAN
_format = Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0);
#else
_format = Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0);
#endif
return true;
} else {
warning("Unsupported pixel format (%X, %X, %d, %X, %X, %X, %X) for %s",
format.flags, format.fourCC, format.bitCount,
format.rBitMask, format.gBitMask, format.bBitMask, format.aBitMask,
_name.c_str());
return false;
}
}
} // End of namespace Formats
} // End of namespace Stark