Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
/* 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/biff.h"
#include "engines/stark/services/archiveloader.h"
namespace Stark {
namespace Formats {
BiffArchive::BiffArchive(ArchiveReadStream *stream, ObjectBuilder objectBuilder) :
_objectBuilder(objectBuilder) {
read(stream);
}
BiffArchive::~BiffArchive() {
for (uint i = 0; i < _rootObjects.size(); i++) {
delete _rootObjects[i];
}
}
void BiffArchive::read(ArchiveReadStream *stream) {
uint32 id = stream->readUint32BE();
if (id != MKTAG('B', 'I', 'F', 'F')) {
error("Wrong magic while reading biff archive");
}
_version = stream->readUint32LE();
/*uint32 u1 = */stream->readUint32LE();
/*uint32 u2 = */stream->readUint32LE();
uint32 len = stream->readUint32LE();
for (uint32 i = 0; i < len; i++) {
BiffObject *object = readObject(stream, nullptr);
_rootObjects.push_back(object);
}
}
BiffObject *BiffArchive::readObject(ArchiveReadStream *stream, BiffObject *parent) {
uint32 marker = stream->readUint32LE();
if (marker != 0xf0f0f0f0) {
error("Wrong magic while reading biff archive");
}
uint32 type = stream->readUint32LE();
BiffObject *object = _objectBuilder(type);
if (!object) {
error("Unimplemented BIFF object type %x", type);
}
object->_parent = parent;
object->_u3 = stream->readUint32LE();
uint32 size = stream->readUint32LE();
if (_version >= 2) {
object->_version = stream->readUint32LE();
}
object->readData(stream, size);
marker = stream->readUint32LE();
if (marker != 0x0f0f0f0f) {
error("Wrong magic while reading biff archive");
}
uint32 len = stream->readUint32LE();
for (uint32 i = 0; i < len; ++i) {
BiffObject *child = readObject(stream, object);
object->addChild(child);
}
return object;
}
Common::Array<BiffObject *> BiffArchive::listObjects() {
return _rootObjects;
}
BiffObject::BiffObject() :
_u3(0),
_version(0),
_parent(nullptr),
_type(0) {
}
uint32 BiffObject::getType() const {
return _type;
}
void BiffObject::addChild(BiffObject *child) {
_children.push_back(child);
}
BiffObject::~BiffObject() {
// Delete the children objects
Common::Array<BiffObject *>::iterator i = _children.begin();
while (i != _children.end()) {
delete *i;
i++;
}
}
} // End of namespace Formats
} // End of namespace Stark

View File

@@ -0,0 +1,135 @@
/* 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/>.
*
*/
#ifndef STARK_FORMATS_BIFF_H
#define STARK_FORMATS_BIFF_H
#include "common/array.h"
#include "common/scummsys.h"
namespace Stark {
class ArchiveReadStream;
namespace Formats {
class BiffObject;
/**
* A tree-style container for BiffObjects
*
* Users of this class must provide a factory method for the BiffObject subclasses
* contained in the archive. This class can only read the archive's structure
* and not specific object types.
*/
class BiffArchive {
public:
typedef BiffObject *(*ObjectBuilder)(uint32 type);
BiffArchive(ArchiveReadStream *stream, ObjectBuilder objectBuilder);
~BiffArchive();
/** List the objects at the root level of the archive */
Common::Array<BiffObject *> listObjects();
/** List objects recursively matching the template parameter type */
template<class T>
Common::Array<T *> listObjectsRecursive();
private:
void read(ArchiveReadStream *stream);
BiffObject *readObject(ArchiveReadStream *stream, BiffObject *parent);
ObjectBuilder _objectBuilder;
uint32 _version;
Common::Array<BiffObject *> _rootObjects;
};
/**
* An object which can be read from a BiffArchive
*
* Each object has a list of children objects, resulting in a tree structure
*/
class BiffObject {
public:
BiffObject();
virtual ~BiffObject();
/**
* Used to read the object data from the stream
*/
virtual void readData(ArchiveReadStream *stream, uint32 dataLength) = 0;
/** Get the object type */
uint32 getType() const;
/** List children recursively matching the template parameter type */
template<class T>
Common::Array<T *> listChildrenRecursive();
/** Add an object to the children list */
void addChild(BiffObject *child);
protected:
uint32 _type;
uint32 _u3;
uint32 _version;
BiffObject *_parent;
Common::Array<BiffObject *> _children;
friend class BiffArchive;
};
template<class T>
Common::Array<T *> BiffArchive::listObjectsRecursive() {
Common::Array<BiffObject *> objects = listObjects();
Common::Array<T *> array;
for (uint i = 0; i < objects.size(); i++) {
array.push_back(objects[i]->listChildrenRecursive<T>());
}
return array;
}
template<class T>
Common::Array<T *> BiffObject::listChildrenRecursive() {
Common::Array<T *> list;
for (uint i = 0; i < _children.size(); i++) {
if (_children[i]->getType() == T::TYPE) {
// Found a matching child
list.push_back(static_cast<T *>(_children[i]));
}
// Look for matching resources in the child's children
list.push_back(_children[i]->listChildrenRecursive<T>());
}
return list;
}
} // End of namespace Formats
} // End of namespace Stark
#endif // STARK_FORMATS_BIFF_H

View File

@@ -0,0 +1,443 @@
/* 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/biffmesh.h"
#include "engines/stark/formats/biff.h"
#include "engines/stark/services/archiveloader.h"
#include "common/hashmap.h"
namespace Stark {
namespace Formats {
enum BiffMeshObjectType {
kMeshObjectSceneData = 0x5a4aa94,
kMeshObjectBase = 0x5a4aa89,
kMeshObjectTri = 0x5a4aa8d,
kMeshObjectMaterial = 0x5a4aa8e
};
class MeshObjectSceneData : public BiffObject {
public:
static const uint32 TYPE = kMeshObjectSceneData;
MeshObjectSceneData() :
BiffObject(),
_animStart(0),
_animEnd(0) {
_type = TYPE;
}
// BiffObject API
void readData(ArchiveReadStream *stream, uint32 dataLength) override {
_animStart = stream->readUint32LE();
_animEnd = stream->readUint32LE();
}
private:
uint32 _animStart;
uint32 _animEnd;
};
class MeshObjectBase : public BiffObject {
public:
static const uint32 TYPE = kMeshObjectBase;
MeshObjectBase() :
BiffObject() {
_type = TYPE;
}
// BiffObject API
void readData(ArchiveReadStream *stream, uint32 dataLength) override {
_name = stream->readString16();
}
private:
Common::String _name;
};
class MeshObjectTri : public BiffObject {
public:
static const uint32 TYPE = kMeshObjectTri;
MeshObjectTri() :
BiffObject(),
_hasPhysics(false) {
_type = TYPE;
}
struct KeyFrame {
uint32 time;
Math::Quaternion essentialRotation;
float determinant;
Math::Quaternion stretchRotation;
Math::Vector3d scale;
Math::Vector3d translation;
};
struct Vertex {
Common::String animName1;
Common::String animName2;
float animInfluence1;
float animInfluence2;
Math::Vector3d position;
};
struct RawFace {
uint32 vertexIndex[3];
uint32 normalIndex[3];
uint32 textureVertexIndex[3];
uint32 materialId;
uint32 smoothingGroup;
};
// BiffObject API
void readData(ArchiveReadStream *stream, uint32 dataLength) override {
_name = stream->readString16();
uint32 keyFrameCount = stream->readUint32LE();
for (uint i = 0; i < keyFrameCount; i++) {
KeyFrame keyFrame;
keyFrame.time = stream->readUint32LE();
keyFrame.essentialRotation = stream->readQuaternion();
keyFrame.determinant = stream->readFloatLE();
keyFrame.stretchRotation = stream->readQuaternion();
keyFrame.scale = stream->readVector3();
keyFrame.translation = stream->readVector3();
_keyFrames.push_back(keyFrame);
}
if (_version >= 2) {
uint32 uvKeyFrameCount = stream->readUint32LE();
assert(uvKeyFrameCount == 0); // Reading the uv keyframes is not implemented
uint32 attributeCount = stream->readUint32LE();
assert(attributeCount == 0); // Reading the attributes is not implemented
}
uint32 vertexCount = stream->readUint32LE();
for (uint i = 0; i < vertexCount; i++) {
Vertex vertex;
vertex.animName1 = stream->readString16();
vertex.animName2 = stream->readString16();
vertex.animInfluence1 = stream->readFloatLE();
vertex.animInfluence2 = stream->readFloatLE();
vertex.position = stream->readVector3();
_rawVertices.push_back(vertex);
}
uint32 normalCount = stream->readUint32LE();
for (uint i = 0; i < normalCount; i++) {
_rawNormals.push_back(stream->readVector3());
}
uint32 textureVertexCount = stream->readUint32LE();
for (uint i = 0; i < textureVertexCount; i++) {
_rawTexturePositions.push_back(stream->readVector3());
}
uint32 faceCount = stream->readUint32LE();
for (uint i = 0; i < faceCount; i++) {
RawFace face;
face.vertexIndex[0] = stream->readUint32LE();
face.vertexIndex[1] = stream->readUint32LE();
face.vertexIndex[2] = stream->readUint32LE();
face.normalIndex[0] = stream->readUint32LE();
face.normalIndex[1] = stream->readUint32LE();
face.normalIndex[2] = stream->readUint32LE();
face.textureVertexIndex[0] = stream->readUint32LE();
face.textureVertexIndex[1] = stream->readUint32LE();
face.textureVertexIndex[2] = stream->readUint32LE();
face.materialId = stream->readUint32LE();
face.smoothingGroup = stream->readUint32LE();
_rawFaces.push_back(face);
}
_hasPhysics = stream->readByte();
}
void reindex() {
// The raw data loaded from the BIFF archive needs to be split into faces of identical material.
// Reserve enough faces in our faces array
for (uint i = 0; i < _rawFaces.size(); i++) {
if (_rawFaces[i].materialId >= _faces.size()) {
_faces.resize(_rawFaces[i].materialId + 1);
}
_faces[_rawFaces[i].materialId].materialId = _rawFaces[i].materialId;
}
// The raw data loaded from the BIFF archive is multi-indexed, which is not simple to use to draw.
// Here, we reindex the data so that each vertex owns all of its related attributes, hence requiring
// a single index list.
Common::HashMap<VertexKey, uint32, VertexKey::Hash, VertexKey::EqualTo> vertexIndexMap;
for (uint i = 0; i < _rawFaces.size(); i++) {
for (uint j = 0; j < 3; j++) {
VertexKey vertexKey(_rawFaces[i].vertexIndex[j], _rawFaces[i].normalIndex[j], _rawFaces[i].textureVertexIndex[j]);
if (!vertexIndexMap.contains(vertexKey)) {
BiffMesh::Vertex vertex;
vertex.position = _rawVertices[_rawFaces[i].vertexIndex[j]].position;
vertex.normal = _rawNormals[_rawFaces[i].normalIndex[j]];
vertex.texturePosition = _rawTexturePositions[_rawFaces[i].textureVertexIndex[j]];
_vertices.push_back(vertex);
vertexIndexMap.setVal(vertexKey, _vertices.size() - 1);
}
uint32 vertexIndex = vertexIndexMap.getVal(vertexKey);
// Add the index to a face according to its material
_faces[_rawFaces[i].materialId].vertexIndices.push_back(vertexIndex);
}
}
// Clear the raw data
_rawVertices.clear();
_rawNormals.clear();
_rawTexturePositions.clear();
_rawFaces.clear();
}
Math::Matrix4 getTransform(uint keyframeIndex) const {
const KeyFrame &keyframe = _keyFrames[keyframeIndex];
Math::Matrix4 translation;
translation.setPosition(keyframe.translation);
Math::Matrix4 essentialRotation = keyframe.essentialRotation.toMatrix();
Math::Matrix4 determinant;
determinant.setValue(0, 0, keyframe.determinant);
determinant.setValue(1, 1, keyframe.determinant);
determinant.setValue(2, 2, keyframe.determinant);
Math::Matrix4 stretchRotation = keyframe.stretchRotation.toMatrix();
Math::Matrix4 stretchRotationTransposed = stretchRotation;
stretchRotationTransposed.transpose();
Math::Matrix4 scale;
scale.setValue(0, 0, keyframe.scale.x());
scale.setValue(1, 1, keyframe.scale.y());
scale.setValue(2, 2, keyframe.scale.z());
return translation * essentialRotation * determinant * stretchRotationTransposed * scale * stretchRotation;
}
const Common::Array<BiffMesh::Vertex> &getVertices() const {
return _vertices;
}
const Common::Array<Face> &getFaces() const {
return _faces;
}
private:
struct VertexKey {
uint32 _vertexIndex;
uint32 _normalIndex;
uint32 _textureVertexIndex;
VertexKey(uint32 vertexIndex, uint32 normalIndex, uint32 textureVertexIndex) {
_vertexIndex = vertexIndex;
_normalIndex = normalIndex;
_textureVertexIndex = textureVertexIndex;
}
struct Hash {
uint operator() (const VertexKey &x) const {
return x._vertexIndex + x._normalIndex + x._textureVertexIndex;
}
};
struct EqualTo {
bool operator() (const VertexKey &x, const VertexKey &y) const {
return x._vertexIndex == y._vertexIndex &&
x._normalIndex == y._normalIndex &&
x._textureVertexIndex == y._textureVertexIndex;
}
};
};
Common::String _name;
Common::Array<KeyFrame> _keyFrames;
Common::Array<Vertex> _rawVertices;
Common::Array<RawFace> _rawFaces;
Common::Array<Math::Vector3d> _rawNormals;
Common::Array<Math::Vector3d> _rawTexturePositions;
Common::Array<BiffMesh::Vertex> _vertices;
Common::Array<Face> _faces;
bool _hasPhysics;
};
class MeshObjectMaterial : public BiffObject {
public:
static const uint32 TYPE = kMeshObjectMaterial;
MeshObjectMaterial() :
BiffObject(),
_shading(0),
_shininess(0),
_opacity(1),
_doubleSided(false),
_textureTiling(0),
_alphaTiling(0),
_environementTiling(0),
_isColorKey(false),
_colorKey(0) {
_type = TYPE;
}
// BiffObject API
void readData(ArchiveReadStream *stream, uint32 dataLength) override {
_name = stream->readString16();
_texture = stream->readString16();
_alpha = stream->readString16();
_environment = stream->readString16();
_shading = stream->readUint32LE();
_ambiant = stream->readVector3();
_diffuse = stream->readVector3();
_specular = stream->readVector3();
_shininess = stream->readFloatLE();
_opacity = stream->readFloatLE();
_doubleSided = stream->readByte();
_textureTiling = stream->readUint32LE();
_alphaTiling = stream->readUint32LE();
_environementTiling = stream->readUint32LE();
_isColorKey = stream->readByte();
_colorKey = stream->readUint32LE();
uint32 attributeCount = stream->readUint32LE();
assert(attributeCount == 0); // Reading the attributes is not implemented
}
Material toMaterial() const {
Material material;
material.name = _name;
material.texture = _texture;
material.r = _diffuse.x();
material.g = _diffuse.y();
material.b = _diffuse.z();
material.doubleSided = _doubleSided;
return material;
}
private:
Common::String _name;
Common::String _texture;
Common::String _alpha;
Common::String _environment;
uint32 _shading;
Math::Vector3d _ambiant;
Math::Vector3d _diffuse;
Math::Vector3d _specular;
float _shininess;
float _opacity;
bool _doubleSided;
uint32 _textureTiling;
uint32 _alphaTiling;
uint32 _environementTiling;
bool _isColorKey;
uint32 _colorKey;
};
BiffMesh *BiffMeshReader::read(ArchiveReadStream *stream) {
BiffArchive archive = BiffArchive(stream, &biffObjectBuilder);
Common::Array<MeshObjectTri *> tris = archive.listObjectsRecursive<MeshObjectTri>();
Common::Array<MeshObjectMaterial *> materialObjects = archive.listObjectsRecursive<MeshObjectMaterial>();
if (tris.size() != 1) {
error("Unexpected tri count in BIFF archive: '%d'", tris.size());
}
tris[0]->reindex();
Common::Array<Material> materials;
for (uint i = 0; i < materialObjects.size(); i++) {
materials.push_back(materialObjects[i]->toMaterial());
}
BiffMesh *mesh = new BiffMesh(tris[0]->getVertices(), tris[0]->getFaces(), materials);
mesh->setTransform(tris[0]->getTransform(0));
return mesh;
}
BiffObject *BiffMeshReader::biffObjectBuilder(uint32 type) {
switch (type) {
case kMeshObjectSceneData:
return new MeshObjectSceneData();
case kMeshObjectBase:
return new MeshObjectBase();
case kMeshObjectTri:
return new MeshObjectTri();
case kMeshObjectMaterial:
return new MeshObjectMaterial();
default:
return nullptr;
}
}
BiffMesh::BiffMesh(const Common::Array<Vertex> &vertices, const Common::Array<Face> &faces,
const Common::Array<Material> &materials) :
_vertices(vertices),
_faces(faces),
_materials(materials) {
}
const Common::Array<BiffMesh::Vertex> &BiffMesh::getVertices() const {
return _vertices;
}
const Common::Array<Face> &BiffMesh::getFaces() const {
return _faces;
}
const Common::Array<Material> &BiffMesh::getMaterials() const {
return _materials;
}
void BiffMesh::setTransform(const Math::Matrix4 &transform) {
_transform = transform;
}
Math::Matrix4 BiffMesh::getTransform() const {
return _transform;
}
} // End of namespace Formats
} // End of namespace Stark

View File

@@ -0,0 +1,87 @@
/* 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/>.
*
*/
#ifndef STARK_FORMATS_BIFF_MESH_H
#define STARK_FORMATS_BIFF_MESH_H
#include "engines/stark/model/model.h"
#include "common/array.h"
#include "math/matrix4.h"
#include "math/vector3d.h"
namespace Stark {
class ArchiveReadStream;
namespace Formats {
class BiffObject;
class BiffMesh;
/**
* A mesh reader
*
* Reads a mesh out of a BIFF archive
*/
class BiffMeshReader {
public:
/** Read a mesh from a BIFF archive stream */
static BiffMesh *read(ArchiveReadStream *stream);
private:
static BiffObject *biffObjectBuilder(uint32 type);
};
/**
* A mesh read out of a BIFF archive
*/
class BiffMesh {
public:
struct Vertex {
Math::Vector3d position;
Math::Vector3d normal;
Math::Vector3d texturePosition;
};
BiffMesh(const Common::Array<Vertex> &vertices, const Common::Array<Face> &faces, const Common::Array<Material> &materials);
const Common::Array<Vertex> &getVertices() const;
const Common::Array<Face> &getFaces() const;
const Common::Array<Material> &getMaterials() const;
Math::Matrix4 getTransform() const;
void setTransform(const Math::Matrix4 &transform);
private:
Common::Array<Vertex> _vertices;
Common::Array<Face> _faces;
Common::Array<Material> _materials;
Math::Matrix4 _transform;
};
} // End of namespace Formats
} // End of namespace Stark
#endif // STARK_FORMATS_BIFF_MESH_H

View File

@@ -0,0 +1,190 @@
/* 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)) {
_format = Graphics::PixelFormat::createFormatBGRA32();
return true;
} else if (!(format.flags & kPixelFlagsHasAlpha) &&
(format.bitCount == 24) &&
(format.rBitMask == 0x00FF0000) && (format.gBitMask == 0x0000FF00) &&
(format.bBitMask == 0x000000FF)) {
_format = Graphics::PixelFormat::createFormatBGR24();
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

100
engines/stark/formats/dds.h Normal file
View File

@@ -0,0 +1,100 @@
/* 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/>.
*
*/
#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

View File

@@ -0,0 +1,154 @@
/* 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/iss.h"
#include "audio/decoders/adpcm_intern.h"
#include "audio/decoders/raw.h"
#include "common/substream.h"
namespace Stark {
namespace Formats {
/**
* ADPCM decoder for the .iss files
*/
class ISSADPCMStream : public Audio::Ima_ADPCMStream {
public:
ISSADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
: Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {}
protected:
int readBuffer(int16 *buffer, const int numSamples) override {
// Similar to MS IMA, but without the four-bytes-per-channel requirement
int samples;
assert(numSamples % 2 == 0);
for (samples = 0; samples < numSamples && !endOfData(); samples += 2) {
if (_blockPos[0] == _blockAlign) {
// read block header
for (byte i = 0; i < _channels; i++) {
_status.ima_ch[i].last = _stream->readSint16LE();
_status.ima_ch[i].stepIndex = _stream->readSint16LE();
}
_blockPos[0] = 4 * _channels;
}
byte data = _stream->readByte();
buffer[samples + (isStereo() ? 1 : 0)] = decodeIMA(data & 0x0f, isStereo() ? 1 : 0);
buffer[samples + (isStereo() ? 0 : 1)] = decodeIMA((data >> 4) & 0x0f);
_blockPos[0]++;
}
return samples;
}
};
static void skipString(Common::SeekableReadStream *stream) {
// Skip until the next space. Note that this will read past \0
// characters as well. That's not a bug.
byte ch;
while ((ch = stream->readByte()) != 0x20)
;
}
static Common::String readString(Common::SeekableReadStream *stream) {
Common::String ret = "";
byte ch;
while ((ch = stream->readByte()) != 0x20)
ret += ch;
return ret;
}
Audio::RewindableAudioStream *makeISSStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
Common::String codec;
uint16 blockSize, channels, freq = 44100;
uint32 size;
byte flags;
codec = readString(stream);
if (codec.equals("IMA_ADPCM_Sound")) {
codec = readString(stream);
blockSize = (uint16)strtol(codec.c_str(), 0, 10);
skipString(stream);
// name ?
skipString(stream);
// ?
codec = readString(stream);
channels = (uint16)strtol(codec.c_str(), 0, 10) + 1;
skipString(stream);
// ?
codec = readString(stream);
int val = strtol(codec.c_str(), 0, 10);
if (val)
freq /= val;
skipString(stream);
skipString(stream);
codec = readString(stream);
size = (uint32)strtol(codec.c_str(), 0, 10);
return new ISSADPCMStream(stream, DisposeAfterUse::YES, size, freq, channels, blockSize);
} else if (codec.equals("Sound")) {
skipString(stream);
// name ?
codec = readString(stream);
// sample count ?
codec = readString(stream);
channels = (uint16)strtol(codec.c_str(), 0, 10) + 1;
skipString(stream);
// ?
codec = readString(stream);
int val = strtol(codec.c_str(), 0, 10);
if (val)
freq /= val;
skipString(stream);
skipString(stream);
flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
if (channels == 2)
flags |= Audio::FLAG_STEREO;
return Audio::makeRawStream(new Common::SeekableSubReadStream(stream, stream->pos(), stream->size(), DisposeAfterUse::YES), freq, flags, DisposeAfterUse::YES);
} else {
error("Unknown ISS codec '%s'", codec.c_str());
}
}
} // End of namespace Formats
} // End of namespace Stark

View File

@@ -0,0 +1,45 @@
/* 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/>.
*
*/
#ifndef STARK_ISS_H
#define STARK_ISS_H
#include "common/stream.h"
#include "audio/audiostream.h"
namespace Stark {
namespace Formats {
/**
* Create a new RewindableAudioStream from the ISS data in the given stream.
* ISS is the file format used by the 4 CD version of the game.
*
* @param stream the SeekableReadStream from which to read the ISS data
* @param disposeAfterUse whether to delete the stream after use
* @return a new RewindableAudioStream, or NULL, if an error occurred
*/
Audio::RewindableAudioStream *makeISSStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
} // End of namespace Formats
} // End of namespace Stark
#endif // STARK_ISS_H

View File

@@ -0,0 +1,155 @@
/* 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/tm.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/texture.h"
#include "engines/stark/formats/biff.h"
#include "engines/stark/services/archiveloader.h"
#include "engines/stark/services/services.h"
namespace Stark {
namespace Formats {
class TextureGroup : public BiffObject {
public:
static const uint32 TYPE = kTextureSetGroup;
TextureGroup() :
BiffObject(),
_palette(nullptr) {
_type = TYPE;
}
~TextureGroup() override {
delete[] _palette;
}
const byte *getPalette() {
return _palette;
}
// BiffObject API
void readData(ArchiveReadStream *stream, uint32 dataLength) override {
int entries = stream->readUint32LE();
_palette = new byte[entries * 3];
byte *ptr = _palette;
for (int i = 0; i < entries; ++i) {
*ptr++ = (byte) stream->readUint16LE();
*ptr++ = (byte) stream->readUint16LE();
*ptr++ = (byte) stream->readUint16LE();
}
}
private:
byte *_palette;
};
Texture::Texture() :
BiffObject(),
_texture(nullptr),
_u(0) {
_type = TYPE;
}
Texture::~Texture() {
_surface.free();
delete _texture;
}
void Texture::readData(ArchiveReadStream *stream, uint32 dataLength) {
TextureGroup *textureGroup = static_cast<TextureGroup *>(_parent);
_name = stream->readString16();
_u = stream->readByte();
uint32 w = stream->readUint32LE();
uint32 h = stream->readUint32LE();
uint32 levels = stream->readUint32LE();
_texture = StarkGfx->createTexture();
_texture->setLevelCount(levels);
for (uint32 i = 0; i < levels; ++i) {
// Read the pixel data to a surface
Graphics::Surface level;
Graphics::Surface *surface = i == 0 ? &_surface : &level;
surface->create(w, h, Graphics::PixelFormat::createFormatCLUT8());
stream->read(surface->getPixels(), surface->w * surface->h);
// Add the mipmap level to the texture
_texture->addLevel(i, surface, textureGroup->getPalette());
level.free();
w /= 2;
h /= 2;
}
}
Gfx::Texture *Texture::acquireTexturePointer() {
Gfx::Texture *texture = _texture;
_texture = nullptr;
return texture;
}
Graphics::Surface *Texture::getSurface() const {
TextureGroup *textureGroup = static_cast<TextureGroup *>(_parent);
return _surface.convertTo(Gfx::Driver::getRGBAPixelFormat(), textureGroup->getPalette());
}
Gfx::TextureSet *TextureSetReader::read(ArchiveReadStream *stream) {
BiffArchive archive = BiffArchive(stream, &biffObjectBuilder);
Common::Array<Texture *> textures = archive.listObjectsRecursive<Texture>();
Gfx::TextureSet *textureSet = new Gfx::TextureSet();
for (uint i = 0; i < textures.size(); i++) {
textureSet->addTexture(textures[i]->getName(), textures[i]->acquireTexturePointer());
}
return textureSet;
}
BiffArchive *TextureSetReader::readArchive(ArchiveReadStream *stream) {
return new BiffArchive(stream, &biffObjectBuilder);
}
BiffObject *TextureSetReader::biffObjectBuilder(uint32 type) {
switch (type) {
case kTextureSetGroup:
return new TextureGroup();
case kTextureSetTexture:
return new Texture();
default:
return nullptr;
}
}
} // End of namespace Formats
} // End of namespace Stark

107
engines/stark/formats/tm.h Normal file
View File

@@ -0,0 +1,107 @@
/* 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/>.
*
*/
#ifndef STARK_FORMATS_TM_H
#define STARK_FORMATS_TM_H
#include "engines/stark/formats/biff.h"
#include "common/str.h"
#include "graphics/surface.h"
namespace Stark {
class ArchiveReadStream;
namespace Gfx {
class TextureSet;
class Texture;
}
namespace Formats {
/**
* A texture set loader able to read '.tm' files
*/
class TextureSetReader {
public:
/**
* Load a texture set from the provided stream.
*
* The caller is responsible for freeing the texture set.
*/
static Gfx::TextureSet *read(ArchiveReadStream *stream);
/** Read the texture set archive from the provided stream */
static BiffArchive *readArchive(ArchiveReadStream *stream);
private:
static BiffObject *biffObjectBuilder(uint32 type);
};
enum TextureSetType {
kTextureSetGroup = 0x02faf082,
kTextureSetTexture = 0x02faf080
};
/**
* A texture contained in a '.tm' texture set archive
*
* Textures have mipmaps.
*/
class Texture : public BiffObject {
public:
static const uint32 TYPE = kTextureSetTexture;
Texture();
~Texture() override;
Common::String getName() const {
return _name;
}
/**
* Return a pointer to a texture ready for rendering
*
* The caller takes ownership of the texture.
* This method can only be called successfully once
* per texture. Subsequent calls return a null pointer.
*/
Gfx::Texture *acquireTexturePointer();
/** Return a RGBA copy of the pixel data */
Graphics::Surface *getSurface() const;
// BiffObject API
void readData(ArchiveReadStream *stream, uint32 dataLength) override;
private:
Common::String _name;
Gfx::Texture *_texture;
Graphics::Surface _surface;
byte _u;
};
} // End of namespace Formats
} // End of namespace Stark
#endif // STARK_FORMATS_TM_H

View File

@@ -0,0 +1,229 @@
/* 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/>.
*
*/
// Based on the Xentax Wiki documentation:
// https://web.archive.org/web/20090621212912/http://wiki.xentax.com/index.php?title=The_Longest_Journey_XARC
#include "engines/stark/formats/xarc.h"
#include "engines/stark/debug.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/substream.h"
namespace Stark {
namespace Formats {
// ARCHIVE MEMBER
class XARCMember : public Common::ArchiveMember {
public:
XARCMember(const XARCArchive *xarc, Common::ReadStream &stream, uint32 offset);
Common::SeekableReadStream *createReadStream() const override;
Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override;
Common::String getName() const override { return _name.baseName(); }
Common::Path getPathInArchive() const override { return _name; }
Common::String getFileName() const override { return _name.baseName(); }
uint32 getLength() const { return _length; }
uint32 getOffset() const { return _offset; }
private:
const XARCArchive *_xarc;
Common::Path _name;
uint32 _offset;
uint32 _length;
// Helper function
Common::String readString(Common::ReadStream &stream);
};
XARCMember::XARCMember(const XARCArchive *xarc, Common::ReadStream &stream, uint32 offset) {
_xarc = xarc;
// Read the information about this archive member
_name = Common::Path(readString(stream));
_offset = offset;
_length = stream.readUint32LE();
debugC(20, kDebugArchive, "Stark::XARC Member: \"%s\" starts at offset=%d and has length=%d", _name.toString().c_str(), _offset, _length);
// Unknown value. English: 0, others: 1
uint32 unknown = stream.readUint32LE();
debugC(kDebugUnknown, "Stark::XARC Member: \"%s\" has unknown=%d", _name.toString().c_str(), unknown);
if (unknown != 0 && unknown != 1) {
warning("Stark::XARC Member: \"%s\" has unknown=%d with unknown meaning", _name.toString().c_str(), unknown);
}
}
Common::SeekableReadStream *XARCMember::createReadStream() const {
return _xarc->createReadStreamForMember(this);
}
Common::SeekableReadStream *XARCMember::createReadStreamForAltStream(Common::AltStreamType altStreamType) const {
return nullptr;
}
Common::String XARCMember::readString(Common::ReadStream &stream) {
Common::String str;
// Read until we find a 0
char c = 1;
while (c != 0) {
c = stream.readByte();
if (stream.eos()) {
c = 0;
}
if (c != 0) {
str += c;
}
}
return str;
}
// ARCHIVE
bool XARCArchive::open(const Common::Path &filename) {
Common::File stream;
if (!stream.open(filename)) {
return false;
}
_filename = filename;
// Unknown: always 1? version?
uint32 unknown = stream.readUint32LE();
debugC(kDebugUnknown, "Stark::XARC: \"%s\" has unknown=%d", _filename.toString(Common::Path::kNativeSeparator).c_str(), unknown);
if (unknown != 1) {
warning("Stark::XARC: \"%s\" has unknown=%d with unknown meaning", _filename.toString(Common::Path::kNativeSeparator).c_str(), unknown);
}
// Read the number of contained files
uint32 numFiles = stream.readUint32LE();
debugC(20, kDebugArchive, "Stark::XARC: \"%s\" contains %d files", _filename.toString(Common::Path::kNativeSeparator).c_str(), numFiles);
// Read the offset to the contents of the first file
uint32 offset = stream.readUint32LE();
debugC(20, kDebugArchive, "Stark::XARC: \"%s\"'s first file has offset=%d", _filename.toString(Common::Path::kNativeSeparator).c_str(), offset);
for (uint32 i = 0; i < numFiles; i++) {
XARCMember *member = new XARCMember(this, stream, offset);
_members.push_back(Common::ArchiveMemberPtr(member));
// Set the offset to the next member
offset += member->getLength();
}
return true;
}
Common::Path XARCArchive::getFilename() const {
return _filename;
}
bool XARCArchive::hasFile(const Common::Path &path) const {
Common::String name = path.toString();
for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) {
if ((*it)->getName() == name) {
// Found it
return true;
}
}
// Not found
return false;
}
int XARCArchive::listMatchingMembers(Common::ArchiveMemberList &list, const Common::Path &pattern, bool matchPathComponents) const {
Common::String patternString = pattern.toString();
int matches = 0;
for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) {
if ((*it)->getName().matchString(patternString)) {
// This file matches, add it
list.push_back(*it);
matches++;
}
}
return matches;
}
int XARCArchive::listMembers(Common::ArchiveMemberList &list) const {
int files = 0;
for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) {
// Add all the members to the list
list.push_back(*it);
files++;
}
return files;
}
const Common::ArchiveMemberPtr XARCArchive::getMember(const Common::Path &path) const {
Common::String name = path.toString();
for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) {
if ((*it)->getName() == name) {
// Found it
return *it;
}
}
// Not found, return an empty ptr
return Common::ArchiveMemberPtr();
}
Common::SeekableReadStream *XARCArchive::createReadStreamForMember(const Common::Path &path) const {
Common::String name = path.toString();
for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) {
if ((*it)->getName() == name) {
// Found it
return createReadStreamForMember((const XARCMember *)it->get());
}
}
// Not found
return 0;
}
Common::SeekableReadStream *XARCArchive::createReadStreamForMember(const XARCMember *member) const {
// Open the xarc file
Common::File *f = new Common::File;
if (!f)
return NULL;
if (!f->open(_filename)) {
delete f;
return NULL;
}
// Return the substream that contains the archive member
uint32 offset = member->getOffset();
uint32 length = member->getLength();
return new Common::SeekableSubReadStream(f, offset, offset + length, DisposeAfterUse::YES);
// Different approach: keep the archive open and read full resources to memory
//f.seek(member->getOffset());
//return f.readStream(member->getLength());
}
} // End of namespace Formats
} // End of namespace Stark

View File

@@ -0,0 +1,55 @@
/* 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/>.
*
*/
#ifndef STARK_ARCHIVE_H
#define STARK_ARCHIVE_H
#include "common/archive.h"
#include "common/stream.h"
namespace Stark {
namespace Formats {
class XARCMember;
class XARCArchive : public Common::Archive {
public:
bool open(const Common::Path &filename);
Common::Path getFilename() const;
// Archive API
bool hasFile(const Common::Path &path) const;
int listMatchingMembers(Common::ArchiveMemberList &list, const Common::Path &pattern, bool matchPathComponents = false) const;
int listMembers(Common::ArchiveMemberList &list) const;
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const;
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const;
Common::SeekableReadStream *createReadStreamForMember(const XARCMember *member) const;
private:
Common::Path _filename;
Common::ArchiveMemberList _members;
};
} // End of namespace Formats
} // End of namespace Stark
#endif // STARK_ARCHIVE_H

View File

@@ -0,0 +1,248 @@
/* 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/xmg.h"
#include "engines/stark/debug.h"
#include "engines/stark/gfx/driver.h"
#include "graphics/conversion.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "common/stream.h"
#include "common/textconsole.h"
namespace Stark {
namespace Formats {
XMGDecoder::XMGDecoder(Common::ReadStream *stream) :
_width(0),
_height(0),
_currX(0),
_currY(0),
_stream(stream),
_transColor(0) {
}
Graphics::Surface *XMGDecoder::decode(Common::ReadStream *stream) {
XMGDecoder dec(stream);
dec.readHeader();
return dec.decodeImage();
}
void XMGDecoder::readSize(Common::ReadStream *stream, uint &width, uint &height) {
XMGDecoder dec(stream);
dec.readHeader();
width = dec._width;
height = dec._height;
}
void XMGDecoder::readHeader() {
// Read the file version
uint32 version = _stream->readUint32LE();
if (version != 3) {
error("Stark::XMG: File version unknown: %d", version);
}
// Read the transparency color (RGBA)
_transColor = _stream->readUint32LE();
// Read the image size
_width = _stream->readUint32LE();
_height = _stream->readUint32LE();
debugC(10, kDebugXMG, "Stark::XMG: Version=%d, TransparencyColor=0x%08x, size=%dx%d", version, _transColor, _width, _height);
// Read the scan length
uint32 scanLen = _stream->readUint32LE();
if (scanLen != 3 * _width) {
error("Stark::XMG: The scan length (%d) doesn't match the width bytes (%d)", scanLen, 3 * _width);
}
// Unknown
uint32 unknown2 = _stream->readUint32LE();
debugC(kDebugUnknown, "Stark::XMG: unknown2 = %08x = %d", unknown2, unknown2);
uint32 unknown3 = _stream->readUint32LE();
debugC(kDebugUnknown, "Stark::XMG: unknown3 = %08x = %d", unknown3, unknown3);
}
Graphics::Surface *XMGDecoder::decodeImage() {
// Create the destination surface
Graphics::Surface *surface = new Graphics::Surface();
surface->create(_width, _height, Gfx::Driver::getRGBAPixelFormat());
_currX = 0, _currY = 0;
while (!_stream->eos()) {
if (_currX >= _width) {
assert(_currX == _width);
_currX = 0;
_currY += 2;
if (_currY >= _height)
break;
}
// Read the number and mode of the tiles
byte op = _stream->readByte();
uint16 count;
if ((op & 0xC0) != 0xC0) {
count = op & 0x3F;
} else {
count = ((op & 0xF) << 8) + _stream->readByte();
op <<= 2;
}
op &= 0xC0;
// Process the current serie
for (int i = 0; i < count; i++) {
Block block = decodeBlock(op);
drawBlock(block, surface);
}
}
return surface;
}
XMGDecoder::Block XMGDecoder::decodeBlock(byte op) {
Block block;
switch (op) {
case 0x00:
// YCrCb
block = processYCrCb();
break;
case 0x40:
// Trans
block = processTrans();
break;
case 0x80:
// RGB
block = processRGB();
break;
default:
error("Unsupported color mode '%d'", op);
}
return block;
}
void XMGDecoder::drawBlock(const Block &block, Graphics::Surface *surface) {
uint32 *pixels = (uint32 *)surface->getBasePtr(_currX, _currY);
bool drawTwoColumns = _currX + 1 < _width;
bool drawTwoLines = _currY + 1 < _height;
pixels[0] = TO_LE_32(block.a1);
if (drawTwoColumns) {
pixels[1] = TO_LE_32(block.a2);
}
if (drawTwoLines) {
pixels[_width + 0] = TO_LE_32(block.b1);
}
if (drawTwoColumns && drawTwoLines) {
pixels[_width + 1] = TO_LE_32(block.b2);
}
_currX += drawTwoColumns ? 2 : 1;
}
XMGDecoder::Block XMGDecoder::processYCrCb() {
byte y0, y1, y2, y3;
byte cr, cb;
y0 = _stream->readByte();
y1 = _stream->readByte();
y2 = _stream->readByte();
y3 = _stream->readByte();
cr = _stream->readByte();
cb = _stream->readByte();
byte r, g, b;
Block block;
Graphics::YUV2RGB(y0, cb, cr, r, g, b);
block.a1 = (255u << 24) + (b << 16) + (g << 8) + r;
Graphics::YUV2RGB(y1, cb, cr, r, g, b);
block.a2 = (255u << 24) + (b << 16) + (g << 8) + r;
Graphics::YUV2RGB(y2, cb, cr, r, g, b);
block.b1 = (255u << 24) + (b << 16) + (g << 8) + r;
Graphics::YUV2RGB(y3, cb, cr, r, g, b);
block.b2 = (255u << 24) + (b << 16) + (g << 8) + r;
return block;
}
XMGDecoder::Block XMGDecoder::processTrans() {
Block block;
block.a1 = 0;
block.a2 = 0;
block.b1 = 0;
block.b2 = 0;
return block;
}
XMGDecoder::Block XMGDecoder::processRGB() {
Block block;
uint32 color;
color = _stream->readUint16LE();
color += _stream->readByte() << 16;
if (color != _transColor)
color += 255u << 24;
else
color = 0;
block.a1 = color;
color = _stream->readUint16LE();
color += _stream->readByte() << 16;
if (color != _transColor)
color += 255u << 24;
else
color = 0;
block.a2 = color;
color = _stream->readUint16LE();
color += _stream->readByte() << 16;
if (color != _transColor)
color += 255u << 24;
else
color = 0;
block.b1 = color;
color = _stream->readUint16LE();
color += _stream->readByte() << 16;
if (color != _transColor)
color += 255u << 24;
else
color = 0;
block.b2 = color;
return block;
}
} // End of namespace Formats
} // End of namespace Stark

View File

@@ -0,0 +1,78 @@
/* 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/>.
*
*/
#ifndef STARK_XMG_H
#define STARK_XMG_H
#include "common/stream.h"
namespace Graphics {
struct Surface;
}
namespace Stark {
namespace Formats {
/**
* XMG (still image) decoder
*/
class XMGDecoder {
public:
static Graphics::Surface *decode(Common::ReadStream *stream);
static void readSize(Common::ReadStream *stream, uint &width, uint &height);
private:
explicit XMGDecoder(Common::ReadStream *stream);
struct Block {
uint32 a1, a2;
uint32 b1, b2;
};
void readHeader();
Graphics::Surface *decodeImage();
Block decodeBlock(byte op);
void drawBlock(const Block &block, Graphics::Surface *surface);
Block processYCrCb();
Block processTrans();
Block processRGB();
uint32 _width;
uint32 _height;
uint32 _currX;
uint32 _currY;
Common::ReadStream *_stream;
/**
* The transparency color in the RGB and transparency blocks.
* In the output surface, the transparent color is black with zero
* alpha. So the images are effectively pre-multiplied alpha.
*/
uint32 _transColor;
};
} // End of namespace Formats
} // End of namespace Stark
#endif // STARK_XMG_H

View File

@@ -0,0 +1,333 @@
/* 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/xrc.h"
#include "engines/stark/formats/xarc.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/animhierarchy.h"
#include "engines/stark/resources/animscript.h"
#include "engines/stark/resources/animsoundtrigger.h"
#include "engines/stark/resources/bonesmesh.h"
#include "engines/stark/resources/bookmark.h"
#include "engines/stark/resources/camera.h"
#include "engines/stark/resources/container.h"
#include "engines/stark/resources/command.h"
#include "engines/stark/resources/dialog.h"
#include "engines/stark/resources/direction.h"
#include "engines/stark/resources/fmv.h"
#include "engines/stark/resources/image.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/floor.h"
#include "engines/stark/resources/floorface.h"
#include "engines/stark/resources/floorfield.h"
#include "engines/stark/resources/knowledge.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/layer.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/light.h"
#include "engines/stark/resources/lipsync.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/path.h"
#include "engines/stark/resources/pattable.h"
#include "engines/stark/resources/root.h"
#include "engines/stark/resources/script.h"
#include "engines/stark/resources/scroll.h"
#include "engines/stark/resources/speech.h"
#include "engines/stark/resources/sound.h"
#include "engines/stark/resources/string.h"
#include "engines/stark/resources/textureset.h"
#include "engines/stark/resourcereference.h"
namespace Stark {
namespace Formats {
XRCReadStream::XRCReadStream(const Common::Path &archiveName,
Common::SeekableReadStream *parentStream, DisposeAfterUse::Flag disposeParentStream) :
SeekableSubReadStream(parentStream, 0, parentStream->size(), disposeParentStream),
_archiveName(archiveName) {
}
XRCReadStream::~XRCReadStream() {
}
Common::String XRCReadStream::readString() {
// Read the string length
uint16 length = readUint16LE();
// Read the string
char *data = new char[length];
read(data, length);
Common::String string(data, length);
delete[] data;
return string;
}
Resources::Type XRCReadStream::readResourceType() {
byte rawType;
rawType = readByte();
return Resources::Type((Resources::Type::ResourceType) (rawType));
}
ResourceReference XRCReadStream::readResourceReference() {
ResourceReference reference;
reference.loadFromStream(this);
return reference;
}
Math::Vector3d XRCReadStream::readVector3() {
Math::Vector3d v;
v.readFromStream(this);
return v;
}
Common::Rect XRCReadStream::readRect() {
Common::Rect r;
r.left = readSint32LE();
r.top = readSint32LE();
r.right = readSint32LE();
r.bottom = readSint32LE();
return r;
}
Common::Point XRCReadStream::readPoint() {
uint32 x = readUint32LE();
uint32 y = readUint32LE();
return Common::Point(x, y);
}
bool XRCReadStream::readBool() {
uint32 b = readUint32LE();
return b != 0;
}
bool XRCReadStream::isDataLeft() {
return pos() < size();
}
Common::Path XRCReadStream::getArchiveName() const {
return _archiveName;
}
Resources::Object *XRCReader::importTree(XARCArchive *archive) {
// Find the XRC file
Common::ArchiveMemberList members;
archive->listMatchingMembers(members, "*.xrc");
if (members.size() == 0) {
error("No resource tree in archive '%s'", archive->getFilename().toString(Common::Path::kNativeSeparator).c_str());
}
if (members.size() > 1) {
error("Too many resource scripts in archive '%s'", archive->getFilename().toString(Common::Path::kNativeSeparator).c_str());
}
// Open the XRC file
Common::SeekableReadStream *stream = archive->createReadStreamForMember(members.front()->getPathInArchive());
XRCReadStream *xrcStream = new XRCReadStream(archive->getFilename(), stream);
// Import the resource tree
Resources::Object *root = importResource(xrcStream, nullptr);
delete xrcStream;
return root;
}
Resources::Object *XRCReader::importResource(XRCReadStream *stream, Resources::Object *parent) {
Resources::Object *resource = createResource(stream, parent);
importResourceData(stream, resource);
importResourceChildren(stream, resource);
// Resource lifecycle update
resource->onPostRead();
return resource;
}
Resources::Object *XRCReader::createResource(XRCReadStream *stream, Resources::Object *parent) {
// Read the resource type and subtype
Resources::Type type = stream->readResourceType();
byte subType = stream->readByte();
// Read the resource properties
uint16 index = stream->readUint16LE();
Common::String name = stream->readString();
// Create a new resource
Resources::Object *resource;
switch (type.get()) {
case Resources::Type::kRoot:
resource = new Resources::Root(parent, subType, index, name);
break;
case Resources::Type::kLevel:
resource = new Resources::Level(parent, subType, index, name);
break;
case Resources::Type::kLocation:
resource = new Resources::Location(parent, subType, index, name);
break;
case Resources::Type::kLayer:
resource = Resources::Layer::construct(parent, subType, index, name);
break;
case Resources::Type::kCamera:
resource = new Resources::Camera(parent, subType, index, name);
break;
case Resources::Type::kFloor:
resource = new Resources::Floor(parent, subType, index, name);
break;
case Resources::Type::kFloorFace:
resource = new Resources::FloorFace(parent, subType, index, name);
break;
case Resources::Type::kItem:
resource = Resources::Item::construct(parent, subType, index, name);
break;
case Resources::Type::kScript:
resource = new Resources::Script(parent, subType, index, name);
break;
case Resources::Type::kAnimHierarchy:
resource = new Resources::AnimHierarchy(parent, subType, index, name);
break;
case Resources::Type::kAnim:
resource = Resources::Anim::construct(parent, subType, index, name);
break;
case Resources::Type::kDirection:
resource = new Resources::Direction(parent, subType, index, name);
break;
case Resources::Type::kImage:
resource = Resources::Image::construct(parent, subType, index, name);
break;
case Resources::Type::kAnimScript:
resource = new Resources::AnimScript(parent, subType, index, name);
break;
case Resources::Type::kAnimScriptItem:
resource = new Resources::AnimScriptItem(parent, subType, index, name);
break;
case Resources::Type::kSoundItem:
resource = new Resources::Sound(parent, subType, index, name);
break;
case Resources::Type::kPath:
resource = Resources::Path::construct(parent, subType, index, name);
break;
case Resources::Type::kFloorField:
resource = new Resources::FloorField(parent, subType, index, name);
break;
case Resources::Type::kBookmark:
resource = new Resources::Bookmark(parent, subType, index, name);
break;
case Resources::Type::kKnowledgeSet:
resource = new Resources::KnowledgeSet(parent, subType, index, name);
break;
case Resources::Type::kKnowledge:
resource = new Resources::Knowledge(parent, subType, index, name);
break;
case Resources::Type::kCommand:
resource = new Resources::Command(parent, subType, index, name);
break;
case Resources::Type::kPATTable:
resource = new Resources::PATTable(parent, subType, index, name);
break;
case Resources::Type::kContainer:
resource = new Resources::Container(parent, subType, index, name);
break;
case Resources::Type::kDialog:
resource = new Resources::Dialog(parent, subType, index, name);
break;
case Resources::Type::kSpeech:
resource = new Resources::Speech(parent, subType, index, name);
break;
case Resources::Type::kLight:
resource = new Resources::Light(parent, subType, index, name);
break;
case Resources::Type::kBonesMesh:
resource = new Resources::BonesMesh(parent, subType, index, name);
break;
case Resources::Type::kScroll:
resource = new Resources::Scroll(parent, subType, index, name);
break;
case Resources::Type::kFMV:
resource = new Resources::FMV(parent, subType, index, name);
break;
case Resources::Type::kLipSync:
resource = new Resources::LipSync(parent, subType, index, name);
break;
case Resources::Type::kAnimSoundTrigger:
resource = new Resources::AnimSoundTrigger(parent, subType, index, name);
break;
case Resources::Type::kString:
resource = new Resources::String(parent, subType, index, name);
break;
case Resources::Type::kTextureSet:
resource = new Resources::TextureSet(parent, subType, index, name);
break;
default:
resource = new Resources::UnimplementedResource(parent, type, subType, index, name);
break;
}
return resource;
}
void XRCReader::importResourceData(XRCReadStream *stream, Resources::Object *resource) {
// Read the data length
uint32 dataLength = stream->readUint32LE();
// Read the resource type specific data using a memory stream
if (dataLength > 0) {
XRCReadStream *xrcDataStream = new XRCReadStream(stream->getArchiveName(), stream->readStream(dataLength));
resource->readData(xrcDataStream);
if (xrcDataStream->isDataLeft()) {
warning("Not all XRC data was read. Type %s, subtype %d, name %s",
resource->getType().getName(), resource->getSubType(), resource->getName().c_str());
}
if (xrcDataStream->eos()) {
warning("Too much XRC data was read. Type %s, subtype %d, name %s",
resource->getType().getName(), resource->getSubType(), resource->getName().c_str());
}
delete xrcDataStream;
}
}
void XRCReader::importResourceChildren(XRCReadStream *stream, Resources::Object *resource) {
// Get the number of children
uint16 numChildren = stream->readUint16LE();
// Read more unknown data
uint16 unknown3 = stream->readUint16LE();
if (unknown3 != 0) {
warning("Stark::XRCReader: \"%s\" has unknown3=0x%04X with unknown meaning", resource->getName().c_str(), unknown3);
}
// Read the children resources
for (int i = 0; i < numChildren; i++) {
Resources::Object *child = importResource(stream, resource);
// Add child to parent
resource->addChild(child);
}
}
} // End of namespace Formats
} // End of namespace Stark

View File

@@ -0,0 +1,86 @@
/* 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/>.
*
*/
#ifndef STARK_XRC_READER_H
#define STARK_XRC_READER_H
#include "common/array.h"
#include "common/rect.h"
#include "common/str.h"
#include "common/substream.h"
#include "common/types.h"
#include "math/vector3d.h"
#include "math/vector4d.h"
#include "engines/stark/resources/object.h"
#include "engines/stark/resourcereference.h"
namespace Stark {
namespace Formats {
class XARCArchive;
/**
* A read stream with helper functions to read usual XRC data types
*/
class XRCReadStream : public Common::SeekableSubReadStream {
public:
XRCReadStream(const Common::Path &archiveName, Common::SeekableReadStream *parentStream, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::YES);
virtual ~XRCReadStream();
/** Obtain the file name of the archive containing the XRC tree */
Common::Path getArchiveName() const;
Common::String readString();
Resources::Type readResourceType();
ResourceReference readResourceReference();
Math::Vector3d readVector3();
Common::Rect readRect();
Common::Point readPoint();
bool readBool();
bool isDataLeft();
private:
Common::Path _archiveName;
};
/**
* An XRC stream parser, used to build resource trees.
*/
class XRCReader {
public:
/**
* Build a resource tree from a stream
*/
static Resources::Object *importTree(XARCArchive *archive);
protected:
static Resources::Object *importResource(XRCReadStream *stream, Resources::Object *parent);
static Resources::Object *createResource(XRCReadStream *stream, Resources::Object *parent);
static void importResourceChildren(XRCReadStream *stream, Resources::Object *resource);
static void importResourceData(XRCReadStream* stream, Resources::Object* resource);
};
} // End of namespace Formats
} // End of namespace Stark
#endif // STARK_XRC_READER_H