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,94 @@
/* 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 "twine/parser/anim.h"
#include "common/memstream.h"
namespace TwinE {
void AnimData::loadBoneFrame(KeyFrame &keyframe, Common::SeekableReadStream &stream) {
BoneFrame boneframe;
boneframe.type = (BoneType)stream.readSint16LE();
boneframe.x = stream.readSint16LE();
boneframe.y = stream.readSint16LE();
boneframe.z = stream.readSint16LE();
keyframe.boneframes.push_back(boneframe);
}
void AnimData::loadKeyFrames(Common::SeekableReadStream &stream) {
for (uint16 i = 0U; i < _numKeyframes; ++i) {
KeyFrame keyframe;
keyframe.length = stream.readUint16LE();
keyframe.x = stream.readSint16LE();
keyframe.y = stream.readSint16LE();
keyframe.z = stream.readSint16LE();
keyframe.animMasterRot = stream.readSint16LE();
keyframe.animStepAlpha = stream.readSint16LE();
keyframe.animStepBeta = stream.readSint16LE();
keyframe.animStepGamma = stream.readSint16LE();
stream.seek(-8, SEEK_CUR);
for (uint16 j = 0U; j < _numBoneframes; ++j) {
loadBoneFrame(keyframe, stream);
}
_keyframes.push_back(keyframe);
assert(keyframe.boneframes.size() == (uint)_numBoneframes);
}
}
void AnimData::reset() {
_keyframes.clear();
}
bool AnimData::loadFromStream(Common::SeekableReadStream &stream, bool lba1) {
reset();
_numKeyframes = stream.readUint16LE();
_numBoneframes = stream.readUint16LE();
_loopFrame = stream.readUint16LE();
stream.readUint16LE();
loadKeyFrames(stream);
return !stream.err();
}
const Common::Array<KeyFrame>& AnimData::getKeyframes() const {
return _keyframes;
}
const KeyFrame* AnimData::getKeyframe(uint index) const {
if (index >= _numKeyframes) {
return nullptr;
}
return &_keyframes[index];
}
uint16 AnimData::getLoopFrame() const { // GetBouclageAnim
return _loopFrame;
}
uint16 AnimData::getNumBoneframes() const {
return _numBoneframes;
}
} // namespace TwinE

View File

@@ -0,0 +1,88 @@
/* 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 TWINE_PARSER_ANIM_H
#define TWINE_PARSER_ANIM_H
#include "common/array.h"
#include "common/stream.h"
#include "twine/parser/parser.h"
#include "twine/shared.h"
namespace TwinE {
enum BoneType : uint16 {
TYPE_ROTATE = 0,
TYPE_TRANSLATE = 1,
TYPE_ZOOM = 2,
};
struct BoneFrame { // T_GROUP_INFO
BoneType type = BoneType::TYPE_ROTATE;
int16 x = 0; // alpha
int16 y = 0; // beta
int16 z = 0; // gamma
};
using T_GROUP_INFO = BoneFrame; // (lba2)
struct KeyFrame {
uint16 length = 0;
int16 x = 0;
int16 y = 0;
int16 z = 0;
int16 animMasterRot = 0;
int16 animStepAlpha = 0;
int16 animStepBeta = 0;
int16 animStepGamma = 0;
Common::Array<BoneFrame> boneframes;
};
class AnimData : public Parser {
private:
Common::Array<KeyFrame> _keyframes;
void loadBoneFrame(KeyFrame &keyframe, Common::SeekableReadStream &stream);
void loadKeyFrames(Common::SeekableReadStream &stream);
uint16 _numKeyframes;
uint16 _numBoneframes;
uint16 _loopFrame;
protected:
void reset() override;
public:
bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
const KeyFrame *getKeyframe(uint index) const;
const Common::Array<KeyFrame> &getKeyframes() const;
uint getNbFramesAnim() const;
uint16 getLoopFrame() const;
uint16 getNumBoneframes() const;
};
inline uint AnimData::getNbFramesAnim() const {
return getKeyframes().size();
}
} // End of namespace TwinE
#endif

View File

@@ -0,0 +1,42 @@
/* 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 "twine/parser/anim3ds.h"
#include "common/debug.h"
namespace TwinE {
bool Anim3DSData::loadFromStream(Common::SeekableReadStream &stream, bool lba1) {
assert(!lba1);
const int n = (int)stream.size() / 8;
debug("preload %i anim3ds entries", n);
for (int i = 0; i < n; ++i) {
T_ANIM_3DS anim;
stream.read(anim.Name, sizeof(anim.Name));
anim.Deb = stream.readSint16LE();
anim.Fin = stream.readSint16LE();
_anims.push_back(anim);
}
return !stream.err();
}
} // namespace TwinE

View File

@@ -0,0 +1,48 @@
/* 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 TWINE_PARSER_ANIM3DS_H
#define TWINE_PARSER_ANIM3DS_H
#include "twine/parser/parser.h"
#include "twine/shared.h"
namespace TwinE {
struct T_ANIM_3DS {
char Name[4]; // Name of the animation
int16 Deb; // Start frame in the HQR
int16 Fin; // End frame in the HQR
};
class Anim3DSData : public Parser {
private:
Common::Array<T_ANIM_3DS> _anims; // ListAnim3DS
public:
bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
const Common::Array<T_ANIM_3DS> &getAnims() const { return _anims; }
};
} // End of namespace TwinE
#endif

View File

@@ -0,0 +1,74 @@
/* 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 "twine/parser/blocklibrary.h"
#include "common/stream.h"
namespace TwinE {
void BlockLibraryData::reset() {
_layouts.clear();
}
bool BlockLibraryData::loadFromStream(Common::SeekableReadStream &stream, bool lba1) {
reset();
const uint32 numLayouts = stream.readUint32LE() / 4;
_layouts.resize(numLayouts);
stream.seek(0);
for (uint32 i = 0; i < numLayouts; ++i) {
BlockData &blockData = _layouts[i];
const uint32 offset = stream.readUint32LE();
const uint32 nextOffset = stream.pos();
if (!stream.seek(offset)) {
return false;
}
if (!parseLayout(blockData, stream, lba1)) {
return false;
}
stream.seek(nextOffset);
}
return !stream.err();
}
bool BlockLibraryData::parseLayout(BlockData &blockData, Common::SeekableReadStream &stream, bool lba1) {
const uint8 x = stream.readByte();
const uint8 y = stream.readByte();
const uint8 z = stream.readByte();
const int32 numBricks = x * y * z;
blockData.entries.resize(numBricks);
for (int32 i = 0; i < numBricks; ++i) {
BlockDataEntry &blockEntry = blockData.entries[i];
blockEntry.brickShape = stream.readByte();
blockEntry.brickType = stream.readByte();
blockEntry.brickIdx = stream.readUint16LE();
blockEntry.sound = bits(blockEntry.brickType, 0, 4);
}
return !stream.err();
}
const BlockData *BlockLibraryData::getLayout(int index) const {
if (index < 0 || index >= (int)_layouts.size()) {
error("Block library index out of range: %i", index);
}
return &_layouts[index];
}
} // End of namespace TwinE

View File

@@ -0,0 +1,59 @@
/* 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 TWINE_PARSER_BLOCKLIBRARY_H
#define TWINE_PARSER_BLOCKLIBRARY_H
#include "common/array.h"
#include "common/stream.h"
#include "twine/parser/parser.h"
#include "twine/shared.h"
namespace TwinE {
struct BlockDataEntry {
uint8 brickShape;
uint8 brickType;
/**
* Index is not starting at 0 - but at 1. A 0 indicates an empty brick
*/
uint16 brickIdx;
uint8 sound;
};
struct BlockData {
Common::Array<BlockDataEntry> entries;
};
class BlockLibraryData : public Parser {
private:
Common::Array<BlockData> _layouts;
bool parseLayout(BlockData &blockData, Common::SeekableReadStream &stream, bool lba1);
protected:
void reset() override;
public:
bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
const BlockData *getLayout(int index) const;
};
} // End of namespace TwinE
#endif

View File

@@ -0,0 +1,241 @@
/* 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 "twine/parser/body.h"
#include "twine/renderer/renderer.h"
#include "common/memstream.h"
#define INFO_TRI 1
#define INFO_ANIM 2
namespace TwinE {
void BodyData::reset() {
_vertices.clear();
_bones.clear();
_normals.clear();
_polygons.clear();
_spheres.clear();
_lines.clear();
}
void BodyData::loadVertices(Common::SeekableReadStream &stream) {
const uint16 numVertices = stream.readUint16LE();
if (stream.eos())
return;
_vertices.reserve(numVertices);
for (uint16 i = 0U; i < numVertices; ++i) {
const int16 x = stream.readSint16LE();
const int16 y = stream.readSint16LE();
const int16 z = stream.readSint16LE();
const uint16 bone = 0;
_vertices.push_back({x, y, z, bone});
}
}
void BodyData::loadBones(Common::SeekableReadStream &stream) {
const uint16 numBones = stream.readUint16LE();
if (stream.eos())
return;
_bones.reserve(numBones);
for (uint16 i = 0; i < numBones; ++i) {
const int16 firstPoint = stream.readSint16LE() / 6;
const int16 numPoints = stream.readSint16LE();
const int16 basePoint = stream.readSint16LE() / 6;
const int16 baseElementOffset = stream.readSint16LE();
BoneFrame boneframe;
boneframe.type = (BoneType)stream.readSint16LE();
boneframe.x = stream.readSint16LE();
boneframe.y = stream.readSint16LE();
boneframe.z = stream.readSint16LE();
/*int16 unk1 =*/ stream.readSint16LE();
const int16 numNormals = stream.readSint16LE();
/*int16 unk2 =*/ stream.readSint16LE();
/*int32 field_18 =*/ stream.readSint32LE();
/*int32 y =*/ stream.readSint32LE();
/*int32 field_20 =*/ stream.readSint32LE();
/*int32 field_24 =*/ stream.readSint32LE();
// PatchObjet in original sources
BodyBone bone;
bone.parent = baseElementOffset == -1 ? 0xffff : baseElementOffset / 38;
bone.vertex = basePoint;
bone.firstVertex = firstPoint;
bone.numVertices = numPoints;
bone.initalBoneState = boneframe;
bone.numNormals = numNormals;
// assign the bone index to the vertices
for (int j = 0; j < numPoints; ++j) {
_vertices[firstPoint + j].bone = i;
}
_bones.push_back(bone);
_boneStates[i] = bone.initalBoneState;
}
}
void BodyData::loadNormals(Common::SeekableReadStream &stream) {
const uint16 numNormals = stream.readUint16LE();
if (stream.eos())
return;
_normals.reserve(numNormals);
for (uint16 i = 0; i < numNormals; ++i) {
BodyNormal shape;
shape.x = stream.readSint16LE();
shape.y = stream.readSint16LE();
shape.z = stream.readSint16LE();
shape.prenormalizedRange = stream.readUint16LE();
_normals.push_back(shape);
}
}
void BodyData::loadPolygons(Common::SeekableReadStream &stream) {
const uint16 numPolygons = stream.readUint16LE();
if (stream.eos())
return;
_polygons.reserve(numPolygons);
for (uint16 i = 0; i < numPolygons; ++i) {
BodyPolygon poly;
poly.materialType = stream.readByte();
const uint8 numVertices = stream.readByte();
poly.intensity = stream.readSint16LE();
int16 normal = -1;
if (poly.materialType == MAT_FLAT || poly.materialType == MAT_GRANIT) {
// only one shade value is used
normal = stream.readSint16LE();
}
poly.indices.reserve(numVertices);
poly.normals.reserve(numVertices);
for (int k = 0; k < numVertices; ++k) {
if (poly.materialType >= MAT_GOURAUD) {
normal = stream.readSint16LE();
}
// numPoint is point index precomupted * 6
const uint16 vertexIndex = stream.readUint16LE() / 6;
poly.indices.push_back(vertexIndex);
poly.normals.push_back(normal);
}
_polygons.push_back(poly);
}
}
void BodyData::loadLines(Common::SeekableReadStream &stream) {
const uint16 numLines = stream.readUint16LE();
if (stream.eos())
return;
_lines.reserve(numLines);
for (uint16 i = 0; i < numLines; ++i) {
BodyLine line;
stream.skip(1);
line.color = stream.readByte();
stream.skip(2);
// indexPoint is point index precomupted * 6
line.vertex1 = stream.readUint16LE() / 6;
line.vertex2 = stream.readUint16LE() / 6;
_lines.push_back(line);
}
}
void BodyData::loadSpheres(Common::SeekableReadStream &stream) {
const uint16 numSpheres = stream.readUint16LE();
if (stream.eos())
return;
_spheres.reserve(numSpheres);
for (uint16 i = 0; i < numSpheres; ++i) {
BodySphere sphere;
sphere.fillType = stream.readByte();
sphere.color = stream.readUint16LE();
stream.readByte();
sphere.radius = stream.readUint16LE();
sphere.vertex = stream.readUint16LE() / 6;
_spheres.push_back(sphere);
}
}
bool BodyData::loadFromStream(Common::SeekableReadStream &stream, bool lba1) {
reset();
if (lba1) {
const uint16 flags = stream.readUint16LE();
animated = (flags & INFO_ANIM) != 0;
bbox.mins.x = stream.readSint16LE();
bbox.maxs.x = stream.readSint16LE();
bbox.mins.y = stream.readSint16LE();
bbox.maxs.y = stream.readSint16LE();
bbox.mins.z = stream.readSint16LE();
bbox.maxs.z = stream.readSint16LE();
offsetToData = stream.readSint16LE();
// using this value as the offset crashes the demo of lba1 - see https://bugs.scummvm.org/ticket/14294
// stream.seek(offsetToData);
stream.seek(0x1A);
loadVertices(stream);
loadBones(stream);
loadNormals(stream);
loadPolygons(stream);
loadLines(stream);
loadSpheres(stream);
} else {
// T_BODY_HEADER (lba2)
const uint32 flags = stream.readUint32LE();
animated = (flags & INFO_ANIM) != 0;
stream.skip(4); // int16 size of header and int16 dummy
bbox.mins.x = stream.readSint32LE();
bbox.maxs.x = stream.readSint32LE();
bbox.mins.y = stream.readSint32LE();
bbox.maxs.y = stream.readSint32LE();
bbox.mins.z = stream.readSint32LE();
bbox.maxs.z = stream.readSint32LE();
stream.seek(0x20);
#if 0
const uint32 bonesSize = stream.readUint32LE();
const uint32 bonesOffset = stream.readUint32LE();
const uint32 verticesSize = stream.readUint32LE();
const uint32 verticesOffset = stream.readUint32LE();
const uint32 normalsSize = stream.readUint32LE();
const uint32 normalsOffset = stream.readUint32LE();
const uint32 unk1Size = stream.readUint32LE();
const uint32 unk1Offset = stream.readUint32LE();
const uint32 polygonsSize = stream.readUint32LE();
const uint32 polygonsOffset = stream.readUint32LE();
const uint32 linesSize = stream.readUint32LE();
const uint32 linesOffset = stream.readUint32LE();
const uint32 spheresSize = stream.readUint32LE();
const uint32 spheresOffset = stream.readUint32LE();
const uint32 uvGroupsSize = stream.readUint32LE();
const uint32 uvGroupsOffset = stream.readUint32LE();
#endif
}
return !stream.err();
}
} // namespace TwinE

126
engines/twine/parser/body.h Normal file
View File

@@ -0,0 +1,126 @@
/* 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 TWINE_PARSER_BODY_H
#define TWINE_PARSER_BODY_H
#include "common/array.h"
#include "common/memstream.h"
#include "common/stream.h"
#include "twine/parser/anim.h"
#include "twine/parser/bodytypes.h"
#include "twine/parser/parser.h"
#include "twine/shared.h"
namespace TwinE {
/** Actors animation timer structure */
struct AnimTimerDataStruct {
const KeyFrame *ptr = nullptr;
int32 time = 0; // keyframe time
};
class BodyData : public Parser {
private:
void loadVertices(Common::SeekableReadStream &stream);
void loadBones(Common::SeekableReadStream &stream);
void loadNormals(Common::SeekableReadStream &stream);
void loadPolygons(Common::SeekableReadStream &stream);
void loadLines(Common::SeekableReadStream &stream);
void loadSpheres(Common::SeekableReadStream &stream);
Common::Array<BodyPolygon> _polygons;
Common::Array<BodyVertex> _vertices;
Common::Array<BodySphere> _spheres;
Common::Array<BodyNormal> _normals;
Common::Array<BodyLine> _lines;
Common::Array<BodyBone> _bones;
BoneFrame _boneStates[560];
protected:
void reset() override;
public:
bool animated = false;
AnimTimerDataStruct _animTimerData;
BoundingBox bbox;
int16 offsetToData = 0;
inline bool isAnimated() const {
return animated;
}
inline uint getNumBones() const {
return _bones.size();
}
inline uint getNumVertices() const {
return _vertices.size();
}
BoneFrame *getBoneState(int16 boneIdx) {
return &_boneStates[boneIdx];
}
const BoneFrame *getBoneState(int16 boneIdx) const {
return &_boneStates[boneIdx];
}
const Common::Array<BodyPolygon> &getPolygons() const {
return _polygons;
}
const Common::Array<BodyVertex> &getVertices() const {
return _vertices;
}
const Common::Array<BodySphere> &getSpheres() const {
return _spheres;
}
const Common::Array<BodyNormal> &getNormals() const {
return _normals;
}
const BodyNormal &getNormal(int16 normalIdx) const {
return _normals[normalIdx];
}
const Common::Array<BodyLine> &getLines() const {
return _lines;
}
const Common::Array<BodyBone> &getBones() const {
return _bones;
}
const BodyBone &getBone(int16 boneIdx) const {
return _bones[boneIdx];
}
bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
};
} // End of namespace TwinE
#endif

View File

@@ -0,0 +1,83 @@
/* 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 TWINE_PARSER_BODYTYPES_H
#define TWINE_PARSER_BODYTYPES_H
#include "common/array.h"
#include "twine/shared.h"
#include "twine/parser/anim.h"
namespace TwinE {
struct BodyVertex {
int16 x;
int16 y;
int16 z;
uint16 bone;
};
struct BodyLine {
// fill byte here
uint8 color;
// 2 fill bytes here
uint16 vertex1;
uint16 vertex2;
};
struct BodySphere {
uint8 fillType;
uint16 color; // start and end color index
// fill byte here
uint16 radius;
uint16 vertex;
};
struct BodyBone {
uint16 parent;
uint16 vertex;
int16 firstVertex;
int16 numVertices;
int32 numNormals;
BoneFrame initalBoneState;
inline bool isRoot() const {
return parent == 0xffff;
}
};
struct BodyNormal {
int16 x;
int16 y;
int16 z;
uint16 prenormalizedRange;
};
struct BodyPolygon {
Common::Array<uint16> indices;
Common::Array<uint16> normals;
int8 materialType = 0;
int16 intensity = 0; // color1 / color2
};
}
#endif

View File

@@ -0,0 +1,341 @@
/* 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 "twine/parser/entity.h"
#include "common/stream.h"
#include "twine/resources/resources.h"
#include "twine/shared.h"
namespace TwinE {
bool EntityData::loadBody(Common::SeekableReadStream &stream, bool lba1) {
EntityBody body;
body.index = stream.readByte();
const int64 pos = stream.pos();
uint8 size = stream.readByte();
body.hqrBodyIndex = (int16)stream.readUint16LE();
if (!body.body.loadFromHQR(TwineResource(Resources::HQR_BODY_FILE, body.hqrBodyIndex), lba1)) {
error("Failed to load body with index: %i", body.hqrBodyIndex);
}
const uint8 numActions = stream.readByte();
for (uint8 i = 0U; i < numActions; ++i) {
if ((ActionType)stream.readByte() == ActionType::ACTION_ZV) {
body.actorBoundingBox.hasBoundingBox = true;
body.actorBoundingBox.bbox.mins.x = stream.readSint16LE();
body.actorBoundingBox.bbox.mins.y = stream.readSint16LE();
body.actorBoundingBox.bbox.mins.z = stream.readSint16LE();
body.actorBoundingBox.bbox.maxs.x = stream.readSint16LE();
body.actorBoundingBox.bbox.maxs.y = stream.readSint16LE();
body.actorBoundingBox.bbox.maxs.z = stream.readSint16LE();
}
}
_bodies.push_back(body);
stream.seek(pos + size);
return !stream.err();
}
bool EntityData::loadAnim(Common::SeekableReadStream &stream, bool lba1) {
EntityAnim anim;
if (lba1) {
anim.animation = (AnimationTypes)stream.readByte();
} else {
anim.animation = (AnimationTypes)stream.readUint16LE();
}
const int64 pos = stream.pos();
uint8 size = stream.readByte();
anim.animIndex = stream.readSint16LE();
const uint8 numActions = stream.readByte();
for (uint8 i = 0U; i < numActions; ++i) {
EntityAnim::Action action;
action.type = (ActionType)stream.readByte();
switch (action.type) {
case ActionType::ACTION_HITTING:
action.animFrame = stream.readByte();
action.strength = stream.readByte();
break;
case ActionType::ACTION_SAMPLE:
action.animFrame = stream.readByte();
action.sampleIndex = stream.readSint16LE();
break;
case ActionType::ACTION_SAMPLE_FREQ:
action.animFrame = stream.readByte();
action.sampleIndex = stream.readSint16LE();
action.frequency = stream.readSint16LE();
break;
case ActionType::ACTION_THROW_MAGIC_BALL:
action.animFrame = stream.readByte();
action.yHeight = stream.readSint16LE();
action.xAngle = ToAngle(stream.readSint16LE());
action.xRotPoint = stream.readSint16LE();
action.extraAngle = stream.readByte();
break;
case ActionType::ACTION_SAMPLE_REPEAT:
action.animFrame = stream.readByte();
action.sampleIndex = stream.readSint16LE();
action.repeat = stream.readSint16LE();
if (!lba1) {
action.decal = stream.readSint16LE();
action.sampleVolume = stream.readByte();
action.frequency = stream.readSint16LE();
}
break;
case ActionType::ACTION_THROW_SEARCH:
action.animFrame = stream.readByte();
action.yHeight = stream.readSint16LE();
action.spriteIndex = stream.readByte();
action.targetActor = stream.readByte();
action.finalAngle = stream.readSint16LE();
action.strength = stream.readByte();
break;
case ActionType::ACTION_THROW_EXTRA_BONUS:
case ActionType::ACTION_THROW_ALPHA:
action.animFrame = stream.readByte();
action.yHeight = stream.readSint16LE();
action.spriteIndex = stream.readByte();
action.xAngle = ToAngle(stream.readSint16LE());
action.yAngle = ToAngle(stream.readSint16LE());
action.xRotPoint = stream.readSint16LE();
// TODO: does lba2 need a scaling here for xRotPoint?
action.extraAngle = ToAngle(stream.readByte());
action.strength = stream.readByte();
break;
case ActionType::ACTION_LEFT_STEP:
case ActionType::ACTION_RIGHT_STEP:
case ActionType::ACTION_HERO_HITTING:
action.animFrame = stream.readByte();
break;
case ActionType::ACTION_SAMPLE_STOP:
action.animFrame = stream.readByte();
if (lba1) {
action.sampleIndex = stream.readByte();
stream.skip(1);
} else {
action.sampleIndex = stream.readUint16LE();
}
break;
case ActionType::ACTION_THROW_3D:
case ActionType::ACTION_THROW_3D_ALPHA:
action.animFrame = stream.readByte();
action.distanceX = stream.readSint16LE();
action.distanceY = stream.readSint16LE();
action.distanceZ = stream.readSint16LE();
action.spriteIndex = stream.readByte();
action.xAngle = ToAngle(stream.readSint16LE());
action.yAngle = ToAngle(stream.readSint16LE());
action.xRotPoint = stream.readSint16LE();
action.extraAngle = ToAngle(stream.readByte());
action.strength = stream.readByte();
break;
case ActionType::ACTION_THROW_3D_SEARCH:
action.animFrame = stream.readByte();
action.distanceX = stream.readSint16LE();
action.distanceY = stream.readSint16LE();
action.distanceZ = stream.readSint16LE();
action.spriteIndex = stream.readByte();
action.targetActor = stream.readByte();
action.finalAngle = ToAngle(stream.readSint16LE());
action.strength = stream.readByte();
break;
case ActionType::ACTION_THROW_3D_MAGIC:
action.animFrame = stream.readByte();
action.distanceX = stream.readSint16LE();
action.distanceY = stream.readSint16LE();
action.distanceZ = stream.readSint16LE();
action.xAngle = stream.readSint16LE();
action.yAngle = stream.readSint16LE();
action.finalAngle = stream.readByte();
break;
case ActionType::ACTION_ZV:
default:
break;
}
if (!lba1) {
switch (action.type) {
case ActionType::ACTION_ZV:
action.animFrame = stream.readByte();
action.bbox.mins.x = stream.readSint16LE();
action.bbox.mins.y = stream.readSint16LE();
action.bbox.mins.z = stream.readSint16LE();
action.bbox.maxs.x = stream.readSint16LE();
action.bbox.maxs.y = stream.readSint16LE();
action.bbox.maxs.z = stream.readSint16LE();
break;
case ActionType::ACTION_SUPER_HIT:
action.animFrame = stream.readByte();
action.strength = stream.readByte();
action.superHitX = stream.readSint16LE();
action.superHitY = stream.readSint16LE();
action.superHitZ = stream.readSint16LE();
action.sizeSuperHit = stream.readSint16LE();
break;
case ActionType::ACTION_THROW_OBJ_3D:
action.animFrame = stream.readByte();
action.distanceX = stream.readSint16LE();
action.distanceY = stream.readSint16LE();
action.distanceZ = stream.readSint16LE();
action.spriteIndex = stream.readByte();
action.xAngle = ToAngle(stream.readSint16LE());
action.yAngle = ToAngle(stream.readSint16LE());
action.xRotPoint = stream.readSint16LE();
action.extraAngle = ToAngle(stream.readByte());
action.strength = stream.readByte();
break;
case ActionType::ACTION_NEW_SAMPLE:
action.animFrame = stream.readByte();
action.sampleIndex = stream.readSint16LE();
action.decal = stream.readSint16LE();
action.sampleVolume = stream.readByte();
action.frequency = stream.readSint16LE();
break;
case ActionType::ACTION_THROW_DART:
action.animFrame = stream.readByte();
action.distanceY = stream.readSint16LE();
action.xAngle = ToAngle(stream.readSint16LE());
action.speed = stream.readSint16LE();
action.weight = stream.readSByte();
break;
case ActionType::ACTION_SHIELD:
action.animFrame = stream.readByte();
action.lastAnimFrame = stream.readByte();
break;
case ActionType::ACTION_FLOW_3D:
case ActionType::ACTION_THROW_3D_CONQUE:
action.animFrame = stream.readByte();
action.xAngle = ToAngle(stream.readSint16LE());
action.yHeight = stream.readSint16LE();
action.yAngle = ToAngle(stream.readSint16LE());
action.spriteIndex = stream.readByte();
break;
case ActionType::ACTION_IMPACT:
case ActionType::ACTION_RENVOYABLE:
action.animFrame = stream.readByte();
action.strength = stream.readSint16LE();
break;
case ActionType::ACTION_SCALE:
action.animFrame = stream.readByte();
action.scale = stream.readSint32LE();
break;
case ActionType::ACTION_IMPACT_3D:
action.animFrame = stream.readByte();
action.xAngle = ToAngle(stream.readSint16LE());
action.yHeight = stream.readSint16LE();
action.yAngle = ToAngle(stream.readSint16LE());
action.spriteIndex = stream.readSint16LE();
break;
case ActionType::ACTION_THROW_MAGIC_EXTRA:
action.animFrame = stream.readByte();
action.pointIndex = stream.readSint16LE();
action.spriteIndex = stream.readByte();
action.xAngle = ToAngle(stream.readSint16LE());
action.speed = stream.readSint16LE();
action.weight = stream.readSByte();
break;
case ActionType::ACTION_ZV_ANIMIT:
case ActionType::ACTION_RENVOIE:
case ActionType::ACTION_TRANSPARENT:
case ActionType::ACTION_SAMPLE_MAGIC:
case ActionType::ACTION_LEFT_JUMP:
case ActionType::ACTION_RIGHT_JUMP:
case ActionType::ACTION_THROW_FOUDRE:
action.animFrame = stream.readByte();
/* empty */
break;
case ActionType::ACTION_PATH:
case ActionType::ACTION_FLOW:
default:
break;
}
}
if (action.type > ActionType::ACTION_THROW_FOUDRE) {
error("Unknown action type %d", (int)action.type);
}
anim._actions.push_back(action);
}
_animations.push_back(anim);
stream.seek(pos + size);
return !stream.err();
}
void EntityData::reset() {
_animations.clear();
_bodies.clear();
}
bool EntityData::loadFromStream(Common::SeekableReadStream &stream, bool lba1) {
reset();
do {
const uint8 opcode = stream.readByte();
if (opcode == 1) {
if (!loadBody(stream, lba1)) {
return false;
}
} else if (opcode == 3) {
if (!loadAnim(stream, lba1)) {
return false;
}
} else if (opcode == 0xFF) {
break;
}
} while (!stream.eos() && !stream.err());
return true;
}
const Common::Array<EntityAnim::Action> *EntityData::getActions(AnimationTypes animation) const {
for (const EntityAnim &anim : _animations) {
if (anim.animation == animation) {
if (anim._actions.empty()) {
return nullptr;
}
return &anim._actions;
}
}
return nullptr;
}
BodyData &EntityData::getBody(int index) {
for (EntityBody &body : _bodies) {
if (body.index == index) {
return body.body;
}
}
error("Could not find body for index: %i", index);
}
const EntityBody *EntityData::getEntityBody(const int index) const {
for (const EntityBody &body : _bodies) {
if (body.index == index) {
return &body;
}
}
return nullptr;
}
int32 EntityData::getAnimIndex(AnimationTypes animation) const {
for (const EntityAnim &anim : _animations) {
if (anim.animation == animation) {
return anim.animIndex;
}
}
return -1;
}
} // End of namespace TwinE

View File

@@ -0,0 +1,119 @@
/* 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 TWINE_PARSER_ENTITY_H
#define TWINE_PARSER_ENTITY_H
#include "common/array.h"
#include "common/memstream.h"
#include "common/stream.h"
#include "twine/parser/body.h"
#include "twine/parser/parser.h"
#include "twine/shared.h"
namespace TwinE {
struct EntityBody {
int index; /**< index in file3d.hqr */
ActorBoundingBox actorBoundingBox;
int hqrBodyIndex; /**< index in body.hqr */
BodyData body;
};
struct EntityAnim {
AnimationTypes animation;
int animIndex;
struct Action {
ActionType type = ActionType::ACTION_NOP;
uint8 animFrame = 0;
uint8 lastAnimFrame = 0;
int8 weight = 0;
byte sampleVolume = 0;
int16 pointIndex = 0;
int16 spriteIndex = 0;
uint8 targetActor = 0;
int16 sampleIndex = 0;
int16 frequency = 0;
int16 xAngle = 0;
int16 yAngle = 0;
int16 xRotPoint = 0;
int16 extraAngle = 0;
int16 finalAngle = 0;
int16 strength = 0;
int16 distanceX = 0;
int16 distanceY = 0;
int16 distanceZ = 0;
int16 yHeight = 0;
int16 repeat = 0;
int16 speed = 0;
int16 superHitX = 0;
int16 superHitY = 0;
int16 superHitZ = 0;
int16 sizeSuperHit = 0;
int16 decal = 0;
int32 scale = 0;
BoundingBox bbox;
};
Common::Array<Action> _actions;
};
/**
* @brief Associate 3d models from body hqr with animations from anim.hqr for the game characters
*/
class EntityData : public Parser {
private:
Common::Array<EntityBody> _bodies;
Common::Array<EntityAnim> _animations;
bool loadBody(Common::SeekableReadStream &stream, bool lba1);
bool loadAnim(Common::SeekableReadStream &stream, bool lba1);
protected:
void reset() override;
public:
bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
const Common::Array<EntityAnim::Action> *getActions(AnimationTypes animation) const;
const EntityBody *getEntityBody(const int index) const;
BodyData &getBody(int index);
int32 getAnimIndex(AnimationTypes animation) const;
const Common::Array<EntityBody> &getBodies() const {
return _bodies;
}
const Common::Array<EntityAnim> &getAnimations() const {
return _animations;
}
Common::Array<EntityBody> &getBodies() {
return _bodies;
}
Common::Array<EntityAnim> &getAnimations() {
return _animations;
}
};
} // End of namespace TwinE
#endif

View File

@@ -0,0 +1,54 @@
/* 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 "twine/parser/holomap.h"
#include "common/debug.h"
#include "common/stream.h"
namespace TwinE {
void TrajectoryData::reset() {
_trajectories.clear();
}
bool TrajectoryData::loadFromStream(Common::SeekableReadStream &stream, bool lba1) {
reset();
_trajectories.reserve(100); // this is the lba1 amount of trajectories
while (stream.pos() < stream.size()) {
Trajectory data;
data.locationIdx = stream.readSint16LE();
data.trajLocationIdx = stream.readSint16LE();
data.vehicleIdx = stream.readSint16LE();
data.angle.x = stream.readSint16LE();
data.angle.y = stream.readSint16LE();
data.angle.z = stream.readSint16LE();
data.numAnimFrames = stream.readSint16LE();
assert(data.numAnimFrames < ARRAYSIZE(data.positions));
for (int32 i = 0; i < data.numAnimFrames; ++i) {
data.positions[i].x = stream.readSint16LE();
data.positions[i].y = stream.readSint16LE();
}
_trajectories.push_back(data);
}
return !stream.err();
}
} // End of namespace TwinE

View File

@@ -0,0 +1,94 @@
/* 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 TWINE_PARSER_HOLOMAP_H
#define TWINE_PARSER_HOLOMAP_H
#include "common/memstream.h"
#include "twine/parser/parser.h"
namespace TwinE {
enum HolomapVehicle {
FerryBoat = 31,
Motorbike = 33,
Car = 35,
FishingBoat = 37,
Catamaran = 39,
Hovercraft = 41,
Dino = 43,
ArmyBoat = 45,
HamalayiTransporter = 47
};
struct TrajectoryPos {
int16 x = 0;
int16 y = 0;
};
struct Trajectory {
int16 locationIdx = -1;
int16 trajLocationIdx = -1;
int16 vehicleIdx = -1;
IVec3 angle;
int16 numAnimFrames = 0;
TrajectoryPos positions[512];
bool isValid() const {
return locationIdx != -1;
}
/**
* The HQR index of the vehicle model for the holomap
* @note Multiplied by 2 because the model index is always followed by the corresponding animation index for that model
*/
int32 getModel() const {
return 2 * vehicleIdx + HolomapVehicle::FerryBoat;
}
int32 getAnimation() const {
return getModel() + 1;
}
};
class TrajectoryData : public Parser {
private:
Common::Array<Trajectory> _trajectories;
protected:
void reset() override;
public:
bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
const Trajectory *getTrajectory(uint index) const {
if (index >= _trajectories.size()) {
return nullptr;
}
return &_trajectories[index];
}
const Common::Array<Trajectory> &getTrajectories() const {
return _trajectories;
}
};
} // End of namespace TwinE
#endif

View File

@@ -0,0 +1,52 @@
/* 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 "twine/parser/parser.h"
#include "common/stream.h"
#include "twine/resources/hqr.h"
#include "twine/shared.h"
namespace TwinE {
bool Parser::loadFromBuffer(const uint8 *buf, uint32 size, bool lba1) {
if (size == 0) {
return false;
}
Common::MemoryReadStream stream(buf, size);
return loadFromStream(stream, lba1);
}
bool Parser::loadFromHQR(const char *name, int index, bool lba1) {
Common::SeekableReadStream *stream = HQR::makeReadStream(name, index);
if (stream == nullptr) {
warning("Failed to load %s with index %i", name, index);
return false;
}
if (!loadFromStream(*stream, lba1)) {
delete stream;
return false;
}
_hqrIndex = index;
delete stream;
return true;
}
} // End of namespace TwinE

View File

@@ -0,0 +1,56 @@
/* 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 TWINE_PARSER_PARSER_H
#define TWINE_PARSER_PARSER_H
#include "common/array.h"
#include "common/memstream.h"
#include "common/stream.h"
#include "twine/shared.h"
namespace TwinE {
class Parser {
protected:
int _hqrIndex = -1;
virtual void reset() {}
public:
virtual ~Parser() {
reset();
}
virtual bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) = 0;
bool loadFromBuffer(const uint8 *buf, uint32 size, bool lba1);
bool loadFromHQR(const char *name, int index, bool lba1);
int hqrIndex() const {
return _hqrIndex;
}
inline bool loadFromHQR(const TwineResource &resource, bool lba1) {
return loadFromHQR(resource.hqr, resource.index, lba1);
}
};
} // End of namespace TwinE
#endif

View File

@@ -0,0 +1,118 @@
/* 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 "twine/parser/sprite.h"
#include "common/stream.h"
#include "twine/shared.h"
namespace TwinE {
bool SpriteBoundingBoxData::loadFromStream(Common::SeekableReadStream &stream, bool lba1) {
const int32 size = stream.size();
const int32 amount = size / 16;
for (int32 i = 0; i < amount; ++i) {
SpriteDim spriteDim;
spriteDim.x = stream.readSint16LE();
spriteDim.y = stream.readSint16LE();
BoundingBox boundingBox;
boundingBox.mins.x = stream.readSint16LE();
boundingBox.maxs.x = stream.readSint16LE();
boundingBox.mins.y = stream.readSint16LE();
boundingBox.maxs.y = stream.readSint16LE();
boundingBox.mins.z = stream.readSint16LE();
boundingBox.maxs.z = stream.readSint16LE();
_boundingBoxes.push_back(boundingBox);
_dimensions.push_back(spriteDim);
}
return !stream.err();
}
void SpriteData::reset() {
for (int i = 0; i < _sprites; ++i) {
_surfaces[i].free();
}
_sprites = 0;
}
bool SpriteData::loadFromStream(Common::SeekableReadStream &stream, bool lba1) {
reset();
if (_bricks) {
// brick sprites don't have the offsets
return loadSprite(stream, 0);
}
const uint32 offset1 = stream.readUint32LE();
const uint32 offset2 = stream.readUint32LE();
const uint32 offsetData = stream.pos();
if (!loadSprite(stream, offset1)) {
return false;
}
// for most sprites the second offset is just the end of the stream - but
// some sprites (like shadow in lba1) have a second sprite following the
// first one.
if (offset2 + offsetData >= stream.size()) {
return true;
}
return loadSprite(stream, offset2);
}
bool SpriteData::loadSprite(Common::SeekableReadStream &stream, uint32 offset) {
stream.seek(offset);
int width = stream.readByte();
int height = stream.readByte();
_offsetX[_sprites] = stream.readByte();
_offsetY[_sprites] = stream.readByte();
const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
_surfaces[_sprites].create(width, height, format);
const uint8 *last = (const uint8 *)_surfaces[_sprites].getBasePtr(width, height - 1);
for (int y = 0; y < height; ++y) {
const uint8 numRuns = stream.readByte();
int x = 0;
for (uint8 run = 0; run < numRuns; ++run) {
const uint8 runSpec = stream.readByte();
const uint8 runLength = bits(runSpec, 0, 6) + 1;
const uint8 type = bits(runSpec, 6, 2);
if (type == 1) {
uint8 *start = (uint8 *)_surfaces[_sprites].getBasePtr(x, y);
for (uint8 i = 0; i < runLength; ++i) {
if (start > last) {
return false;
}
*start++ = stream.readByte();
}
} else if (type != 0) {
uint8 *start = (uint8 *)_surfaces[_sprites].getBasePtr(x, y);
uint8 *end = (uint8 *)_surfaces[_sprites].getBasePtr(x + runLength, y);
if (end > last) {
return false;
}
Common::fill(start, end, stream.readByte());
}
x += runLength;
}
}
if (stream.err()) {
return false;
}
++_sprites;
return true;
}
} // namespace TwinE

View File

@@ -0,0 +1,116 @@
/* 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 TWINE_PARSER_SPRITE_H
#define TWINE_PARSER_SPRITE_H
#include "common/array.h"
#include "common/memstream.h"
#include "common/stream.h"
#include "graphics/managed_surface.h"
#include "twine/parser/parser.h"
#include "twine/shared.h"
namespace TwinE {
struct SpriteDim {
int16 x = 0;
int16 y = 0;
int16 w = 0;
int16 h = 0;
};
// PtrZvExtra
class SpriteBoundingBoxData : public Parser {
private:
Common::Array<BoundingBox> _boundingBoxes;
Common::Array<SpriteDim> _dimensions;
public:
bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
const BoundingBox *bbox(int index) const; // PtrZvAnim3DS, PtrZvExtra, PtrZvExtraRaw
const SpriteDim *dim(int index) const;
};
inline const BoundingBox *SpriteBoundingBoxData::bbox(int index) const {
if (index < 0) {
return nullptr;
}
return &_boundingBoxes[index];
}
inline const SpriteDim *SpriteBoundingBoxData::dim(int index) const {
if (index < 0) {
return nullptr;
}
return &_dimensions[index];
}
class SpriteData : public Parser {
protected:
Graphics::ManagedSurface _surfaces[2];
int _offsetX[2] {0};
int _offsetY[2] {0};
int _sprites = 0;
bool _bricks = false;
bool loadSprite(Common::SeekableReadStream &stream, uint32 offset);
void reset() override;
public:
bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
inline const Graphics::ManagedSurface &surface(int index = 0) const {
if (index < 0 || index >= _sprites) {
error("Sprite surface index out of range: %i (max: %i)", index, _sprites);
}
return _surfaces[index];
}
inline int sprites() const {
return _sprites;
}
inline int offsetX(int index = 0) const {
if (index < 0 || index >= _sprites) {
error("Sprite offset index out of range: %i (max: %i)", index, _sprites);
}
return _offsetX[index];
}
inline int offsetY(int index = 0) const {
if (index < 0 || index >= _sprites) {
error("Sprite offset index out of range: %i (max: %i)", index, _sprites);
}
return _offsetY[index];
}
};
class BrickData : public SpriteData {
public:
BrickData() {
_bricks = true;
}
};
} // End of namespace TwinE
#endif

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/>.
*
*/
#include "twine/parser/text.h"
#include "common/debug.h"
#include "common/str-enc.h"
#include "common/util.h"
#include "common/translation.h"
#include "twine/resources/hqr.h"
#include "twine/shared.h"
namespace TwinE {
void TextData::initCustomTexts(TextBankId textBankId) {
if (textBankId == TextBankId::Options_and_menus) {
// TODO: add resource file for these custom strings to support other languages
add(textBankId, TextEntry{Common::U32String("High resolution on").encode(Common::CodePage::kDos850), -1, TextId::kCustomHighResOptionOn});
add(textBankId, TextEntry{Common::U32String("High resolution off").encode(Common::CodePage::kDos850), -1, TextId::kCustomHighResOptionOff});
add(textBankId, TextEntry{Common::U32String("Wall collision on").encode(Common::CodePage::kDos850), -1, TextId::kCustomWallCollisionOn});
add(textBankId, TextEntry{Common::U32String("Wall collision off").encode(Common::CodePage::kDos850), -1, TextId::kCustomWallCollisionOff});
add(textBankId, TextEntry{Common::U32String("Language selection").encode(Common::CodePage::kDos850), -1, TextId::kCustomLanguageOption});
add(textBankId, TextEntry{Common::U32String("Voices: None").encode(Common::CodePage::kDos850), -1, TextId::kCustomVoicesNone});
add(textBankId, TextEntry{Common::U32String("Voices: English").encode(Common::CodePage::kDos850), -1, TextId::kCustomVoicesEnglish});
add(textBankId, TextEntry{Common::U32String("Voices: French").encode(Common::CodePage::kDos850), -1, TextId::kCustomVoicesFrench});
add(textBankId, TextEntry{Common::U32String("Voices: German").encode(Common::CodePage::kDos850), -1, TextId::kCustomVoicesGerman});
}
}
bool TextData::loadFromHQR(const char *name, TextBankId textBankId, int language, bool lba1, int entryCount) {
const int langIdx = (int)textBankId * 2 + (entryCount * language);
Common::SeekableReadStream *indexStream = HQR::makeReadStream(name, langIdx + 0);
Common::SeekableReadStream *offsetStream = HQR::makeReadStream(name, langIdx + 1);
if (indexStream == nullptr || offsetStream == nullptr) {
warning("Failed to load %s with index %i", name, langIdx);
delete indexStream;
delete offsetStream;
return false;
}
_texts[(int)textBankId].clear();
initCustomTexts(textBankId);
const int numIdxEntries = (int)indexStream->size() / 2;
_texts[(int)textBankId].reserve(numIdxEntries + _texts[(int)textBankId].size());
for (int entry = 0; entry < numIdxEntries; ++entry) {
const TextId textIdx = (TextId)indexStream->readUint16LE();
uint16 start = offsetStream->readUint16LE();
const int32 offsetPos = offsetStream->pos();
const uint16 end = offsetStream->readUint16LE();
if (!lba1) {
++start;
}
offsetStream->seek(start);
Common::String result;
for (int16 i = start; i < end - 1; ++i) {
const char c = (char)offsetStream->readByte();
if (c == '\0') {
break;
}
result += c;
}
add(textBankId, TextEntry{result, entry, textIdx});
debugC(2, TwinE::kDebugResources, "index: %i (bank %i), text: %s", (int)textIdx, (int)textBankId, result.c_str());
offsetStream->seek(offsetPos);
if (end >= offsetStream->size()) {
break;
}
}
delete indexStream;
delete offsetStream;
return true;
}
const TextEntry *TextData::getText(TextBankId textBankId, TextId textIndex) const {
const Common::Array<TextEntry> &entries = _texts[(int)textBankId];
const int32 size = entries.size();
for (int32 i = 0; i < size; ++i) {
if (entries[i].textIndex == textIndex) {
return &entries[i];
}
}
debugC(1, TwinE::kDebugResources, "Failed to find text entry for bank id %i with text index %i", (int)textBankId, (int)textIndex);
return nullptr;
}
} // End of namespace TwinE

View File

@@ -0,0 +1,58 @@
/* 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 TWINE_PARSER_TEXT_H
#define TWINE_PARSER_TEXT_H
#include "common/array.h"
#include "common/memstream.h"
#include "common/stream.h"
#include "twine/parser/parser.h"
#include "twine/shared.h"
namespace TwinE {
class TextEntry {
public:
Common::String string; /**< The real string behind the text id */
int index; /**< The index in the text index hqr file. This is also the index in the corresponding vox hqr file */
TextId textIndex; /**< The text identifier */
};
class TextData {
private:
// 30 is the max for lba2, lba1 uses 28
Common::Array<TextEntry> _texts[30];
void add(TextBankId textBankId, const TextEntry &entry) {
_texts[(int)textBankId].push_back(entry);
}
// custom texts that are not included in the original game
void initCustomTexts(TextBankId textBankId);
public:
bool loadFromHQR(const char *name, TextBankId textBankId, int language, bool lba1, int entryCount);
const TextEntry *getText(TextBankId textBankId, TextId textIndex) const;
};
} // End of namespace TwinE
#endif