mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-21 19:51:49 +00:00
STARK: Add support for loading modded textures
This commit is contained in:
parent
db33fb3e41
commit
c95f711a8e
14
README.md
14
README.md
@ -511,6 +511,20 @@ mods for the game. These are the currently supported modding features:
|
||||
for instance `mods/[my_mod]/08/02/xarc/011001.bik` is the animation where
|
||||
the tree spirit lifts the egg back into the nest.
|
||||
|
||||
* Load replacement textures for the 3d models.
|
||||
Each original `tm` file contains several textures, each with its
|
||||
associated mipmaps. The replacement files are `zip` archives containing
|
||||
`dds` packaged textures. The replacement archives must be placed at the root
|
||||
of the mod directory and be named after the `tm` file they replace:
|
||||
`mods/[my_mod]/april_waitress.tm.zip`.
|
||||
Each `zip` archive must contain all the textures from the replaced `tm`
|
||||
file. The textures need to be encoded in uncompressed RGB or RGBA `dds`
|
||||
files with mipmaps. Files inside the archive must be named according
|
||||
to the replaced texture name, but with the `bmp` extension replaced with
|
||||
`dds`: `backdress-highres-battic.dds`
|
||||
The `extractAllTextures` console command can be used to extract the `tm`
|
||||
files to `png` files.
|
||||
|
||||
Contact us if you need further capabilities for your mod.
|
||||
|
||||
## 9. Bug reports
|
||||
|
199
engines/stark/formats/dds.cpp
Normal file
199
engines/stark/formats/dds.cpp
Normal file
@ -0,0 +1,199 @@
|
||||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#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
|
101
engines/stark/formats/dds.h
Normal file
101
engines/stark/formats/dds.h
Normal file
@ -0,0 +1,101 @@
|
||||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the AUTHORS
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STARK_FORMATS_DDS_H
|
||||
#define STARK_FORMATS_DDS_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/stream.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Stark {
|
||||
namespace Formats {
|
||||
|
||||
// Based on xoreos' DDS code
|
||||
|
||||
/**
|
||||
* DDS texture
|
||||
*
|
||||
* Only a very small subset of DDS features are supported. Especially,
|
||||
* compressed formats are not supported. This class is meant to
|
||||
* load a single DDS file per instance.
|
||||
*/
|
||||
class DDS {
|
||||
public:
|
||||
~DDS();
|
||||
|
||||
typedef Common::Array<Graphics::Surface> MipMaps;
|
||||
|
||||
/** Load a DDS texture from a stream */
|
||||
bool load(Common::SeekableReadStream &dds, const Common::String &name);
|
||||
|
||||
/**
|
||||
* Retrieve the mip map levels for a loaded texture
|
||||
*
|
||||
* The first mipmap is the full size image. Each further
|
||||
* mipmap divides by two the with and the height of the
|
||||
* previous one.
|
||||
*/
|
||||
const MipMaps &getMipMaps() const;
|
||||
|
||||
private:
|
||||
/** The specific pixel format of the included image data. */
|
||||
struct DDSPixelFormat {
|
||||
/** The size of the image data in bytes */
|
||||
uint32 size;
|
||||
|
||||
/** Features of the image data */
|
||||
uint32 flags;
|
||||
|
||||
/** The FourCC to detect the format by */
|
||||
uint32 fourCC;
|
||||
|
||||
/** Number of bits per pixel */
|
||||
uint32 bitCount;
|
||||
|
||||
/** Bit mask for the red color component */
|
||||
uint32 rBitMask;
|
||||
|
||||
/** Bit mask for the green color component */
|
||||
uint32 gBitMask;
|
||||
|
||||
/** Bit mask for the blue color component */
|
||||
uint32 bBitMask;
|
||||
|
||||
/** Bit mask for the alpha component */
|
||||
uint32 aBitMask;
|
||||
};
|
||||
|
||||
bool readHeader(Common::SeekableReadStream &dds);
|
||||
bool readData(Common::SeekableReadStream &dds);
|
||||
|
||||
bool detectFormat(const DDSPixelFormat &format);
|
||||
|
||||
MipMaps _mipmaps;
|
||||
Graphics::PixelFormat _format;
|
||||
Common::String _name;
|
||||
};
|
||||
|
||||
} // End of namespace Formats
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_FORMATS_DDS_H
|
@ -96,7 +96,7 @@ public:
|
||||
const Texture *getTexture(const Common::String &name) const;
|
||||
|
||||
private:
|
||||
typedef Common::HashMap<Common::String, Texture *> TextureMap;
|
||||
typedef Common::HashMap<Common::String, Texture *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> TextureMap;
|
||||
|
||||
TextureMap _texMap;
|
||||
};
|
||||
|
@ -16,6 +16,7 @@ MODULE_OBJS := \
|
||||
gfx/texture.o \
|
||||
formats/biff.o \
|
||||
formats/biffmesh.o \
|
||||
formats/dds.o \
|
||||
formats/iss.o \
|
||||
formats/tm.o \
|
||||
formats/xarc.o \
|
||||
|
@ -22,13 +22,21 @@
|
||||
|
||||
#include "engines/stark/resources/textureset.h"
|
||||
|
||||
#include "engines/stark/debug.h"
|
||||
|
||||
#include "engines/stark/formats/dds.h"
|
||||
#include "engines/stark/formats/tm.h"
|
||||
#include "engines/stark/formats/xrc.h"
|
||||
|
||||
#include "engines/stark/gfx/driver.h"
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
#include "engines/stark/services/services.h"
|
||||
#include "engines/stark/services/settings.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/unzip.h"
|
||||
#include "image/png.h"
|
||||
|
||||
namespace Stark {
|
||||
@ -54,11 +62,17 @@ void TextureSet::readData(Formats::XRCReadStream *stream) {
|
||||
}
|
||||
|
||||
void TextureSet::onPostRead() {
|
||||
ArchiveReadStream *stream = StarkArchiveLoader->getFile(_filename, _archiveName);
|
||||
if (StarkSettings->isAssetsModEnabled()) {
|
||||
_textureSet = readOverrideDdsArchive();
|
||||
}
|
||||
|
||||
_textureSet = Formats::TextureSetReader::read(stream);
|
||||
if (!_textureSet) {
|
||||
ArchiveReadStream *stream = StarkArchiveLoader->getFile(_filename, _archiveName);
|
||||
|
||||
delete stream;
|
||||
_textureSet = Formats::TextureSetReader::read(stream);
|
||||
|
||||
delete stream;
|
||||
}
|
||||
}
|
||||
|
||||
static Common::String stripExtension(const Common::String &filename) {
|
||||
@ -103,6 +117,73 @@ void TextureSet::extractArchive() {
|
||||
delete stream;
|
||||
}
|
||||
|
||||
Gfx::TextureSet *TextureSet::readOverrideDdsArchive() {
|
||||
Common::String archiveName = _filename + ".zip";
|
||||
|
||||
debugC(kDebugModding, "Attempting to load %s", archiveName.c_str());
|
||||
|
||||
Common::Archive *archive = Common::makeZipArchive(archiveName);
|
||||
if (!archive) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::ArchiveMemberList files;
|
||||
archive->listMatchingMembers(files, "*.dds");
|
||||
if (files.empty()) {
|
||||
warning("No DDS files found in archive %s", archiveName.c_str());
|
||||
delete archive;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint loadedCount = 0;
|
||||
Gfx::TextureSet *textureSet = new Gfx::TextureSet();
|
||||
|
||||
for (Common::ArchiveMemberList::const_iterator it = files.begin(); it != files.end(); it++) {
|
||||
const Common::String &name = (*it)->getName();
|
||||
|
||||
Common::SeekableReadStream *ddsStream = (*it)->createReadStream();
|
||||
if (!ddsStream) {
|
||||
warning("Unable to open %s for reading in %s", (*it)->getName().c_str(), archiveName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
Formats::DDS dds;
|
||||
if (!dds.load(*ddsStream, name + " in " + archiveName)) {
|
||||
delete ddsStream;
|
||||
continue;
|
||||
}
|
||||
|
||||
const Formats::DDS::MipMaps &mipmaps = dds.getMipMaps();
|
||||
if (mipmaps.empty()) {
|
||||
warning("No mipmaps in %s", name.c_str());
|
||||
delete ddsStream;
|
||||
continue;
|
||||
}
|
||||
|
||||
Gfx::Texture *texture = StarkGfx->createTexture();
|
||||
texture->setLevelCount(mipmaps.size());
|
||||
for (uint i = 0; i < mipmaps.size(); i++) {
|
||||
texture->addLevel(i, &mipmaps[i]);
|
||||
}
|
||||
|
||||
// Remove the .dds extension, add .bmp to match the names
|
||||
// used by the models.
|
||||
Common::String textureName = Common::String(name.c_str(), name.size() - 4);
|
||||
|
||||
textureSet->addTexture(textureName + ".bmp", texture);
|
||||
|
||||
delete ddsStream;
|
||||
|
||||
loadedCount++;
|
||||
}
|
||||
|
||||
debugC(kDebugModding, "Loaded %d textures from %s", loadedCount, archiveName.c_str());
|
||||
|
||||
delete archive;
|
||||
|
||||
return textureSet;
|
||||
}
|
||||
|
||||
void TextureSet::printData() {
|
||||
debug("filename: %s", _filename.c_str());
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ public:
|
||||
};
|
||||
|
||||
TextureSet(Object *parent, byte subType, uint16 index, const Common::String &name);
|
||||
virtual ~TextureSet();
|
||||
~TextureSet() override;
|
||||
|
||||
// Resource API
|
||||
void readData(Formats::XRCReadStream *stream) override;
|
||||
@ -68,6 +68,7 @@ public:
|
||||
|
||||
protected:
|
||||
void printData() override;
|
||||
Gfx::TextureSet *readOverrideDdsArchive();
|
||||
|
||||
Common::String _filename;
|
||||
Common::String _archiveName;
|
||||
|
Loading…
x
Reference in New Issue
Block a user