[Scummvm-git-logs] scummvm master -> 0c4d570bcfb0a4331050905fadcf955c42e01789
aquadran
noreply at scummvm.org
Sun Sep 11 21:27:06 UTC 2022
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
0c4d570bcf WINTERMUTE: Implement X file loader and restore original engine code
Commit: 0c4d570bcfb0a4331050905fadcf955c42e01789
https://github.com/scummvm/scummvm/commit/0c4d570bcfb0a4331050905fadcf955c42e01789
Author: PaweÅ KoÅodziejski (aquadran at gmail.com)
Date: 2022-09-11T23:27:00+02:00
Commit Message:
WINTERMUTE: Implement X file loader and restore original engine code
Changed paths:
A engines/wintermute/base/gfx/xfile.cpp
A engines/wintermute/base/gfx/xfile.h
A engines/wintermute/base/gfx/xfile_loader.cpp
A engines/wintermute/base/gfx/xfile_loader.h
R engines/wintermute/base/gfx/xloader.cpp
R engines/wintermute/base/gfx/xloader.h
engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h
engines/wintermute/base/gfx/xanimation.cpp
engines/wintermute/base/gfx/xanimation.h
engines/wintermute/base/gfx/xanimation_set.cpp
engines/wintermute/base/gfx/xanimation_set.h
engines/wintermute/base/gfx/xframe_node.cpp
engines/wintermute/base/gfx/xframe_node.h
engines/wintermute/base/gfx/xmaterial.cpp
engines/wintermute/base/gfx/xmaterial.h
engines/wintermute/base/gfx/xmesh.cpp
engines/wintermute/base/gfx/xmesh.h
engines/wintermute/base/gfx/xmodel.cpp
engines/wintermute/base/gfx/xmodel.h
engines/wintermute/module.mk
diff --git a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
index 2ba1e5a52e7..b2c14985c8c 100644
--- a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
+++ b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.cpp
@@ -49,8 +49,8 @@ XMeshOpenGLShader::~XMeshOpenGLShader() {
glDeleteBuffers(1, &_indexBuffer);
}
-bool XMeshOpenGLShader::loadFromX(const Common::String &filename, XFileLexer &lexer, Common::Array<MaterialReference> &materialReferences) {
- if (XMesh::loadFromX(filename, lexer, materialReferences)) {
+bool XMeshOpenGLShader::loadFromXData(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences) {
+ if (XMesh::loadFromXData(filename, xobj, materialReferences)) {
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, 4 * kVertexComponentCount * _vertexCount, _vertexData, GL_DYNAMIC_DRAW);
diff --git a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h
index 9e35545392f..4ef48abff0b 100644
--- a/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h
+++ b/engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h
@@ -41,7 +41,7 @@ public:
XMeshOpenGLShader(BaseGame *inGame, OpenGL::Shader *shader, OpenGL::Shader *flatShadowShader);
~XMeshOpenGLShader() override;
- bool loadFromX(const Common::String &filename, XFileLexer &lexer, Common::Array<MaterialReference> &materialReferences) override;
+ bool loadFromXData(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences) override;
bool render(XModel *model) override;
bool renderFlatShadowModel() override;
bool update(FrameNode *parentFrame) override;
diff --git a/engines/wintermute/base/gfx/xanimation.cpp b/engines/wintermute/base/gfx/xanimation.cpp
index 2ad7b76858c..e8ce6c2205d 100644
--- a/engines/wintermute/base/gfx/xanimation.cpp
+++ b/engines/wintermute/base/gfx/xanimation.cpp
@@ -26,11 +26,12 @@
*/
#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_engine.h"
#include "engines/wintermute/base/gfx/xanimation.h"
#include "engines/wintermute/base/gfx/xanimation_set.h"
#include "engines/wintermute/base/gfx/xframe_node.h"
#include "engines/wintermute/base/gfx/xmodel.h"
-#include "engines/wintermute/base/gfx/xloader.h"
+#include "engines/wintermute/base/gfx/xfile_loader.h"
#include "engines/wintermute/dcgf.h"
namespace Wintermute {
@@ -67,72 +68,197 @@ bool Animation::findBone(FrameNode *rootFrame) {
}
//////////////////////////////////////////////////////////////////////////
-bool Animation::loadFromX(XFileLexer &lexer, AnimationSet *parentAnimSet) {
- if (lexer.tokenIsIdentifier()) {
- lexer.advanceToNextToken(); // skip name
- lexer.advanceOnOpenBraces();
+bool Animation::load(XFileData *xobj, AnimationSet *parentAnimSet) {
+ bool result;
+ XClassType objectType;
+
+ if (xobj->isReference()) {
+ // The original data is found
+ result = xobj->getType(objectType);
+ if (!result)
+ return result;
+
+ // The object must be a frame
+ if (objectType == kXClassFrame) {
+ // The frame is found, get its name
+ // The name will be used later by the findBone function to get
+ // a pointer to the target frame
+ if (_targetFrame) {
+ BaseEngine::LOG(0, "Animation frame name reference duplicated");
+ return false;
+ }
+
+ // get name
+ result = XModel::loadName(_targetName, xobj);
+ if (!result) {
+ BaseEngine::LOG(0, "Error retrieving frame name while loading animation");
+ return false;
+ }
+ }
} else {
- lexer.advanceOnOpenBraces();
+ // a data object is found, get its type
+ result = xobj->getType(objectType);
+ if (!result)
+ return false;
+
+ if (objectType == kXClassAnimationKey) {
+ // an animation key is found, load the data
+ XAnimationKeyObject *animationKey = xobj->getXAnimationKeyObject();
+ if (!animationKey)
+ return false;
+ result = loadAnimationKeyData(animationKey);
+ if (!result)
+ return false;
+ } else if (objectType == kXClassAnimationOptions) {
+ XAnimationOptionsObject *animationOptions = xobj->getXAnimationOptionsObject();
+ if (!animationOptions)
+ return false;
+ result = loadAnimationOptionData(animationOptions, parentAnimSet);
+ if (!result)
+ return false;
+ }
}
- bool ret = true;
+ return true;
+}
- while (!lexer.eof()) {
- if (lexer.tokenIsIdentifier("AnimationKey")) {
- lexer.advanceToNextToken();
- lexer.advanceToNextToken(); // skip name
- lexer.advanceOnOpenBraces();
+//////////////////////////////////////////////////////////////////////////
+bool Animation::loadAnimationOptionData(XAnimationOptionsObject *animationOptionData, AnimationSet *parentAnimSet) {
+ if (animationOptionData->_openclosed && parentAnimSet)
+ parentAnimSet->_looping = true;
- int keyType = lexer.readInt();
- int keyCount = lexer.readInt();
+ return true;
+}
- switch (keyType) {
- case 0:
- loadRotationKeyData(lexer, keyCount);
- break;
- case 1:
- loadScaleKeyData(lexer, keyCount);
- break;
- case 2:
- loadPositionKeyData(lexer, keyCount);
- break;
- case 3:
- case 4:
- loadMatrixKeyData(lexer, keyCount);
- break;
- default:
- warning("Animation::loadFromX unknown key type encountered");
+//////////////////////////////////////////////////////////////////////////
+bool Animation::loadAnimationKeyData(XAnimationKeyObject *animationKey) {
+ // get the type and count of the key
+ uint32 keyType = animationKey->_keyType;
+ uint32 numKeys = animationKey->_numKeys;
+
+ if (keyType == 0) { // rotation key
+ if (_rotKeys.size() != 0) {
+ BaseEngine::LOG(0, "Rotation key duplicated");
+ return false;
+ }
+
+ for (uint32 key = 0; key < numKeys; key++) {
+ const XTimedFloatKeys *fileRotKey = &animationKey->_keys[key];
+ assert(fileRotKey->_numTfkeys == 4);
+
+ BoneRotationKey *rotKey = new BoneRotationKey;
+ rotKey->_time = fileRotKey->_time;
+ // NOTE x files are w x y z and QUATERNIONS are x y z w
+ rotKey->_rotation.w() = fileRotKey->_tfkeys[0];
+ rotKey->_rotation.x() = fileRotKey->_tfkeys[1];
+ rotKey->_rotation.y() = fileRotKey->_tfkeys[2];
+ // mirror z component
+ rotKey->_rotation.z() = -fileRotKey->_tfkeys[3];
+
+ _rotKeys.push_back(rotKey);
+ }
+ } else if (keyType == 1) { // scale key
+ if (_scaleKeys.size() != 0) {
+ BaseEngine::LOG(0, "Scale key duplicated");
+ return false;
+ }
+
+ for (uint32 key = 0; key < numKeys; key++) {
+ const XTimedFloatKeys *fileScaleKey = &animationKey->_keys[key];
+ assert(fileScaleKey->_numTfkeys == 3);
+
+ BoneScaleKey *scaleKey = new BoneScaleKey;
+ scaleKey->_time = fileScaleKey->_time;
+ for (uint i = 0; i < fileScaleKey->_numTfkeys; ++i) {
+ scaleKey->_scale.getData()[i] = fileScaleKey->_tfkeys[i];
}
- lexer.advanceToNextToken(); // skip closed braces
-
- } else if (lexer.tokenIsIdentifier("AnimationOptions")) {
- lexer.advanceToNextToken();
- lexer.advanceToNextToken();
- lexer.advanceOnOpenBraces();
-
- // I think we can ignore these for the moment
- lexer.readInt(); // whether animation is open or closed
- lexer.readInt(); // position quality
- lexer.advanceToNextToken(); // skip closed braces
- } else if (lexer.tokenIsOfType(OPEN_BRACES)) {
- // this is a reference to a frame/bone, given as an identifier
- lexer.advanceToNextToken();
- _targetName = lexer.readString();
- } else if (lexer.tokenIsIdentifier("Animation")) {
- // pass it up
- break;
- } else if (lexer.reachedClosedBraces()) {
- lexer.advanceToNextToken(); // skip closed braces
- break;
- } else {
- warning("Animation::loadFromX unexpected token encounterd");
- ret = false;
- break;
+ _scaleKeys.push_back(scaleKey);
}
- }
+ } else if (keyType == 2) { // position key
+ if (_posKeys.size() != 0) {
+ BaseEngine::LOG(0, "Position key duplicated");
+ return false;
+ }
+
+ for (uint32 key = 0; key < numKeys; key++) {
+ const XTimedFloatKeys *filePosKey = &animationKey->_keys[key];
+ assert(filePosKey->_numTfkeys == 3);
- return ret;
+ BonePositionKey *posKey = new BonePositionKey;
+ posKey->_time = filePosKey->_time;
+ for (uint i = 0; i < filePosKey->_numTfkeys; ++i) {
+ posKey->_pos.getData()[i] = filePosKey->_tfkeys[i];
+ }
+
+ // mirror Z
+ posKey->_pos.getData()[2] *= -1.0f;
+
+ _posKeys.push_back(posKey);
+ }
+ } else if (keyType == 4) { // matrix key
+ if (_rotKeys.size() != 0 || _scaleKeys.size() != 0 || _posKeys.size() != 0) {
+ BaseEngine::LOG(0, "Matrix key duplicated");
+ return false;
+ }
+
+ for (uint32 key = 0; key < numKeys; key++) {
+ const XTimedFloatKeys *fileMatrixKey = &animationKey->_keys[key];
+ uint32 time = fileMatrixKey->_time;
+ assert(fileMatrixKey->_numTfkeys == 16);
+
+ Math::Matrix4 keyData;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ keyData(c, r) = fileMatrixKey->_tfkeys[r * 4 + c];
+ }
+ }
+
+ // mirror at orign
+ keyData(2, 3) *= -1.0f;
+
+ // mirror base vectors
+ keyData(2, 0) *= -1.0f;
+ keyData(2, 1) *= -1.0f;
+
+ // change handedness
+ keyData(0, 2) *= -1.0f;
+ keyData(1, 2) *= -1.0f;
+
+ Math::Vector3d translation = keyData.getPosition();
+
+ Math::Vector3d scale;
+ scale.x() = keyData(0, 0) * keyData(0, 0) + keyData(1, 0) * keyData(1, 0) + keyData(2, 0) * keyData(2, 0);
+ scale.x() = sqrtf(scale.x());
+ scale.y() = keyData(0, 1) * keyData(0, 1) + keyData(1, 1) * keyData(1, 1) + keyData(2, 1) * keyData(2, 1);
+ scale.y() = sqrtf(scale.y());
+ scale.z() = keyData(0, 2) * keyData(0, 2) + keyData(1, 2) * keyData(1, 2) + keyData(2, 2) * keyData(2, 2);
+ scale.z() = sqrtf(scale.z());
+
+ Math::Quaternion rotation;
+ rotation.fromMatrix(keyData.getRotation());
+
+ BonePositionKey *positionKey = new BonePositionKey;
+ BoneScaleKey *scaleKey = new BoneScaleKey;
+ BoneRotationKey *rotationKey = new BoneRotationKey;
+
+ positionKey->_time = time;
+ scaleKey->_time = time;
+ rotationKey->_time = time;
+
+ positionKey->_pos = translation;
+ scaleKey->_scale = scale;
+ rotationKey->_rotation = rotation;
+
+ _posKeys.push_back(positionKey);
+ _scaleKeys.push_back(scaleKey);
+ _rotKeys.push_back(rotationKey);
+ }
+ } else {
+ // the type is unknown, report the error
+ BaseEngine::LOG(0, "Unexpected animation key type (%d)", keyType);
+ }
+ return true;
}
//////////////////////////////////////////////////////////////////////////
@@ -307,146 +433,4 @@ uint32 Animation::getTotalTime() {
return totalTime;
}
-// try to put these functions into a template?
-bool Animation::loadRotationKeyData(XFileLexer &lexer, int count) {
- for (int keyIndex = 0; keyIndex < count; ++keyIndex) {
- BoneRotationKey *key = new BoneRotationKey;
- key->_time = lexer.readInt();
-
- int floatCount = lexer.readInt();
- assert(floatCount == 4);
-
- // .X file format puts the w coordinate first
- key->_rotation.w() = lexer.readFloat();
- key->_rotation.x() = lexer.readFloat();
- key->_rotation.y() = lexer.readFloat();
- // mirror z component
- key->_rotation.z() = -lexer.readFloat();
-
- lexer.skipTerminator(); // skip semicolon
-
- if (lexer.tokenIsOfType(SEMICOLON) || lexer.tokenIsOfType(COMMA)) {
- lexer.advanceToNextToken(); // skip closed braces
- }
-
- _rotKeys.push_back(key);
- }
-
- return true;
-}
-
-bool Animation::loadScaleKeyData(XFileLexer &lexer, int count) {
- for (int keyIndex = 0; keyIndex < count; ++keyIndex) {
- BoneScaleKey *key = new BoneScaleKey;
- key->_time = lexer.readInt();
-
- int floatCount = lexer.readInt();
- assert(floatCount == 3);
-
- for (int i = 0; i < floatCount; ++i) {
- key->_scale.getData()[i] = lexer.readFloat();
- }
-
- lexer.skipTerminator(); // skip semicolon
-
- if (lexer.tokenIsOfType(SEMICOLON) || lexer.tokenIsOfType(COMMA)) {
- lexer.advanceToNextToken(); // skip closed braces
- }
-
- _scaleKeys.push_back(key);
- }
-
- return true;
-}
-
-bool Animation::loadPositionKeyData(XFileLexer &lexer, int count) {
- for (int keyIndex = 0; keyIndex < count; ++keyIndex) {
- BonePositionKey *key = new BonePositionKey;
- key->_time = lexer.readInt();
-
- int floatCount = lexer.readInt();
- assert(floatCount == 3);
-
- for (int i = 0; i < floatCount; ++i) {
- key->_pos.getData()[i] = lexer.readFloat();
- }
-
- key->_pos.getData()[2] *= -1.0f;
-
- lexer.skipTerminator(); // skip semicolon
-
- if (lexer.tokenIsOfType(SEMICOLON) || lexer.tokenIsOfType(COMMA)) {
- lexer.advanceToNextToken(); // skip closed braces
- }
-
- _posKeys.push_back(key);
- }
-
- return true;
-}
-
-bool Animation::loadMatrixKeyData(XFileLexer &lexer, int count) {
- for (int keyIndex = 0; keyIndex < count; ++keyIndex) {
- uint32 time = lexer.readInt();
- int floatCount = lexer.readInt();
- assert(floatCount == 16);
-
- Math::Matrix4 keyData;
-
- for (int r = 0; r < 4; ++r) {
- for (int c = 0; c < 4; ++c) {
- keyData(c, r) = lexer.readFloat();
- }
- }
-
- // mirror at orign
- keyData(2, 3) *= -1.0f;
-
- // mirror base vectors
- keyData(2, 0) *= -1.0f;
- keyData(2, 1) *= -1.0f;
-
- // change handedness
- keyData(0, 2) *= -1.0f;
- keyData(1, 2) *= -1.0f;
-
- Math::Vector3d translation = keyData.getPosition();
-
- Math::Vector3d scale;
- scale.x() = keyData(0, 0) * keyData(0, 0) + keyData(1, 0) * keyData(1, 0) + keyData(2, 0) * keyData(2, 0);
- scale.x() = sqrtf(scale.x());
- scale.y() = keyData(0, 1) * keyData(0, 1) + keyData(1, 1) * keyData(1, 1) + keyData(2, 1) * keyData(2, 1);
- scale.y() = sqrtf(scale.y());
- scale.z() = keyData(0, 2) * keyData(0, 2) + keyData(1, 2) * keyData(1, 2) + keyData(2, 2) * keyData(2, 2);
- scale.z() = sqrtf(scale.z());
-
- Math::Quaternion rotation;
- rotation.fromMatrix(keyData.getRotation());
-
- BonePositionKey *positionKey = new BonePositionKey;
- BoneScaleKey *scaleKey = new BoneScaleKey;
- BoneRotationKey *rotationKey = new BoneRotationKey;
-
- positionKey->_time = time;
- scaleKey->_time = time;
- rotationKey->_time = time;
-
- positionKey->_pos = translation;
- scaleKey->_scale = scale;
- rotationKey->_rotation = rotation;
-
- _posKeys.push_back(positionKey);
- _scaleKeys.push_back(scaleKey);
- _rotKeys.push_back(rotationKey);
-
- lexer.skipTerminator(); // skip semicolon
-
- if (lexer.tokenIsOfType(SEMICOLON) || lexer.tokenIsOfType(COMMA)) {
- lexer.advanceToNextToken(); // skip closed braces
- }
- }
-
- return true;
-}
-
} // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xanimation.h b/engines/wintermute/base/gfx/xanimation.h
index 659d9f5eee4..650a85b34c8 100644
--- a/engines/wintermute/base/gfx/xanimation.h
+++ b/engines/wintermute/base/gfx/xanimation.h
@@ -38,14 +38,16 @@ namespace Wintermute {
class FrameNode;
class AnimationSet;
-class XFileLexer;
+class XFileData;
+struct XAnimationKeyObject;
+struct XAnimationOptionsObject;
class Animation : public BaseClass {
public:
Animation(BaseGame *inGame);
virtual ~Animation();
- bool loadFromX(XFileLexer &lexer, AnimationSet *parentAnimationSet);
+ bool load(XFileData *xobj, AnimationSet *parentAnimSet);
bool findBone(FrameNode *rootFrame);
bool update(int slot, uint32 localTime, float animLerpValue);
@@ -79,10 +81,8 @@ protected:
BaseArray<BoneScaleKey *> _scaleKeys;
private:
- bool loadRotationKeyData(XFileLexer &lexer, int count);
- bool loadScaleKeyData(XFileLexer &lexer, int count);
- bool loadPositionKeyData(XFileLexer &lexer, int count);
- bool loadMatrixKeyData(XFileLexer &lexer, int count);
+ bool loadAnimationKeyData(XAnimationKeyObject *animationKey);
+ bool loadAnimationOptionData(XAnimationOptionsObject *animationSet, AnimationSet *parentAnimSet);
};
} // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xanimation_set.cpp b/engines/wintermute/base/gfx/xanimation_set.cpp
index 02e24da98eb..b022d2749f2 100644
--- a/engines/wintermute/base/gfx/xanimation_set.cpp
+++ b/engines/wintermute/base/gfx/xanimation_set.cpp
@@ -28,7 +28,6 @@
#include "engines/wintermute/base/base_game.h"
#include "engines/wintermute/base/gfx/xanimation_set.h"
#include "engines/wintermute/base/gfx/xmodel.h"
-#include "engines/wintermute/base/gfx/xloader.h"
#include "engines/wintermute/dcgf.h"
namespace Wintermute {
@@ -56,39 +55,6 @@ AnimationSet::~AnimationSet() {
_events.clear();
}
-bool AnimationSet::loadFromX(XFileLexer &lexer, const Common::String &filename) {
- if (lexer.tokenIsIdentifier()) {
- setName(lexer.tokenToString().c_str());
- lexer.advanceToNextToken();
- } else {
- Common::String name = filename + "_animation";
- setName(name.c_str());
- }
-
- lexer.advanceToNextToken();
-
- bool ret = true;
-
- while (!lexer.eof()) {
- if (lexer.tokenIsIdentifier("Animation")) {
- lexer.advanceToNextToken();
-
- Animation *animation = new Animation(_gameRef);
- animation->loadFromX(lexer, this);
- _animations.add(animation);
- } else if (lexer.reachedClosedBraces()) {
- lexer.advanceToNextToken(); // skip closed braces
- break;
- } else {
- warning("AnimationSet::loadFromX unexpected token");
- ret = false;
- break;
- }
- }
-
- return ret;
-}
-
//////////////////////////////////////////////////////////////////////////
bool AnimationSet::findBones(FrameNode *rootFrame) {
for (uint32 i = 0; i < _animations.size(); i++) {
diff --git a/engines/wintermute/base/gfx/xanimation_set.h b/engines/wintermute/base/gfx/xanimation_set.h
index 54e5b5db314..57eac886d82 100644
--- a/engines/wintermute/base/gfx/xanimation_set.h
+++ b/engines/wintermute/base/gfx/xanimation_set.h
@@ -38,7 +38,6 @@
namespace Wintermute {
class XModel;
-class XFileLexer;
class AnimationSet : public BaseNamedObject {
public:
@@ -75,7 +74,6 @@ public:
AnimationSet(BaseGame *inGame, XModel *model);
virtual ~AnimationSet();
- bool loadFromX(XFileLexer &lexer, const Common::String &filename);
bool findBones(FrameNode *rootFrame);
bool addAnimation(Animation *anim);
bool addEvent(AnimationEvent *event);
diff --git a/engines/wintermute/base/gfx/xfile.cpp b/engines/wintermute/base/gfx/xfile.cpp
new file mode 100644
index 00000000000..81e65bc591d
--- /dev/null
+++ b/engines/wintermute/base/gfx/xfile.cpp
@@ -0,0 +1,89 @@
+/* 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/>.
+ *
+ */
+
+/*
+ * This file is based on WME.
+ * http://dead-code.org/redir.php?target=wme
+ * Copyright (c) 2003-2013 Jan Nedoma and contributors
+ */
+
+#include "engines/wintermute/base/base_file_manager.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/base/gfx/xfile.h"
+#include "engines/wintermute/dcgf.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+XFile::XFile(BaseGame *inGame) : BaseClass(inGame) {
+ _xfile = nullptr;
+}
+
+//////////////////////////////////////////////////////////////////////////
+XFile::~XFile() {
+ closeFile();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool XFile::closeFile() {
+ delete _xfile;
+ _xfile = nullptr;
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool XFile::openFile(const Common::String &filename) {
+ closeFile();
+
+ // load file
+ uint32 size;
+ byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename, &size);
+ if (!buffer) {
+ closeFile();
+ return false;
+ }
+
+ _xfile = new XFileLoader();
+ if (!_xfile) {
+ delete[] buffer;
+ return false;
+ }
+
+ bool res = _xfile->load(buffer, size);
+ delete[] buffer;
+ if (!res) {
+ BaseEngine::LOG(0, "Error loading X file '%s'", filename.c_str());
+ return false;
+ }
+
+ // create enum object
+ if (!res || !_xfile->createEnumObject(_xenum)) {
+ BaseEngine::LOG(res, "Error creating XFile enum object for '%s'", filename.c_str());
+ closeFile();
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xfile.h b/engines/wintermute/base/gfx/xfile.h
new file mode 100644
index 00000000000..6c349dc797d
--- /dev/null
+++ b/engines/wintermute/base/gfx/xfile.h
@@ -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/>.
+ *
+ */
+
+/*
+ * This file is based on WME.
+ * http://dead-code.org/redir.php?target=wme
+ * Copyright (c) 2003-2013 Jan Nedoma and contributors
+ */
+
+#ifndef WINTERMUTE_XFILE_H
+#define WINTERMUTE_XFILE_H
+
+#include "engines/wintermute/base/base.h"
+#include "engines/wintermute/base/gfx/xfile_loader.h"
+
+namespace Wintermute {
+
+class XFile : public BaseClass {
+public:
+ XFile(BaseGame *inGame);
+ virtual ~XFile();
+
+ bool openFile(const Common::String &filename);
+ bool closeFile();
+
+ XFileEnumObject getEnum() {
+ return _xenum;
+ }
+
+private:
+
+ XFileLoader *_xfile;
+ XFileEnumObject _xenum;
+};
+
+} // namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/xfile_loader.cpp b/engines/wintermute/base/gfx/xfile_loader.cpp
new file mode 100644
index 00000000000..3ec4537be9d
--- /dev/null
+++ b/engines/wintermute/base/gfx/xfile_loader.cpp
@@ -0,0 +1,1510 @@
+/* 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/>.
+ *
+ */
+
+/*
+ * Partially based on XFile parser code from Wine sources.
+ * Copyright 2008 Christian Costa
+ */
+
+#include "common/endian.h"
+#include "common/str.h"
+#include "common/util.h"
+#include "common/zlib.h"
+
+#include "wintermute/base/gfx/xfile_loader.h"
+
+namespace Wintermute {
+
+typedef struct {
+ const char *className;
+ const XClassType type;
+} XClassEntries;
+
+// strings must be in lower case
+static const XClassEntries gXClasses[] = {
+ { "skinweights", kXClassSkinWeights },
+ { "frame", kXClassFrame },
+ { "frametransformmatrix", kXClassFrameTransformMatrix },
+ { "animationkey", kXClassAnimationKey },
+ { "animationoptions", kXClassAnimationOptions },
+ { "mesh", kXClassMesh },
+ { "meshnormals", kXClassMeshNormals },
+ { "animation", kXClassAnimation },
+ { "animationset", kXClassAnimationSet },
+ { "meshvertexcolors", kXClassMeshVertexColors },
+ { "meshtexturecoords", kXClassMeshTextureCoords },
+ { "meshmateriallist", kXClassMeshMaterialList },
+ { "vertexduplicationindices", kXClassVertexDuplicationIndices },
+ { "material", kXClassMaterial },
+ { "texturefilename", kXClassTextureFilename },
+ { "xskinmeshheader", kXClassSkinMeshHeader },
+ { "animtickspersecond", kXClassAnimTicksPerSecond },
+ { "decldata", kXClassDeclData },
+ { "fvfdata", kXClassFVFData },
+};
+
+// first string expected in lower case
+FORCEINLINE static int XFileLoader_strncmp(const char *s1, const char *s2, uint n) {
+ byte l1, l2;
+ do {
+ if (n-- == 0)
+ return 0;
+
+ l1 = (byte)*s1++;
+ l2 = (byte)*s2++;
+ l2 = tolower(l2);
+ } while (l1 == l2 && l1 != 0);
+ return l1 - l2;
+}
+
+// first string expected in lower case
+FORCEINLINE static int XFileLoader_strcmp(const char *s1, const char *s2) {
+ byte l1, l2;
+ do {
+ l1 = (byte)*s1++;
+ l2 = (byte)*s2++;
+ l2 = tolower(l2);
+ } while (l1 == l2 && l1 != 0);
+ return l1 - l2;
+}
+
+XFileLoader::XFileLoader() {
+ init();
+}
+
+XFileLoader::~XFileLoader() {
+ deinit();
+}
+
+void XFileLoader::init() {
+ deinit();
+
+ _decompBuffer = nullptr;
+ _tokenPresent = false;
+ _isText = false;
+ _listSeparator = false;
+ _listTypeFloat = false;
+ _listNbElements = 0;
+
+ _initialised = true;
+}
+
+void XFileLoader::deinit() {
+ delete _decompBuffer;
+ for (uint i = 0; i < _xobjects.size(); i++) {
+ if (_xobjects[i]->_object && !_xobjects[i]->_targetObject) {
+ _xobjects[i]->deinit();
+ }
+ _xobjects[i]->_object = nullptr;
+ delete _xobjects[i];
+ }
+ _xobjects.clear();
+}
+
+bool XFileLoader::createEnumObject(XFileEnumObject &xobj) {
+ if (_initialised) {
+ xobj._file = this;
+ return true;
+ }
+ return false;
+}
+
+bool XFileLoader::isSpace(char c) {
+ switch (c) {
+ case ' ':
+ case '\t':
+ case 0x0D:
+ case 0x0A:
+ case 0x00:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+bool XFileLoader::isOperator(char c) {
+ switch (c) {
+ case ',':
+ case ';':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case '(':
+ case ')':
+ case '<':
+ case '>':
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+bool XFileLoader::isSeparator(char c) {
+ return isSpace(c) || isOperator(c);
+}
+
+bool XFileLoader::isPrimitiveType(XTokenType token) {
+ switch (token) {
+ case XTOKEN_DWORD:
+ case XTOKEN_FLOAT:
+ case XTOKEN_WORD:
+ case XTOKEN_CSTRING:
+ case XTOKEN_DOUBLE:
+ case XTOKEN_CHAR:
+ case XTOKEN_UCHAR:
+ case XTOKEN_SWORD:
+ case XTOKEN_SDWORD:
+ case XTOKEN_VOID:
+ case XTOKEN_LPSTR:
+ case XTOKEN_UNICODE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool XFileLoader::readChar(char &c) {
+ if (_bufferLeft == 0)
+ return false;
+ c = *_buffer;
+ _buffer += 1;
+ _bufferLeft -= 1;
+ return true;
+}
+
+bool XFileLoader::readBytes(void *data, uint32 size) {
+ if (_bufferLeft < size)
+ return false;
+ memcpy(data, _buffer, size);
+ _buffer += size;
+ _bufferLeft -= size;
+ return true;
+}
+
+bool XFileLoader::readLE16(uint16 *data) {
+ if (_bufferLeft < 2)
+ return false;
+ *data = READ_LE_UINT16(_buffer);
+ _buffer += 2;
+ _bufferLeft -= 2;
+ return true;
+}
+
+bool XFileLoader::readLE32(uint32 *data) {
+ if (_bufferLeft < 4)
+ return false;
+ *data = READ_LE_UINT32(_buffer);
+ _buffer += 4;
+ _bufferLeft -= 4;
+ return true;
+}
+
+bool XFileLoader::readBE32(uint32 *data) {
+ if (_bufferLeft < 4)
+ return false;
+ *data = READ_BE_UINT32(_buffer);
+ _buffer += 4;
+ _bufferLeft -= 4;
+ return true;
+}
+
+void XFileLoader::rewindBytes(uint32 size) {
+ _buffer -= size;
+ _bufferLeft += size;
+}
+
+static struct keywords {
+ const char *keyword;
+ const uint len;
+ const XTokenType token;
+} XKeywords[] = {
+ { "dword", sizeof("dword") - 1, XTOKEN_DWORD },
+ { "float", sizeof("float") - 1, XTOKEN_FLOAT },
+ { "array", sizeof("array") - 1, XTOKEN_ARRAY },
+ { "template", sizeof("template") - 1, XTOKEN_TEMPLATE },
+ { "word", sizeof("word") - 1, XTOKEN_WORD },
+ { "cstring", sizeof("cstring") - 1, XTOKEN_CSTRING },
+ { "char", sizeof("char") - 1, XTOKEN_CHAR },
+ { "uchar", sizeof("uchar") - 1, XTOKEN_UCHAR },
+ { "sword", sizeof("sword") - 1, XTOKEN_SWORD },
+ { "sdword", sizeof("sdword") - 1, XTOKEN_SDWORD },
+ { "void", sizeof("void") - 1, XTOKEN_VOID },
+ { "string", sizeof("string") - 1, XTOKEN_LPSTR },
+};
+
+// string expected in lower case
+bool XFileLoader::isKeyword(const char *keyword, uint len) {
+ if (XFileLoader_strncmp(keyword, (char *)_buffer, len)) {
+ return false;
+ }
+ _buffer += len;
+ _bufferLeft -= len;
+
+ char tmp;
+ if (!readChar(tmp))
+ return true;
+ if (isSeparator(tmp)) {
+ rewindBytes(1);
+ return true;
+ }
+
+ rewindBytes(len + 1);
+ return false;
+}
+
+XTokenType XFileLoader::getKeywordToken() {
+ for (int i = 0; i < ARRAYSIZE(XKeywords); i++) {
+ if (isKeyword(XKeywords[i].keyword, XKeywords[i].len))
+ return XKeywords[i].token;
+ }
+ return XTOKEN_NONE;
+}
+
+bool XFileLoader::isGuid() {
+ if (_bufferLeft < 38 || *_buffer != '<')
+ return false;
+
+ char tmp[50];
+ uint32 pos = 1;
+ tmp[0] = '<';
+ while (pos < sizeof(tmp) - 2 && *(_buffer + pos) != '>') {
+ tmp[pos] = *(_buffer + pos);
+ pos++;
+ }
+ tmp[pos++] = '>';
+ tmp[pos] = 0;
+ if (pos != 38) {
+ warning("XFileLoader: Wrong guid %s (%d)", tmp, pos);
+ return false;
+ }
+ _buffer += pos;
+ _bufferLeft -= pos;
+ return true;
+}
+
+bool XFileLoader::isName() {
+ char tmp[XMAX_STRING_LEN];
+ uint32 pos = 0;
+ char c;
+ bool error = false;
+
+ while (pos < _bufferLeft && !isSeparator(c = *(_buffer + pos))) {
+ if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || (c == '_') || (c == '-')))
+ error = true;
+ if (pos < sizeof(tmp))
+ tmp[pos] = c;
+ pos++;
+ }
+ tmp[MIN(pos, (uint32)sizeof(tmp) - 1)] = 0;
+
+ if (error) {
+ warning("XFileLoader: Wrong name %s", tmp);
+ return false;
+ }
+
+ _buffer += pos;
+ _bufferLeft -= pos;
+
+ Common::strlcpy(_currentToken._textVal, tmp, XMAX_STRING_LEN);
+
+ return true;
+}
+
+bool XFileLoader::isFloat() {
+ char tmp[XMAX_STRING_LEN];
+ uint32 pos = 0;
+ char c;
+ bool dot = false;
+
+ while (pos < _bufferLeft && !isSeparator(c = *(_buffer + pos))) {
+ if (!((!pos && (c == '-')) || ((c >= '0') && (c <= '9')) || (!dot && (c == '.'))))
+ return false;
+ if (c == '.')
+ dot = true;
+ if (pos < sizeof(tmp))
+ tmp[pos] = c;
+ pos++;
+ }
+ tmp[MIN(pos, (uint32)sizeof(tmp) - 1)] = 0;
+
+ _buffer += pos;
+ _bufferLeft -= pos;
+
+ _currentToken._floatVal = atof(tmp);
+
+ return true;
+}
+
+bool XFileLoader::isInteger() {
+ char tmp[XMAX_STRING_LEN];
+ uint32 pos = 0;
+ char c;
+
+ while (pos < _bufferLeft && !isSeparator(c = *(_buffer + pos))) {
+ if (!((c >= '0') && (c <= '9')))
+ return false;
+ if (pos < sizeof(tmp))
+ tmp[pos] = c;
+ pos++;
+ }
+ tmp[MIN(pos, (uint32)sizeof(tmp) - 1)] = 0;
+
+ _buffer += pos;
+ _bufferLeft -= pos;
+
+ _currentToken._integerVal = atoi(tmp);
+
+ return true;
+}
+
+bool XFileLoader::isString() {
+ char tmp[XMAX_STRING_LEN];
+ uint32 pos = 0;
+ char c;
+ bool ok = false;
+
+ if (*_buffer != '"')
+ return false;
+
+ while ((pos + 1) < _bufferLeft) {
+ c = *(_buffer + pos + 1);
+ if (c == '"') {
+ ok = true;
+ break;
+ }
+ if (pos < sizeof(tmp))
+ tmp[pos] = c;
+ pos++;
+ }
+ tmp[MIN((int32)pos, (int32)sizeof(tmp) - 1)] = 0;
+
+ if (!ok) {
+ warning("XFileLoader: Wrong string %s", tmp);
+ return false;
+ }
+
+ _buffer += pos + 2;
+ _bufferLeft -= pos + 2;
+
+ Common::strlcpy(_currentToken._textVal, tmp, XMAX_STRING_LEN);
+
+ return true;
+}
+
+void XFileLoader::parseToken() {
+ if (_isText) {
+ char current;
+
+ while (true) {
+ if (!readChar(current)) {
+ _currentToken._type = XTOKEN_NONE;
+ return;
+ }
+ if (isSpace(current)) {
+ continue;
+ } else if (current == '/' || current == '#') {
+ if (current == '/') {
+ if (!readChar(current)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ if (current != '/') {
+ _currentToken._type = XTOKEN_ERROR;
+ warning("XFileLoader: Unknown token %c", current);
+ return;
+ }
+ }
+ while (current != 0xA) {
+ if (!readChar(current)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ }
+ continue;
+ }
+ break;
+ }
+
+ switch (current) {
+ case ';':
+ _currentToken._type = XTOKEN_SEMICOLON;
+ return;
+ case ',':
+ _currentToken._type = XTOKEN_COMMA;
+ return;
+ case '{':
+ _currentToken._type = XTOKEN_OBRACE;
+ return;
+ case '}':
+ _currentToken._type = XTOKEN_CBRACE;
+ return;
+ case '(':
+ _currentToken._type = XTOKEN_OPAREN;
+ return;
+ case ')':
+ _currentToken._type = XTOKEN_CPAREN;
+ return;
+ case '[':
+ _currentToken._type = XTOKEN_OBRACKET;
+ return;
+ case ']':
+ _currentToken._type = XTOKEN_CBRACKET;
+ return;
+ case '>':
+ _currentToken._type = XTOKEN_CANGLE;
+ return;
+ case '.':
+ _currentToken._type = XTOKEN_DOT;
+ return;
+ default:
+ rewindBytes(1);
+ _currentToken._type = getKeywordToken();
+ if (_currentToken._type != XTOKEN_NONE) {
+ return;
+ } else if (isGuid()) {
+ _currentToken._type = XTOKEN_GUID;
+ } else if (isInteger()) {
+ _currentToken._type = XTOKEN_INTEGER;
+ } else if (isFloat()) {
+ _currentToken._type = XTOKEN_FLOAT;
+ } else if (isString()) {
+ _currentToken._type = XTOKEN_STRING;
+ } else if (isName()) {
+ _currentToken._type = XTOKEN_NAME;
+ } else {
+ _currentToken._type = XTOKEN_ERROR;
+ warning("XFileLoader: Unknown token %c", current);
+ }
+ }
+ } else {
+ if (!_listNbElements) {
+ uint16 type;
+ if (!readLE16(&type)) {
+ _currentToken._type = XTOKEN_NONE;
+ return;
+ }
+ _currentToken._type = (XTokenType)type;
+
+ if (_currentToken._type == XTOKEN_INTEGER_LIST) {
+ if (!readLE32(&_listNbElements)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ _currentToken._type = XTOKEN_INTEGER;
+ _listTypeFloat = false;
+ } else if (_currentToken._type == XTOKEN_FLOAT_LIST) {
+ if (!readLE32(&_listNbElements)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ _currentToken._type = XTOKEN_FLOAT;
+ _listTypeFloat = true;
+ }
+ }
+
+ if (_listNbElements) {
+ if (_listSeparator) {
+ _listNbElements--;
+ _listSeparator = false;
+ _currentToken._type = XTOKEN_COMMA;
+ } else {
+ uint32 value;
+ if (!readLE32(&value)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ _listSeparator = true;
+ if (_listTypeFloat) {
+ _currentToken._type = XTOKEN_FLOAT;
+ _currentToken._floatVal = *(float *)&value;
+ } else {
+ _currentToken._type = XTOKEN_INTEGER;
+ _currentToken._integerVal = value;
+ }
+ }
+ return;
+ }
+
+ switch (_currentToken._type) {
+ case XTOKEN_NAME: {
+ uint32 count;
+ if (!readLE32(&count)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ char name[XMAX_NAME_LEN];
+ if (!readBytes(name, count)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ name[count] = 0;
+ assert(count < XMAX_NAME_LEN);
+ Common::strlcpy(_currentToken._textVal, name, XMAX_NAME_LEN);
+ }
+ break;
+ case XTOKEN_INTEGER: {
+ uint32 integer;
+ if (!readLE32(&integer)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ _currentToken._integerVal = integer;
+ }
+ break;
+ case XTOKEN_GUID: {
+ byte classId[16];
+ if (!readBytes(&classId, 16)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ }
+ break;
+ case XTOKEN_STRING: {
+ uint32 count;
+ if (!readLE32(&count)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ char string[XMAX_NAME_LEN];
+ if (!readBytes(string, count)) {
+ _currentToken._type = XTOKEN_ERROR;
+ return;
+ }
+ string[count] = 0;
+ assert(count < XMAX_NAME_LEN);
+ Common::strlcpy(_currentToken._textVal, string, XMAX_NAME_LEN);
+ }
+ break;
+ case XTOKEN_COMMA:
+ break;
+ case XTOKEN_SEMICOLON:
+ break;
+ case XTOKEN_OBRACE:
+ break;
+ case XTOKEN_CBRACE:
+ break;
+ case XTOKEN_DWORD:
+ break;
+ case XTOKEN_FLOAT:
+ break;
+ case XTOKEN_OBRACKET:
+ break;
+ case XTOKEN_CBRACKET:
+ break;
+ case XTOKEN_ARRAY:
+ break;
+ case XTOKEN_WORD:
+ break;
+ case XTOKEN_CSTRING:
+ break;
+ case XTOKEN_OPAREN:
+ break;
+ case XTOKEN_CPAREN:
+ break;
+ case XTOKEN_OANGLE:
+ break;
+ case XTOKEN_CANGLE:
+ break;
+ case XTOKEN_DOT:
+ break;
+ case XTOKEN_TEMPLATE:
+ break;
+ case XTOKEN_DOUBLE:
+ break;
+ case XTOKEN_CHAR:
+ break;
+ case XTOKEN_UCHAR:
+ break;
+ case XTOKEN_SWORD:
+ break;
+ case XTOKEN_SDWORD:
+ break;
+ case XTOKEN_VOID:
+ break;
+ case XTOKEN_LPSTR:
+ break;
+ case XTOKEN_UNICODE:
+ break;
+ default:
+ _currentToken._type = XTOKEN_ERROR;
+ warning("XFileLoader::nextToken: Unknown token encountered");
+ return;
+ }
+ }
+}
+
+XTokenType XFileLoader::getToken() {
+ if (_tokenPresent) {
+ _tokenPresent = false;
+ return _currentToken._type;
+ }
+
+ parseToken();
+
+ return _currentToken._type;
+}
+
+XTokenType XFileLoader::checkToken() {
+ if (_tokenPresent)
+ return _currentToken._type;
+
+ parseToken();
+ _tokenPresent = true;
+
+ return _currentToken._type;
+}
+
+bool XFileLoader::skipSemicolonComma() {
+ if (checkToken() != XTOKEN_COMMA && checkToken() != XTOKEN_SEMICOLON) {
+ return false;
+ }
+ while (checkToken() == XTOKEN_SEMICOLON)
+ getToken();
+ if (checkToken() == XTOKEN_COMMA)
+ getToken();
+ return true;
+}
+
+bool XFileLoader::getInteger(uint32 &value) {
+ if (getToken() != XTOKEN_INTEGER) {
+ return false;
+ }
+ value = _currentToken._integerVal;
+ return skipSemicolonComma();
+}
+
+bool XFileLoader::getFloat(float &value) {
+ if (getToken() != XTOKEN_FLOAT) {
+ return false;
+ }
+ value = _currentToken._floatVal;
+ return skipSemicolonComma();
+}
+
+bool XFileLoader::getString(char *str, uint maxLen) {
+ if (getToken() != XTOKEN_STRING) {
+ return false;
+ }
+ uint len = strlen(_currentToken._textVal);
+ assert(maxLen > len);
+ Common::strlcpy(str, _currentToken._textVal, maxLen);
+ return skipSemicolonComma();
+}
+
+bool XFileLoader::decompressMsZipData() {
+#ifdef USE_ZLIB
+ bool error = false;
+
+ byte *compressedBlock = new byte[kCabInputmax];
+ byte *decompressedBlock = new byte[kCabBlockSize];
+
+ uint32 decompressedSize = 0;
+ if (!readLE32(&decompressedSize)) {
+ error = true;
+ } else {
+ decompressedSize -= 16;
+ }
+
+ uint32 decompressedPos = 0;
+ byte *decompressedData = new byte[decompressedSize];
+ if (!decompressedData)
+ error = true;
+
+ while (!error && _bufferLeft) {
+ uint16 uncompressedLen, compressedLen;
+ if (!readLE16(&uncompressedLen) || !readLE16(&compressedLen)) {
+ error = true;
+ break;
+ }
+
+ if (_bufferLeft == 0) {
+ break;
+ }
+
+ if (compressedLen > kCabInputmax || uncompressedLen > kCabBlockSize) {
+ error = true;
+ break;
+ }
+
+ if (!readBytes(compressedBlock, compressedLen)) {
+ error = true;
+ break;
+ }
+
+ if (compressedBlock[0] != 'C' || compressedBlock[1] != 'K') {
+ error = true;
+ break;
+ }
+
+ const byte *dict = decompressedPos ? decompressedBlock : nullptr;
+ bool decRes = Common::inflateZlibHeaderless(decompressedBlock, uncompressedLen, compressedBlock + 2, compressedLen - 2, dict, kCabBlockSize);
+ if (!decRes) {
+ error = true;
+ break;
+ }
+
+ memcpy(decompressedData + decompressedPos, decompressedBlock, uncompressedLen);
+ decompressedPos += uncompressedLen;
+ }
+ if (decompressedSize != decompressedPos)
+ error = true;
+
+ delete[] compressedBlock;
+ delete[] decompressedBlock;
+
+ if (!error) {
+ _decompBuffer = _buffer = decompressedData;
+ _bufferLeft = decompressedSize;
+ return true;
+ }
+
+ delete[] decompressedData;
+#endif
+
+ warning("XFileLoader: decompressMsZipData: Error decompressing data!");
+ return false;
+}
+
+bool XFileLoader::parseHeader() {
+ uint32 header[4];
+
+ for (int i = 0; i < 4; i++) {
+ if (!readBE32(&header[i])) {
+ warning("XFileLoader: bad file");
+ return false;
+ }
+ }
+
+ if (header[0] != MKTAG('x','o','f',' ')) {
+ warning("XFileLoader: bad file");
+ return false;
+ }
+
+ if (header[1] != MKTAG('0','3','0','2') &&
+ header[1] != MKTAG('0','3','0','3')) {
+ warning("XFileLoader: bad version");
+ return false;
+ }
+
+ if (header[2] != MKTAG('b','i','n',' ') &&
+ header[2] != MKTAG('t','x','t',' ') &&
+ header[2] != MKTAG('b','z','i','p') &&
+ header[2] != MKTAG('t','z','i','p')) {
+ warning("XFileLoader: file type unknown");
+ return false;
+ }
+
+ if (header[3] != MKTAG('0','0','3','2') &&
+ header[3] != MKTAG('0','0','6','4')) {
+ warning("XFileLoader: bad float size");
+ return false;
+ }
+
+ if (header[3] == MKTAG('0','0','6','4')) {
+ warning("XFileLoader: double float size is not supported");
+ return false;
+ }
+
+ _isText = header[2] == MKTAG('t','x','t',' ') ||
+ header[2] == MKTAG('t','z','i','p');
+
+ if (header[2] == MKTAG('b','z','i','p') ||
+ header[2] == MKTAG('t','z','i','p')) {
+ if (!decompressMsZipData()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool XFileLoader::parseTemplateOptionInfo() {
+ if (checkToken() == XTOKEN_DOT) {
+ getToken();
+ if (getToken() != XTOKEN_DOT)
+ return false;
+ if (getToken() != XTOKEN_DOT)
+ return false;
+ } else {
+ while (1) {
+ if (getToken() != XTOKEN_NAME)
+ return false;
+ if (checkToken() == XTOKEN_GUID)
+ getToken();
+ if (checkToken() != XTOKEN_COMMA)
+ break;
+ getToken();
+ }
+ }
+ return true;
+}
+
+bool XFileLoader::parseTemplateMembersList() {
+ while (true) {
+ bool array = false;
+ if (checkToken() == XTOKEN_ARRAY) {
+ getToken();
+ array = true;
+ }
+
+ if (checkToken() == XTOKEN_NAME) {
+ getToken();
+ } else if (isPrimitiveType(checkToken())) {
+ getToken();
+ } else
+ break;
+
+ if (getToken() != XTOKEN_NAME)
+ return false;
+
+ if (array) {
+ while (checkToken() == XTOKEN_OBRACKET) {
+ getToken();
+ if (checkToken() == XTOKEN_INTEGER) {
+ getToken();
+ } else {
+ if (getToken() != XTOKEN_NAME)
+ return false;
+ }
+ if (getToken() != XTOKEN_CBRACKET)
+ return false;
+ }
+ }
+ if (getToken() != XTOKEN_SEMICOLON)
+ return false;
+ }
+ return true;
+}
+
+bool XFileLoader::parseTemplateParts() {
+ if (!parseTemplateMembersList())
+ return false;
+ if (checkToken() == XTOKEN_OBRACKET) {
+ getToken();
+ if (!parseTemplateOptionInfo())
+ return false;
+ if (getToken() != XTOKEN_CBRACKET)
+ return false;
+ }
+ return true;
+}
+
+bool XFileLoader::parseTemplate() {
+ if (getToken() != XTOKEN_TEMPLATE)
+ return false;
+ if (getToken() != XTOKEN_NAME)
+ return false;
+ if (getToken() != XTOKEN_OBRACE)
+ return false;
+ if (getToken() != XTOKEN_GUID)
+ return false;
+ if (!parseTemplateParts())
+ return false;
+ if (getToken() != XTOKEN_CBRACE)
+ return false;
+ return true;
+}
+
+bool XFileLoader::parseObjectParts(XObject *object) {
+ switch (object->_classType) {
+ case kXClassAnimTicksPerSecond: {
+ auto objClass = new XAnimTicksPerSecondObject;
+ if (!getInteger(objClass->_animTicksPerSecond)) {
+ delete objClass;
+ return false;
+ }
+ object->_object = objClass;
+ }
+ break;
+
+ case kXClassFrame: {
+ auto objClass = new XFrameObject;
+ if (!parseChildObjects(object)) {
+ delete objClass;
+ return false;
+ }
+ object->_object = objClass;
+ }
+ break;
+
+ case kXClassFrameTransformMatrix: {
+ auto objClass = new XFrameTransformMatrixObject;
+ for (int m = 0; m < 16; m++) {
+ if (!getFloat(objClass->_frameMatrix[m])) {
+ delete objClass;
+ return false;
+ }
+ }
+ object->_object = objClass;
+ }
+ break;
+
+ case kXClassMesh: {
+ auto objClass = new XMeshObject;
+ if (!getInteger(objClass->_numVertices)) {
+ delete objClass;
+ return false;
+ }
+
+ objClass->_vertices = new XVector[objClass->_numVertices];
+ for (uint n = 0; n < objClass->_numVertices; n++) {
+ if (!getFloat(objClass->_vertices[n]._x) ||
+ !getFloat(objClass->_vertices[n]._y) ||
+ !getFloat(objClass->_vertices[n]._z)) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ if (!getInteger(objClass->_numFaces)) {
+ delete objClass;
+ return false;
+ }
+ objClass->_faces = new XMeshFace[objClass->_numFaces];
+ for (uint n = 0; n < objClass->_numFaces; n++) {
+ if (!getInteger(objClass->_faces[n]._numFaceVertexIndices)) {
+ delete objClass;
+ return false;
+ }
+ assert(objClass->_faces[n]._numFaceVertexIndices == 3 ||
+ objClass->_faces[n]._numFaceVertexIndices == 4);
+
+ for (uint f = 0; f < objClass->_faces[n]._numFaceVertexIndices; f++) {
+ if (!getInteger(objClass->_faces[n]._faceVertexIndices[f])) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+ }
+ skipSemicolonComma();
+
+ if (!parseChildObjects(object)) {
+ delete objClass;
+ return false;
+ }
+
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassMeshNormals: {
+ auto objClass = new XMeshNormalsObject;
+ if (!getInteger(objClass->_numNormals)) {
+ delete objClass;
+ return false;
+ }
+
+ objClass->_normals = new XVector[objClass->_numNormals];
+ for (uint n = 0; n < objClass->_numNormals; n++) {
+ if (!getFloat(objClass->_normals[n]._x) ||
+ !getFloat(objClass->_normals[n]._y) ||
+ !getFloat(objClass->_normals[n]._z)) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ if (!getInteger(objClass->_numFaceNormals)) {
+ delete objClass;
+ return false;
+ }
+ objClass->_faceNormals = new XMeshFace[objClass->_numFaceNormals];
+
+ for (uint n = 0; n < objClass->_numFaceNormals; n++) {
+ if (!getInteger(objClass->_faceNormals[n]._numFaceVertexIndices)) {
+ delete objClass;
+ return false;
+ }
+ assert(objClass->_faceNormals[n]._numFaceVertexIndices == 3 ||
+ objClass->_faceNormals[n]._numFaceVertexIndices == 4);
+
+ for (uint f = 0; f < objClass->_faceNormals[n]._numFaceVertexIndices; f++) {
+ if (!getInteger(objClass->_faceNormals[n]._faceVertexIndices[f])) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+ }
+ skipSemicolonComma();
+
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassMeshVertexColors: {
+ auto objClass = new XMeshVertexColorsObject;
+ if (!getInteger(objClass->_numVertexColors)) {
+ delete objClass;
+ return false;
+ }
+
+ objClass->_vertexColors = new XIndexedColor[objClass->_numVertexColors];
+ for (uint n = 0; n < objClass->_numVertexColors; n++) {
+ if (!getInteger(objClass->_vertexColors[n]._index) ||
+ !getFloat(objClass->_vertexColors[n]._indexColorR) ||
+ !getFloat(objClass->_vertexColors[n]._indexColorG) ||
+ !getFloat(objClass->_vertexColors[n]._indexColorB) ||
+ !getFloat(objClass->_vertexColors[n]._indexColorA)) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassMeshTextureCoords: {
+ auto objClass = new XMeshTextureCoordsObject;
+ if (!getInteger(objClass->_numTextureCoords)) {
+ delete objClass;
+ return false;
+ }
+
+ objClass->_textureCoords = new XCoords2d[objClass->_numTextureCoords];
+ for (uint n = 0; n < objClass->_numTextureCoords; n++) {
+ if (!getFloat(objClass->_textureCoords[n]._u)) {
+ delete objClass;
+ return false;
+ }
+ if (!getFloat(objClass->_textureCoords[n]._v)) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassVertexDuplicationIndices: {
+ auto objClass = new XVertexDuplicationIndicesObject;
+ if (!getInteger(objClass->_numIndices) ||
+ !getInteger(objClass->_nOriginalVertices)) {
+ delete objClass;
+ return false;
+ }
+
+ objClass->_indices = new uint32[objClass->_numIndices];
+ for (uint n = 0; n < objClass->_numIndices; n++) {
+ if (!getInteger(objClass->_indices[n])) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassMeshMaterialList: {
+ auto objClass = new XMeshMaterialListObject;
+ if (!getInteger(objClass->_nMaterials) ||
+ !getInteger(objClass->_numFaceIndexes)) {
+ delete objClass;
+ return false;
+ }
+
+ objClass->_faceIndexes = new uint32[objClass->_numFaceIndexes];
+ for (uint n = 0; n < objClass->_numFaceIndexes; n++) {
+ if (!getInteger(objClass->_faceIndexes[n])) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ if (!parseChildObjects(object)) {
+ delete objClass;
+ return false;
+ }
+
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassMaterial: {
+ auto objClass = new XMaterialObject;
+ if (!getFloat(objClass->_colorR) ||
+ !getFloat(objClass->_colorG) ||
+ !getFloat(objClass->_colorB) ||
+ !getFloat(objClass->_colorA) ||
+ !getFloat(objClass->_power) ||
+ !getFloat(objClass->_specularR) ||
+ !getFloat(objClass->_specularG) ||
+ !getFloat(objClass->_specularB) ||
+ !getFloat(objClass->_emissiveR) ||
+ !getFloat(objClass->_emissiveG) ||
+ !getFloat(objClass->_emissiveB)) {
+ delete objClass;
+ return false;
+ }
+
+ if (!parseChildObjects(object)) {
+ delete objClass;
+ return false;
+ }
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassTextureFilename: {
+ auto objClass = new XTextureFilenameObject;
+ if (!getString((char *)objClass->_filename, XMAX_NAME_LEN)) {
+ delete objClass;
+ return false;
+ }
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassSkinMeshHeader: {
+ auto objClass = new XSkinMeshHeaderObject;
+ if (!getInteger(objClass->_nMaxSkinWeightsPerVertex) ||
+ !getInteger(objClass->_nMaxSkinWeightsPerFace) ||
+ !getInteger(objClass->_nBones)) {
+ delete objClass;
+ return false;
+ }
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassSkinWeights: {
+ auto objClass = new XSkinWeightsObject;
+ if (!getString(objClass->_transformNodeName, XMAX_NAME_LEN) ||
+ !getInteger(objClass->_numWeights)) {
+ delete objClass;
+ return false;
+ }
+
+ objClass->_vertexIndices = new uint32[objClass->_numWeights];
+ for (uint n = 0; n < objClass->_numWeights; n++) {
+ if (!getInteger(objClass->_vertexIndices[n])) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ objClass->_weights = new float[objClass->_numWeights];
+ for (uint n = 0; n < objClass->_numWeights; n++) {
+ if (!getFloat(objClass->_weights[n])) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ for (int m = 0; m < 16; m++) {
+ if (!getFloat(objClass->_matrixOffset[m])) {
+ delete objClass;
+ return false;
+ }
+ }
+
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassAnimationSet: {
+ auto objClass = new XAnimationSetObject;
+ if (!parseChildObjects(object)) {
+ delete objClass;
+ return false;
+ }
+ object->_object = objClass;
+ }
+ break;
+
+ case kXClassAnimation: {
+ auto objClass = new XAnimationObject;
+ if (!parseChildObjects(object)) {
+ delete objClass;
+ return false;
+ }
+ object->_object = objClass;
+ }
+ break;
+
+ case kXClassAnimationKey: {
+ auto objClass = new XAnimationKeyObject;
+ if (!getInteger(objClass->_keyType) ||
+ !getInteger(objClass->_numKeys)) {
+ delete objClass;
+ return false;
+ }
+ if (objClass->_keyType > 4) {
+ warning("XFileLoader: AnimationKey key type invalid");
+ delete objClass;
+ return false;
+ }
+
+ objClass->_keys = new XTimedFloatKeys[objClass->_numKeys];
+ for (uint n = 0; n < objClass->_numKeys; n++) {
+ if (checkToken() == XTOKEN_INTEGER) {
+ uint32 timeVal;
+ if (!getInteger(timeVal)) {
+ delete objClass;
+ return false;
+ }
+ objClass->_keys[n]._time = timeVal;
+ } else if (checkToken() == XTOKEN_FLOAT) {
+ if (!getFloat(objClass->_keys[n]._time)) {
+ delete objClass;
+ return false;
+ }
+ }
+
+ if (!getInteger(objClass->_keys[n]._numTfkeys)) {
+ delete objClass;
+ return false;
+ }
+ for (uint f = 0; f < objClass->_keys[n]._numTfkeys; f++) {
+ if (!getFloat(objClass->_keys[n]._tfkeys[f])) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+ }
+ skipSemicolonComma();
+
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassAnimationOptions: {
+ auto objClass = new XAnimationOptionsObject;
+ if (!getInteger(objClass->_openclosed) ||
+ !getInteger(objClass->_positionquality)) {
+ delete objClass;
+ return false;
+ }
+
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassDeclData: {
+ auto objClass = new XDeclDataObject;
+ if (!getInteger(objClass->_numElements)) {
+ delete objClass;
+ return false;
+ }
+
+ objClass->_elements = new XVertexElement[objClass->_numElements];
+ for (uint n = 0; n < objClass->_numElements; n++) {
+ if (!getInteger(objClass->_elements[n]._type) ||
+ !getInteger(objClass->_elements[n]._method) ||
+ !getInteger(objClass->_elements[n]._usage) ||
+ !getInteger(objClass->_elements[n]._usageIndex)) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ if (!getInteger(objClass->_numData)) {
+ delete objClass;
+ return false;
+ }
+ objClass->_data = new uint32[objClass->_numData];
+ for (uint n = 0; n < objClass->_numData; n++) {
+ if (!getInteger(objClass->_data[n])) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ object->_object = objClass;
+ break;
+ }
+
+ case kXClassFVFData: {
+ auto objClass = new XFVFDataObject;
+ if (!getInteger(objClass->_dwFVF) ||
+ !getInteger(objClass->_numData)) {
+ delete objClass;
+ return false;
+ }
+
+ objClass->_data = new uint32[objClass->_numData];
+ for (uint n = 0; n < objClass->_numData; n++) {
+ if (!getInteger(objClass->_data[n])) {
+ delete objClass;
+ return false;
+ }
+ }
+ skipSemicolonComma();
+
+ object->_object = objClass;
+ break;
+ }
+
+ default:
+ error("XFileLoader: Not implemented class %d", object->_classType);
+ }
+
+ return true;
+}
+
+XObject *XFileLoader::resolveChildObject(XObject *object, const Common::String &referenceName) {
+ if (object->_name == referenceName) {
+ return object;
+ }
+ for (uint i = 0; i < object->_children.size(); i++) {
+ XObject *targetObject = resolveChildObject(object->_children[i], referenceName);
+ if (targetObject)
+ return targetObject;
+ }
+ return nullptr;
+}
+
+bool XFileLoader::resolveObject(XObject *referenceObject, const Common::String &referenceName) {
+ bool found = false;
+ for (uint i = 0; i < _xobjects.size(); i++) {
+ XObject *targetObject = resolveChildObject(_xobjects[i], referenceName);
+ if (targetObject) {
+ referenceObject->_targetObject = targetObject;
+ found = true;
+ break;
+ }
+ }
+ return found;
+}
+
+bool XFileLoader::parseChildObjects(XObject *object) {
+ if (checkToken() != XTOKEN_NAME && checkToken() != XTOKEN_OBRACE) {
+ return true;
+ }
+
+ while (true) {
+ if (checkToken() == XTOKEN_OBRACE) {
+ getToken();
+ if (getToken() != XTOKEN_NAME)
+ return false;
+ XObject *child = new XObject();
+ object->_children.push(child);
+ if (!resolveObject(child, _currentToken._textVal)) {
+ warning("XFileLoader: Referenced object doesn't exists \"%s\"", _currentToken._textVal);
+ }
+ if (getToken() != XTOKEN_CBRACE)
+ return false;
+ } else if (checkToken() == XTOKEN_NAME) {
+ XObject *child = new XObject();
+ object->_children.push(child);
+ if (!parseObject(child))
+ return false;
+ } else if (checkToken() != XTOKEN_CBRACE) {
+ return false;
+ } else
+ break;
+ }
+
+ return true;
+}
+
+bool XFileLoader::parseObject(XObject *object) {
+ if (getToken() != XTOKEN_NAME)
+ return false;
+
+ for (uint i = 0; i < ARRAYSIZE(gXClasses); i++) {
+ if (!XFileLoader_strcmp(gXClasses[i].className, _currentToken._textVal)) {
+ object->_classType = gXClasses[i].type;
+ break;
+ }
+ }
+ if (object->_classType == kXClassUnknown) {
+ error("XFileLoader: Unknown class \"%s\"", _currentToken._textVal);
+ return false;
+ }
+
+ if (checkToken() == XTOKEN_NAME) {
+ getToken();
+ object->_name = _currentToken._textVal;
+ }
+
+ if (getToken() != XTOKEN_OBRACE)
+ return false;
+
+ if (checkToken() == XTOKEN_GUID) {
+ getToken();
+ }
+
+ if (!parseObjectParts(object))
+ return false;
+ if (getToken() != XTOKEN_CBRACE)
+ return false;
+
+ checkToken();
+
+ return true;
+}
+
+bool XFileLoader::load(byte *buffer, uint32 bufferSize) {
+ if (!_initialised)
+ return false;
+
+ _buffer = buffer;
+ _bufferLeft = bufferSize;
+
+ if (!parseHeader())
+ return false;
+
+ while (_bufferLeft) {
+ XTokenType token = checkToken();
+ switch (token) {
+ case XTOKEN_TEMPLATE:
+ if (!parseTemplate()) {
+ warning("XFileLoader: Template is not correct");
+ return false;
+ }
+ break;
+ case XTOKEN_NAME: {
+ XObject *xobject = new XObject();
+ _xobjects.push(xobject);
+ if (!parseObject(xobject)) {
+ warning("XFileLoader: Object is not correct");
+ return false;
+ }
+ }
+ break;
+ default:
+ warning("XFileLoader: Unexpected token");
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xfile_loader.h b/engines/wintermute/base/gfx/xfile_loader.h
new file mode 100644
index 00000000000..aa19a9fae46
--- /dev/null
+++ b/engines/wintermute/base/gfx/xfile_loader.h
@@ -0,0 +1,566 @@
+/* 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/>.
+ *
+ */
+
+/*
+ * Partially based on XFile parser code from Wine sources.
+ * Copyright 2008 Christian Costa
+ */
+
+#ifndef WINTERMUTE_XFILE_LOADER_H
+#define WINTERMUTE_XFILE_LOADER_H
+
+#include "common/str.h"
+#include "common/stack.h"
+
+namespace Wintermute {
+
+enum XTokenType : uint16 {
+ XTOKEN_ERROR = 0xffff,
+ XTOKEN_NONE = 0,
+ XTOKEN_NAME = 1,
+ XTOKEN_STRING = 2,
+ XTOKEN_INTEGER = 3,
+ XTOKEN_GUID = 5,
+ XTOKEN_INTEGER_LIST = 6,
+ XTOKEN_FLOAT_LIST = 7,
+ XTOKEN_OBRACE = 10,
+ XTOKEN_CBRACE = 11,
+ XTOKEN_OPAREN = 12,
+ XTOKEN_CPAREN = 13,
+ XTOKEN_OBRACKET = 14,
+ XTOKEN_CBRACKET = 15,
+ XTOKEN_OANGLE = 16,
+ XTOKEN_CANGLE = 17,
+ XTOKEN_DOT = 18,
+ XTOKEN_COMMA = 19,
+ XTOKEN_SEMICOLON = 20,
+ XTOKEN_TEMPLATE = 31,
+ XTOKEN_WORD = 40,
+ XTOKEN_DWORD = 41,
+ XTOKEN_FLOAT = 42,
+ XTOKEN_DOUBLE = 43,
+ XTOKEN_CHAR = 44,
+ XTOKEN_UCHAR = 45,
+ XTOKEN_SWORD = 46,
+ XTOKEN_SDWORD = 47,
+ XTOKEN_VOID = 48,
+ XTOKEN_LPSTR = 49,
+ XTOKEN_UNICODE = 50,
+ XTOKEN_CSTRING = 51,
+ XTOKEN_ARRAY = 52
+};
+
+#define XMAX_NAME_LEN 120
+#define XMAX_STRING_LEN 500
+
+struct XToken {
+ XTokenType _type;
+ char _textVal[XMAX_STRING_LEN];
+ uint32 _integerVal;
+ float _floatVal;
+};
+
+struct XVector {
+ float _x;
+ float _y;
+ float _z;
+};
+
+struct XCoords2d {
+ float _u;
+ float _v;
+};
+
+struct XMeshFace {
+ uint32 _numFaceVertexIndices;
+ uint32 _faceVertexIndices[4];
+};
+
+struct XTimedFloatKeys {
+ float _time;
+ uint32 _numTfkeys;
+ float _tfkeys[16];
+};
+
+struct XIndexedColor {
+ uint32 _index;
+ float _indexColorR;
+ float _indexColorG;
+ float _indexColorB;
+ float _indexColorA;
+};
+
+struct XVertexElement {
+ uint32 _type;
+ uint32 _method;
+ uint32 _usage;
+ uint32 _usageIndex;
+};
+
+struct XMeshMaterialListObject {
+ uint32 _nMaterials;
+ uint32 _numFaceIndexes;
+ uint32 *_faceIndexes{};
+
+ ~XMeshMaterialListObject() {
+ delete[] _faceIndexes;
+ }
+};
+
+struct XVertexDuplicationIndicesObject {
+ uint32 _nOriginalVertices;
+ uint32 _numIndices;
+ uint32 *_indices{};
+
+ ~XVertexDuplicationIndicesObject() {
+ delete[] _indices;
+ }
+};
+
+struct XSkinMeshHeaderObject{
+ uint32 _nMaxSkinWeightsPerVertex;
+ uint32 _nMaxSkinWeightsPerFace;
+ uint32 _nBones;
+};
+
+struct XSkinWeightsObject {
+ char _transformNodeName[XMAX_NAME_LEN];
+ uint32 _numVertexIndices;
+ uint32 *_vertexIndices{};
+ uint32 _numWeights;
+ float *_weights{};
+ float _matrixOffset[16];
+
+ ~XSkinWeightsObject() {
+ delete[] _vertexIndices;
+ delete[] _weights;
+ }
+};
+
+struct XMeshObject {
+ uint32 _numVertices;
+ XVector *_vertices{};
+ uint32 _numFaces;
+ XMeshFace *_faces{};
+
+ ~XMeshObject() {
+ delete[] _vertices;
+ delete[] _faces;
+ }
+};
+
+struct XMeshNormalsObject {
+ uint32 _numNormals;
+ XVector *_normals{};
+ uint32 _numFaceNormals;
+ XMeshFace *_faceNormals{};
+
+ ~XMeshNormalsObject() {
+ delete[] _normals;
+ delete[] _faceNormals;
+ }
+};
+
+struct XMeshVertexColorsObject {
+ uint32 _numVertexColors;
+ XIndexedColor *_vertexColors{};
+
+ ~XMeshVertexColorsObject() {
+ delete[] _vertexColors;
+ }
+};
+
+struct XMeshTextureCoordsObject {
+ uint32 _numTextureCoords;
+ XCoords2d *_textureCoords{};
+
+ ~XMeshTextureCoordsObject() {
+ delete[] _textureCoords;
+ }
+};
+
+struct XMaterialObject {
+ float _colorR;
+ float _colorG;
+ float _colorB;
+ float _colorA;
+ float _power;
+ float _specularR;
+ float _specularG;
+ float _specularB;
+ float _emissiveR;
+ float _emissiveG;
+ float _emissiveB;
+};
+
+struct XTextureFilenameObject {
+ char _filename[XMAX_NAME_LEN];
+};
+
+struct XAnimTicksPerSecondObject {
+ uint32 _animTicksPerSecond;
+};
+
+struct XAnimationSetObject{
+};
+
+struct XAnimationObject{
+};
+
+struct XAnimationKeyObject {
+ uint32 _keyType;
+ uint32 _numKeys;
+ XTimedFloatKeys *_keys{};
+
+ ~XAnimationKeyObject() {
+ delete[] _keys;
+ }
+};
+
+struct XAnimationOptionsObject {
+ uint32 _openclosed;
+ uint32 _positionquality;
+};
+
+struct XFrameObject {
+};
+
+struct XFrameTransformMatrixObject {
+ float _frameMatrix[16];
+};
+
+struct XDeclDataObject {
+ uint32 _numElements;
+ XVertexElement *_elements{};
+ uint32 _numData;
+ uint32 *_data{};
+
+ ~XDeclDataObject() {
+ delete[] _elements;
+ delete[] _data;
+ }
+};
+
+struct XFVFDataObject {
+ uint32 _dwFVF;
+ uint32 _numData;
+ uint32 *_data{};
+
+ ~XFVFDataObject() {
+ delete[] _data;
+ }
+};
+
+enum XClassType {
+ kXClassUnknown = 0,
+ kXClassAnimTicksPerSecond,
+ kXClassFrameTransformMatrix,
+ kXClassFrame,
+ kXClassMesh,
+ kXClassMeshNormals,
+ kXClassMeshVertexColors,
+ kXClassMeshTextureCoords,
+ kXClassMeshMaterialList,
+ kXClassVertexDuplicationIndices,
+ kXClassMaterial,
+ kXClassTextureFilename,
+ kXClassSkinMeshHeader,
+ kXClassSkinWeights,
+ kXClassAnimationSet,
+ kXClassAnimation,
+ kXClassAnimationKey,
+ kXClassAnimationOptions,
+ kXClassDeclData,
+ kXClassFVFData,
+};
+
+class XFileEnumObject;
+
+class XObject {
+ friend class XFileLoader;
+ friend class XFileData;
+ friend class XFileEnumObject;
+
+private:
+
+ Common::String _name;
+ XClassType _classType{};
+ void *_object{};
+ XObject *_targetObject{};
+ Common::Stack<XObject *> _children;
+
+public:
+
+ void deinit() {
+ switch (_classType) {
+ case kXClassAnimTicksPerSecond:
+ delete (XAnimTicksPerSecondObject *)_object;
+ break;
+ case kXClassAnimationKey:
+ delete (XAnimationKeyObject *)_object;
+ break;
+ case kXClassAnimation:
+ delete (XAnimationObject *)_object;
+ break;
+ case kXClassAnimationOptions:
+ delete (XAnimationOptionsObject *)_object;
+ break;
+ case kXClassAnimationSet:
+ delete (XAnimationSetObject *)_object;
+ break;
+ case kXClassDeclData:
+ delete (XDeclDataObject *)_object;
+ break;
+ case kXClassFrame:
+ delete (XFrameObject *)_object;
+ break;
+ case kXClassFrameTransformMatrix:
+ delete (XFrameTransformMatrixObject *)_object;
+ break;
+ case kXClassFVFData:
+ delete (XFVFDataObject *)_object;
+ break;
+ case kXClassMaterial:
+ delete (XMaterialObject *)_object;
+ break;
+ case kXClassMesh:
+ delete (XMeshObject *)_object;
+ break;
+ case kXClassMeshMaterialList:
+ delete (XMeshMaterialListObject *)_object;
+ break;
+ case kXClassMeshNormals:
+ delete (XMeshNormalsObject *)_object;
+ break;
+ case kXClassMeshVertexColors:
+ delete (XMeshVertexColorsObject *)_object;
+ break;
+ case kXClassMeshTextureCoords:
+ delete (XMeshTextureCoordsObject *)_object;
+ break;
+ case kXClassSkinMeshHeader:
+ delete (XSkinMeshHeaderObject *)_object;
+ break;
+ case kXClassSkinWeights:
+ delete (XSkinWeightsObject *)_object;
+ break;
+ case kXClassVertexDuplicationIndices:
+ delete (XVertexDuplicationIndicesObject *)_object;
+ break;
+ case kXClassTextureFilename:
+ delete (XTextureFilenameObject *)_object;
+ break;
+ case kXClassUnknown:
+ break;
+ }
+ }
+};
+
+class XFileLoader {
+ friend class XFileEnumObject;
+
+private:
+
+ const int kCabBlockSize = 0x8000;
+ const int kCabInputmax = kCabBlockSize + 12;
+
+ bool _initialised{};
+ XToken _currentToken;
+ byte *_decompBuffer{};
+ byte *_buffer;
+ uint32 _bufferLeft;
+ bool _isText;
+ uint32 _listNbElements;
+ bool _listTypeFloat;
+ bool _listSeparator;
+ bool _tokenPresent;
+
+ Common::Stack<XObject *> _xobjects;
+
+public:
+
+ XFileLoader();
+ ~XFileLoader();
+ bool load(byte *buffer, uint32 bufferSize);
+ bool createEnumObject(XFileEnumObject &xobj);
+
+private:
+
+ void init();
+ void deinit();
+
+ FORCEINLINE bool readChar(char &c);
+ FORCEINLINE void rewindBytes(uint32 size);
+ bool readBytes(void *data, uint32 size);
+ bool readLE16(uint16 *data);
+ bool readLE32(uint32 *data);
+ bool readBE32(uint32 *data);
+
+ FORCEINLINE bool getInteger(uint32 &value);
+ FORCEINLINE bool getFloat(float &value);
+ FORCEINLINE bool getString(char *str, uint maxLen);
+ FORCEINLINE bool skipSemicolonComma();
+
+ FORCEINLINE bool isSpace(char c);
+ FORCEINLINE bool isOperator(char c);
+ FORCEINLINE bool isSeparator(char c);
+ FORCEINLINE bool isPrimitiveType(XTokenType token);
+ FORCEINLINE bool isGuid();
+ FORCEINLINE bool isName();
+ FORCEINLINE bool isFloat();
+ FORCEINLINE bool isInteger();
+ FORCEINLINE bool isString();
+ FORCEINLINE bool isKeyword(const char *keyword, uint len);
+ FORCEINLINE XTokenType getKeywordToken();
+ FORCEINLINE XTokenType checkToken();
+ XTokenType getToken();
+ void parseToken();
+
+ bool decompressMsZipData();
+
+ bool parseHeader();
+
+ bool parseTemplate();
+ bool parseTemplateParts();
+ bool parseTemplateOptionInfo();
+ bool parseTemplateMembersList();
+ XObject *resolveChildObject(XObject *object, const Common::String &referenceName);
+ bool resolveObject(XObject *referenceObject, const Common::String &referenceName);
+ bool parseObject(XObject *object);
+ bool parseChildObjects(XObject *object);
+ bool parseObjectParts(XObject *object);
+};
+
+class XFileData {
+ friend class XFileEnumObject;
+
+private:
+
+ XObject *_xobject{};
+ bool _reference{};
+
+public:
+
+ bool getChild(uint id, XFileData &child) {
+ if (_xobject) {
+ if (id < _xobject->_children.size()) {
+ child._xobject = _xobject->_children[id];
+ if (child._xobject->_targetObject) {
+ child._xobject = child._xobject->_targetObject;
+ child._reference = true;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool getChildren(uint &num) {
+ if (_xobject) {
+ num = _xobject->_children.size();
+ return true;
+ }
+ return false;
+ }
+
+ bool getName(Common::String &name) {
+ if (_xobject) {
+ name = _xobject->_name;
+ return true;
+ }
+ return false;
+ }
+
+ bool getType(XClassType &classType) {
+ if (_xobject) {
+ classType = _xobject->_classType;
+ return true;
+ }
+ return false;
+ }
+
+ bool isReference() {
+ if (_xobject) {
+ return _reference;
+ }
+ return false;
+ }
+
+#define GET_OBJECT_FUNC(objectName) \
+ objectName *get ## objectName() { \
+ if (_xobject) \
+ return static_cast<objectName *>(_xobject->_object); \
+ else \
+ return nullptr; \
+ }
+
+ GET_OBJECT_FUNC(XAnimTicksPerSecondObject)
+ GET_OBJECT_FUNC(XAnimationKeyObject)
+ GET_OBJECT_FUNC(XAnimationObject)
+ GET_OBJECT_FUNC(XAnimationOptionsObject)
+ GET_OBJECT_FUNC(XAnimationSetObject)
+ GET_OBJECT_FUNC(XDeclDataObject)
+ GET_OBJECT_FUNC(XFrameObject)
+ GET_OBJECT_FUNC(XFrameTransformMatrixObject)
+ GET_OBJECT_FUNC(XFVFDataObject)
+ GET_OBJECT_FUNC(XMaterialObject)
+ GET_OBJECT_FUNC(XMeshObject)
+ GET_OBJECT_FUNC(XMeshMaterialListObject)
+ GET_OBJECT_FUNC(XMeshNormalsObject)
+ GET_OBJECT_FUNC(XMeshVertexColorsObject)
+ GET_OBJECT_FUNC(XMeshTextureCoordsObject)
+ GET_OBJECT_FUNC(XSkinMeshHeaderObject)
+ GET_OBJECT_FUNC(XSkinWeightsObject)
+ GET_OBJECT_FUNC(XVertexDuplicationIndicesObject)
+ GET_OBJECT_FUNC(XTextureFilenameObject)
+};
+
+class XFileEnumObject {
+ friend class XFileLoader;
+
+private:
+
+ XFileLoader *_file{};
+
+public:
+
+ bool getChild(uint id, XFileData &child) {
+ if (_file) {
+ if (id < _file->_xobjects.size()) {
+ child._xobject = _file->_xobjects[id];
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool getChildren(uint &num) {
+ if (_file) {
+ num = _file->_xobjects.size();
+ return true;
+ }
+ return false;
+ }
+};
+
+} // namespace Wintermute
+
+#endif
diff --git a/engines/wintermute/base/gfx/xframe_node.cpp b/engines/wintermute/base/gfx/xframe_node.cpp
index b6aeff3dcba..0f29e52ac36 100644
--- a/engines/wintermute/base/gfx/xframe_node.cpp
+++ b/engines/wintermute/base/gfx/xframe_node.cpp
@@ -26,11 +26,12 @@
*/
#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_engine.h"
#include "engines/wintermute/base/gfx/base_renderer3d.h"
#include "engines/wintermute/base/gfx/xmaterial.h"
#include "engines/wintermute/base/gfx/xframe_node.h"
#include "engines/wintermute/base/gfx/xmodel.h"
-#include "engines/wintermute/base/gfx/xloader.h"
+#include "engines/wintermute/base/gfx/xfile_loader.h"
#include "engines/wintermute/dcgf.h"
namespace Wintermute {
@@ -97,42 +98,35 @@ void FrameNode::setTransformation(int slot, Math::Vector3d pos, Math::Vector3d s
}
//////////////////////////////////////////////////////////////////////////
-bool FrameNode::loadFromX(const Common::String &filename, XFileLexer &lexer, XModel *model, Common::Array<MaterialReference> &materialReferences) {
+bool FrameNode::loadFromXData(const Common::String &filename, XModel *model, XFileData *xobj, Common::Array<MaterialReference> &materialReferences) {
_gameRef->miniUpdate();
- bool ret = true;
-
- setName(lexer.tokenToString().c_str());
- lexer.advanceToNextToken();
- lexer.advanceOnOpenBraces();
+ bool res = true;
- while (!lexer.eof()) {
- if (lexer.tokenIsIdentifier("Frame")) {
- lexer.advanceToNextToken();
- FrameNode *child = new FrameNode(_gameRef);
- if (child->loadFromX(filename, lexer, model, materialReferences)) {
- _frames.add(child);
- } else {
- delete child;
- }
- } else if (lexer.tokenIsIdentifier("Mesh")) {
- lexer.advanceToNextToken();
- XMesh *mesh = _gameRef->_renderer3D->createXMesh();
-
- if (mesh->loadFromX(filename, lexer, materialReferences)) {
- _meshes.add(mesh);
- } else {
- delete mesh;
- }
- } else if (lexer.tokenIsIdentifier("FrameTransformMatrix")) {
- lexer.advanceToNextToken();
- lexer.advanceToNextToken(); // skip optional name
- lexer.advanceOnOpenBraces();
+ // get the type of the object
+ XClassType objectType;
+ res = xobj->getType(objectType);
+ if (objectType == kXClassMesh) { // load a child mesh
+ XMesh *mesh = _gameRef->_renderer3D->createXMesh();
+ res = mesh->loadFromXData(filename, xobj, materialReferences);
+ if (res) {
+ _meshes.add(mesh);
+ return true;
+ } else {
+ delete mesh;
+ return false;
+ }
+ } else if (objectType == kXClassFrameTransformMatrix) { // load the transformation matrix
+ XFrameTransformMatrixObject *frameTransformMatrix = xobj->getXFrameTransformMatrixObject();
+ if (!frameTransformMatrix) {
+ BaseEngine::LOG(0, "Error loading transformation matrix");
+ return false;
+ } else {
// TODO: check if this is the right format
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
- _transformationMatrix(c, r) = lexer.readFloat();
+ _transformationMatrix(c, r) = frameTransformMatrix->_frameMatrix[r * 4 + c];
}
}
@@ -148,84 +142,85 @@ bool FrameNode::loadFromX(const Common::String &filename, XFileLexer &lexer, XMo
_transformationMatrix(1, 2) *= -1.0f;
_originalMatrix = _transformationMatrix;
+ return true;
+ }
+ } else if (objectType == kXClassAnimationSet) { // load animation set
+ return model->loadAnimationSet(filename, xobj);
+ } else if (objectType == kXClassAnimation) { // load a single animation (shouldn't happen here)
+ return model->loadAnimation(filename, xobj);
+ } else if (objectType == kXClassFrame) { // create a new child frame
+ FrameNode *childFrame = new FrameNode(_gameRef);
+
+ // get the name of the child frame
+ res = XModel::loadName(childFrame, xobj);
+ if (!res) {
+ BaseEngine::LOG(0, "Error loading frame name");
+ delete childFrame;
+ return res;
+ }
- lexer.skipTerminator();
- lexer.advanceToNextToken();
-
- } else if (lexer.reachedClosedBraces()) {
- lexer.advanceToNextToken();
- break;
+ // Enumerate child objects.
+ res = false;
+ uint32 numChildren = 0;
+ xobj->getChildren(numChildren);
+ for (uint32 i = 0; i < numChildren; i++) {
+ XFileData xchildData;
+ res = xobj->getChild(i, xchildData);
+ if (res)
+ res = childFrame->loadFromXData(filename, model, &xchildData, materialReferences);
+ }
+ if (res)
+ _frames.add(childFrame);
+ else
+ delete childFrame;
+ return res;
+ } else if (objectType == kXClassAnimTicksPerSecond) {
+ if (!xobj->getXAnimTicksPerSecondObject()) {
+ BaseEngine::LOG(0, "Error loading ticks per seconds info");
+ return res;
} else {
- warning("FrameNode::loadFromX unexpected %i token excountered", lexer.getTypeOfToken());
- ret = false;
- break;
+ model->_ticksPerSecond = xobj->getXAnimTicksPerSecondObject()->_animTicksPerSecond;
+ return true;
}
+ } else if (objectType == kXClassMaterial) {
+ MaterialReference materialReference;
+ xobj->getName(materialReference._name);
+ materialReference._material = new Material(_gameRef);
+ materialReference._material->loadFromX(xobj, filename);
+ materialReferences.push_back(materialReference);
+ return true;
}
- return ret;
+ return true;
}
-bool FrameNode::loadFromXAsRoot(const Common::String &filename, XFileLexer &lexer, XModel *model, Common::Array<MaterialReference> &materialReferences) {
- // technically, there is no root node in a .X file
- // so we just start parsing it here
- lexer.advanceToNextToken();
-
- while (!lexer.eof()) {
- if (lexer.tokenIsIdentifier("Frame")) {
- lexer.advanceToNextToken();
- FrameNode *child = new FrameNode(_gameRef);
- if (child->loadFromX(filename, lexer, model, materialReferences)) {
- _frames.add(child);
- } else {
- delete child;
- }
- } else if (lexer.tokenIsIdentifier("Mesh")) {
- lexer.advanceToNextToken();
- XMesh *mesh = _gameRef->_renderer3D->createXMesh();
-
- if (mesh->loadFromX(filename, lexer, materialReferences)) {
- _meshes.add(mesh);
- } else {
- delete mesh;
- }
- } else if (lexer.tokenIsIdentifier("AnimTicksPerSecond")) {
- lexer.advanceToNextToken();
- lexer.advanceOnOpenBraces();
-
- model->_ticksPerSecond = lexer.readInt();
- lexer.advanceToNextToken(); // skip closed braces
- } else if (lexer.tokenIsIdentifier("AnimationSet")) {
- lexer.advanceToNextToken();
- model->loadAnimationSet(lexer, filename);
- } else if (lexer.tokenIsIdentifier("template")) {
- // we can ignore templates
- while (!lexer.eof()) {
- if (lexer.reachedClosedBraces()) {
- break;
- }
-
- lexer.advanceToNextToken();
- }
-
- lexer.advanceToNextToken();
- } else if(lexer.tokenIsIdentifier("Material")) {
- lexer.advanceToNextToken();
- MaterialReference materialReference;
-
- materialReference._name = lexer.tokenToString();
- materialReference._material = new Material(_gameRef);
- materialReference._material->loadFromX(lexer, filename);
+bool FrameNode::mergeFromXData(const Common::String &filename, XModel *model, XFileData *xobj) {
+ bool res = true;
- materialReferences.push_back(materialReference);
- } else if (lexer.tokenIsOfType(NULL_CHAR)) {
- // prevents some unnecessary warnings
- lexer.advanceToNextToken();
- } else {
- warning("FrameNode::loadFromXAsRoot unknown token %i encountered", lexer.getTypeOfToken());
- lexer.advanceToNextToken(); // just ignore it for the moment
+ // get the type of the object
+ XClassType objectType;
+ res = xobj->getType(objectType);
+ if (!res) {
+ BaseEngine::LOG(0, "Error getting object type");
+ return res;
+ } else if (objectType == kXClassAnimationSet) { // load animation set
+ return model->loadAnimationSet(filename, xobj);
+ } else if (objectType == kXClassAnimation) { // load a single animation (shouldn't happen here)
+ return model->loadAnimation(filename, xobj);
+ } else if (objectType == kXClassFrame) { // scan child frames
+ // Enumerate child objects.
+ res = false;
+
+ uint32 numChildren = 0;
+ xobj->getChildren(numChildren);
+ for (uint32 i = 0; i < numChildren; i++) {
+ XFileData xchildData;
+ res = xobj->getChild(i, xchildData);
+ if (res)
+ res = mergeFromXData(filename, model, &xchildData);
}
+ return res;
}
-
return true;
}
diff --git a/engines/wintermute/base/gfx/xframe_node.h b/engines/wintermute/base/gfx/xframe_node.h
index 933f62bfe17..0932c093fc9 100644
--- a/engines/wintermute/base/gfx/xframe_node.h
+++ b/engines/wintermute/base/gfx/xframe_node.h
@@ -40,8 +40,8 @@
namespace Wintermute {
class XModel;
+class XFileData;
class BaseSprite;
-class XFileLexer;
class FrameNode : public BaseNamedObject {
public:
@@ -55,8 +55,8 @@ public:
bool renderFlatShadowModel();
bool updateShadowVol(ShadowVolume *shadow, Math::Matrix4 &modelMat, const Math::Vector3d &light, float extrusionDepth);
- bool loadFromX(const Common::String &filename, XFileLexer &lexer, XModel *model, Common::Array<MaterialReference> &materialReferences);
- bool loadFromXAsRoot(const Common::String &filename, XFileLexer &lexer, XModel *model, Common::Array<MaterialReference> &materialReferences);
+ bool loadFromXData(const Common::String &filename, XModel *model, XFileData *xobj, Common::Array<MaterialReference> &materialReferences);
+ bool mergeFromXData(const Common::String &filename, XModel *model, XFileData *xobj);
bool findBones(FrameNode *rootFrame);
FrameNode *findFrame(const char *frameName);
Math::Matrix4 *getCombinedMatrix();
diff --git a/engines/wintermute/base/gfx/xloader.cpp b/engines/wintermute/base/gfx/xloader.cpp
deleted file mode 100644
index 312dbdce549..00000000000
--- a/engines/wintermute/base/gfx/xloader.cpp
+++ /dev/null
@@ -1,483 +0,0 @@
-/* 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 "common/algorithm.h"
-#include "common/scummsys.h"
-#include "common/str.h"
-#include "common/textconsole.h"
-#include "common/util.h"
-
-#include "engines/wintermute/base/gfx/xloader.h"
-
-namespace Wintermute {
-
-void nextTokenText(Common::MemoryReadStream &buffer, int &lineCount, Token &tok) {
- char current = buffer.readSByte();
-
- while (true) {
- if (Common::isSpace(current)) {
- if (current == '\n') {
- ++lineCount;
- }
-
- current = buffer.readSByte();
- } else if (current == '/' || current == '#') {
- // single slashes do not seem to be used in .X files,
- // so checking for one should be enough
-
- while (current != '\n') {
- current = buffer.readSByte();
- }
-
- current = buffer.readSByte();
- ++lineCount;
- } else {
- break;
- }
- }
-
- if (Common::isAlpha(current) || current == '_') {
- tok.pushChar(current);
- current = buffer.readSByte();
-
- while (Common::isAlnum(current) || current == '_' || current == '-') {
- tok.pushChar(current);
- current = buffer.readSByte();
- }
-
- buffer.seek(-1, SEEK_CUR);
- tok._type = IDENTIFIER;
- return;
- } else if (Common::isDigit(current) || current == '-') {
- tok.pushChar(current);
- current = buffer.readSByte();
-
- while (Common::isDigit(current)) {
- tok.pushChar(current);
- current = buffer.readSByte();
- }
-
- if (current == '.') {
- tok.pushChar(current);
- current = buffer.readSByte();
-
- while (Common::isDigit(current)) {
- tok.pushChar(current);
- current = buffer.readSByte();
- }
-
- buffer.seek(-1, SEEK_CUR);
- tok._type = FLOAT;
- return;
- }
-
- buffer.seek(-1, SEEK_CUR);
- tok._type = INT;
- return;
- } else if (current == '<') {
- // a uuid consists of 36 characters, 32 alphanumeric characters
- // and four "-". We add space for a null character at the
- // end of the tempoary buffer
- const int uuidSize = 37;
- char uuid[uuidSize];
- buffer.read(uuid, 36);
- uuid[uuidSize - 1] = 0;
-
- current = buffer.readSByte();
-
- if (current != '>') {
- warning("Wrong UUID format at line %i", lineCount);
- } else {
- tok._textVal = uuid;
- tok._type = UUID;
- return;
- }
- } else if (current == '"') {
- current = buffer.readSByte();
-
- while (current != '"') {
- tok.pushChar(current);
- current = buffer.readSByte();
- }
-
- tok._type = STRING;
- return;
- }
-
- switch (current) {
- case '(':
- tok._type = OPEN_PAREN;
- break;
- case ')':
- tok._type = CLOSE_PAREN;
- break;
- case '{':
- tok._type = OPEN_BRACES;
- break;
- case '}':
- tok._type = CLOSE_BRACES;
- break;
- case ']':
- tok._type = OPEN_BRACKET;
- break;
- case '[':
- tok._type = CLOSE_BRACKET;
- break;
- case ',':
- tok._type = COMMA;
- break;
- case ';':
- tok._type = SEMICOLON;
- break;
- case '.':
- tok._type = DOT;
- break;
- case '\0':
- tok._type = NULL_CHAR;
- break;
- default:
- tok._type = UNKNOWN_TOKEN;
- warning("Unknown token %c at line %i", current, lineCount);
- }
-}
-
-// based on MSDN .X file format documentation
-const uint16 XBIN_TOKEN_NAME = 1;
-const uint16 XBIN_TOKEN_STRING = 2;
-const uint16 XBIN_TOKEN_INTEGER = 3;
-const uint16 XBIN_TOKEN_GUID = 5;
-const uint16 XBIN_TOKEN_INTEGER_LIST = 6;
-const uint16 XBIN_TOKEN_FLOAT_LIST = 7;
-
-const uint16 XBIN_TOKEN_OBRACE = 10;
-const uint16 XBIN_TOKEN_CBRACE = 11;
-const uint16 XBIN_TOKEN_OPAREN = 12;
-const uint16 XBIN_TOKEN_CPAREN = 13;
-const uint16 XBIN_TOKEN_OBRACKET = 14;
-const uint16 XBIN_TOKEN_CBRACKET = 15;
-const uint16 XBIN_TOKEN_OANGLE = 16;
-const uint16 XBIN_TOKEN_CANGLE = 17;
-const uint16 XBIN_TOKEN_DOT = 18;
-const uint16 XBIN_TOKEN_COMMA = 19;
-const uint16 XBIN_TOKEN_SEMICOLON = 20;
-const uint16 XBIN_TOKEN_TEMPLATE = 31;
-const uint16 XBIN_TOKEN_WORD = 40;
-const uint16 XBIN_TOKEN_DWORD = 41;
-const uint16 XBIN_TOKEN_FLOAT = 42;
-const uint16 XBIN_TOKEN_DOUBLE = 43;
-const uint16 XBIN_TOKEN_CHAR = 44;
-const uint16 XBIN_TOKEN_UCHAR = 45;
-const uint16 XBIN_TOKEN_SWORD = 46;
-const uint16 XBIN_TOKEN_SDWORD = 47;
-const uint16 XBIN_TOKEN_VOID = 48;
-const uint16 XBIN_TOKEN_LPSTR = 49;
-const uint16 XBIN_TOKEN_UNICODE = 50;
-const uint16 XBIN_TOKEN_CSTRING = 51;
-const uint16 XBIN_TOKEN_ARRAY = 52;
-
-void XFileLexer::nextTokenBinary() {
- uint16 current = _buffer.readUint16LE();
- uint32 length = -1;
-
- switch (current) {
- case XBIN_TOKEN_NAME:
- length = _buffer.readUint32LE();
-
- for (uint32 i = 0; i < length; ++i) {
- _tok.pushChar(_buffer.readByte());
- }
-
- _tok._type = IDENTIFIER;
- break;
- case XBIN_TOKEN_STRING:
- length = _buffer.readUint32LE();
-
- for (uint32 i = 0; i < length; ++i) {
- _tok.pushChar(_buffer.readByte());
- }
-
- _tok._type = STRING;
- break;
- case XBIN_TOKEN_INTEGER:
- _tok._integerVal = _buffer.readUint32LE();
- _tok._type = INT;
- break;
- case XBIN_TOKEN_GUID:
- // ignore the UUID value
- _buffer.readUint32LE();
- _buffer.readUint16LE();
- _buffer.readUint16LE();
-
- for (int i = 0; i < 8; ++i) {
- _buffer.readByte();
- }
-
- _tok._type = UUID;
- break;
- case XBIN_TOKEN_INTEGER_LIST:
- _integersToRead = _buffer.readUint32LE();
- _tok._type = INT;
- _expectsTerminator = false;
- break;
- case XBIN_TOKEN_FLOAT_LIST:
- _floatsToRead = _buffer.readUint32LE();
- _tok._type = FLOAT;
- _expectsTerminator = false;
- break;
- case XBIN_TOKEN_OBRACE:
- _tok._type = OPEN_BRACES;
- break;
- case XBIN_TOKEN_CBRACE:
- _tok._type = CLOSE_BRACES;
- break;
- case XBIN_TOKEN_OPAREN:
- _tok._type = OPEN_PAREN;
- break;
- case XBIN_TOKEN_CPAREN:
- _tok._type = CLOSE_PAREN;
- break;
- case XBIN_TOKEN_OBRACKET:
- _tok._type = OPEN_BRACKET;
- break;
- case XBIN_TOKEN_CBRACKET:
- _tok._type = CLOSE_BRACKET;
- break;
- case XBIN_TOKEN_OANGLE:
- _tok._type = OPEN_ANGLE;
- break;
- case XBIN_TOKEN_CANGLE:
- _tok._type = CLOSE_ANGLE;
- break;
- case XBIN_TOKEN_DOT:
- _tok._type = DOT;
- break;
- case XBIN_TOKEN_COMMA:
- _tok._type = COMMA;
- break;
- case XBIN_TOKEN_SEMICOLON:
- _tok._type = SEMICOLON;
- break;
- case XBIN_TOKEN_TEMPLATE:
- _tok._textVal = "template";
- _tok._type = IDENTIFIER;
- break;
- case XBIN_TOKEN_WORD:
- break;
- case XBIN_TOKEN_DWORD:
- break;
- case XBIN_TOKEN_FLOAT:
- break;
- case XBIN_TOKEN_DOUBLE:
- break;
- case XBIN_TOKEN_CHAR:
- break;
- case XBIN_TOKEN_UCHAR:
- break;
- case XBIN_TOKEN_SWORD:
- break;
- case XBIN_TOKEN_SDWORD:
- break;
- case XBIN_TOKEN_VOID:
- break;
- case XBIN_TOKEN_LPSTR:
- break;
- case XBIN_TOKEN_UNICODE:
- break;
- case XBIN_TOKEN_CSTRING:
- break;
- case XBIN_TOKEN_ARRAY:
- break;
- case 0:
- _tok._type = NULL_CHAR;
- break;
- default:
- _tok._type = UNKNOWN_TOKEN;
- warning("XFileLexer::nextBinaryToken: Unknown token encountered");
- }
-}
-
-XFileLexer::XFileLexer(byte *buffer, uint32 fileSize, bool isText)
- : _buffer(buffer, fileSize), _lineCount(1), _isText(isText), _integersToRead(0), _floatsToRead(0), _expectsTerminator(true) {
-}
-
-void XFileLexer::advanceToNextToken() {
- _tok._textVal.clear();
-
- if (_isText) {
- nextTokenText(_buffer, _lineCount, _tok);
- } else {
- nextTokenBinary();
- }
-}
-
-void XFileLexer::skipTerminator() {
- if (_expectsTerminator) {
- advanceToNextToken();
- }
-
- _expectsTerminator = (_floatsToRead == 0) && (_integersToRead == 0);
-}
-
-bool XFileLexer::eof() {
- return _buffer.pos() == _buffer.size();
-}
-
-bool XFileLexer::tokenIsIdentifier() {
- return _tok._type == IDENTIFIER;
-}
-
-bool XFileLexer::tokenIsIdentifier(const char *val) {
- return _tok._type == IDENTIFIER && _tok._textVal == val;
-}
-
-void XFileLexer::advanceOnOpenBraces() {
- if (_tok._type == OPEN_BRACES) {
- advanceToNextToken();
- }
-}
-
-bool XFileLexer::reachedClosedBraces() {
- return _tok._type == CLOSE_BRACES;
-}
-
-TokenType XFileLexer::getTypeOfToken() {
- return _tok._type;
-}
-
-bool XFileLexer::tokenIsOfType(TokenType type) {
- return _tok._type == type;
-}
-
-int XFileLexer::tokenToInt() {
- return atoi(_tok._textVal.c_str());
-}
-
-double XFileLexer::tokenToFloat() {
- return atof(_tok._textVal.c_str());
-}
-
-Common::String XFileLexer::tokenToString() {
- return _tok._textVal;
-}
-
-uint32 XFileLexer::tokenToUint32() {
- // All integer values in a .X file are unsigned and at most 32 bit
- // so parsing to unsigned long and then converting down should be fine
- return strtoul(_tok._textVal.c_str(), nullptr, 10);
-}
-
-void XFileLexer::skipObject() {
- advanceToNextToken(); // optional name
- advanceToNextToken();
- advanceOnOpenBraces();
-
- // we have one open braces right now, once the counter reaches zero, we're done
- int closedBracesCount = 1;
-
- while (closedBracesCount > 0) {
- while (_integersToRead > 0 || _floatsToRead > 0) {
- if (_integersToRead > 0) {
- readInt();
- }
-
- if (_floatsToRead > 0) {
- readFloat();
- }
- }
-
- if (_tok._type == OPEN_BRACES) {
- ++closedBracesCount;
- }
-
- if (_tok._type == CLOSE_BRACES) {
- --closedBracesCount;
- }
-
- advanceToNextToken();
- }
-}
-
-void Token::pushChar(char c) {
- _textVal.insertChar(c, _textVal.size());
-}
-
-float XFileLexer::readFloat() {
- if (_floatsToRead > 0) {
- --_floatsToRead;
- float tmp = _buffer.readFloatLE();
-
- if (_floatsToRead == 0) {
- advanceToNextToken();
- }
-
- return tmp;
- }
-
- float tmp = tokenToFloat();
- advanceToNextToken();
- advanceToNextToken(); // skip comma or semicolon
- return tmp;
-}
-
-int XFileLexer::readInt() {
- if (_integersToRead > 0) {
- --_integersToRead;
- int tmp = _buffer.readUint32LE();
-
- if (_integersToRead == 0) {
- advanceToNextToken();
- }
-
- return tmp;
- }
-
- int tmp = tokenToInt();
- advanceToNextToken();
- advanceToNextToken(); // skip comma or semicolon
- return tmp;
-}
-
-Common::String XFileLexer::readString() {
- Common::String tmp = tokenToString();
- advanceToNextToken();
- advanceToNextToken(); // skip comma or semicolon
- return tmp;
-}
-
-uint32 XFileLexer::readUint32() {
- if (_integersToRead > 0) {
- --_integersToRead;
- uint32 tmp = _buffer.readUint32LE();
-
- if (_integersToRead == 0) {
- advanceToNextToken();
- }
-
- return tmp;
- }
-
- uint32 tmp = tokenToUint32();
- advanceToNextToken();
- advanceToNextToken(); // skip comma or semicolon
- return tmp;
-}
-
-} // namespace Wintermute
diff --git a/engines/wintermute/base/gfx/xloader.h b/engines/wintermute/base/gfx/xloader.h
deleted file mode 100644
index 65acc0a247d..00000000000
--- a/engines/wintermute/base/gfx/xloader.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* 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 WINTERMUTE_X_FILE_LEXER_H
-#define WINTERMUTE_X_FILE_LEXER_H
-
-#include "common/memstream.h"
-#include "common/scummsys.h"
-#include "common/str.h"
-
-namespace Wintermute {
-
-enum TokenType {
- IDENTIFIER = 0,
- STRING,
- UUID,
- INT,
- FLOAT,
- OPEN_BRACES,
- CLOSE_BRACES,
- OPEN_PAREN,
- CLOSE_PAREN,
- OPEN_ANGLE,
- CLOSE_ANGLE,
- OPEN_BRACKET,
- CLOSE_BRACKET,
- SEMICOLON,
- COMMA,
- DOT,
- NULL_CHAR,
- UNKNOWN_TOKEN
-};
-
-struct Token {
- TokenType _type;
- Common::String _textVal;
- int _integerVal;
- float _floatVal;
-
- void pushChar(char c);
-};
-
-class XFileLexer {
-public:
- XFileLexer(byte *buffer, uint32 fileSize, bool isText);
-
- void advanceToNextToken();
- void skipTerminator();
- bool eof();
- bool tokenIsIdentifier();
- bool tokenIsIdentifier(const char *val);
- void advanceOnOpenBraces();
- bool reachedClosedBraces();
- TokenType getTypeOfToken();
- bool tokenIsOfType(TokenType type);
- int tokenToInt();
- double tokenToFloat();
- Common::String tokenToString();
- uint32 tokenToUint32();
-
- void skipObject();
-
- float readFloat();
- int readInt();
- Common::String readString();
- uint32 readUint32();
-
-private:
- void nextTokenBinary();
-
- Token _tok;
- Common::MemoryReadStream _buffer;
- int _lineCount;
- bool _isText;
- int _integersToRead;
- int _floatsToRead;
- bool _expectsTerminator;
-};
-
-} // namespace Wintermute
-
-#endif
diff --git a/engines/wintermute/base/gfx/xmaterial.cpp b/engines/wintermute/base/gfx/xmaterial.cpp
index f7e3ef25e48..144afccf52d 100644
--- a/engines/wintermute/base/gfx/xmaterial.cpp
+++ b/engines/wintermute/base/gfx/xmaterial.cpp
@@ -30,7 +30,7 @@
#include "engines/wintermute/base/base_surface_storage.h"
#include "engines/wintermute/base/gfx/base_surface.h"
#include "engines/wintermute/base/gfx/xmaterial.h"
-#include "engines/wintermute/base/gfx/xloader.h"
+#include "engines/wintermute/base/gfx/xfile_loader.h"
#include "engines/wintermute/dcgf.h"
#include "engines/wintermute/utils/path_util.h"
#include "engines/wintermute/video/video_theora_player.h"
@@ -141,51 +141,44 @@ BaseSurface *Material::getSurface() {
}
}
-bool Material::loadFromX(XFileLexer &lexer, const Common::String &filename) {
- lexer.advanceToNextToken(); // skip optional name
- lexer.advanceOnOpenBraces();
+bool Material::loadFromX(XFileData *xobj, const Common::String &filename) {
+ XMaterialObject *material = xobj->getXMaterialObject();
+ if (!material)
+ return false;
- _diffuse.r() = lexer.readFloat();
- _diffuse.g() = lexer.readFloat();
- _diffuse.b() = lexer.readFloat();
- _diffuse.a() = lexer.readFloat();
- lexer.skipTerminator(); // skip semicolon
+ _diffuse.r() = material->_colorR;
+ _diffuse.g() = material->_colorG;
+ _diffuse.b() = material->_colorB;
+ _diffuse.a() = material->_colorA;
- _shininess = lexer.readFloat();
+ _shininess = material->_power;
- _specular.r() = lexer.readFloat();
- _specular.g() = lexer.readFloat();
- _specular.b() = lexer.readFloat();
+ _specular.r() = material->_specularR;
+ _specular.g() = material->_specularG;
+ _specular.b() = material->_specularB;
_specular.a() = 1.0f;
- lexer.skipTerminator(); // skip semicolon
- _emissive.r() = lexer.readFloat();
- _emissive.g() = lexer.readFloat();
- _emissive.b() = lexer.readFloat();
+ _emissive.r() = material->_emissiveR;
+ _emissive.g() = material->_emissiveG;
+ _emissive.b() = material->_emissiveB;
_emissive.a() = 1.0f;
- lexer.skipTerminator();
-
- while (!lexer.eof()) {
- // according to assimp sources, we got both possibilities
- // wine also seems to support this
- // MSDN only names the first option
- if (lexer.tokenIsIdentifier("TextureFilename") || lexer.tokenIsIdentifier("TextureFileName")) {
- lexer.advanceToNextToken(); // skip optional name
- lexer.advanceOnOpenBraces();
-
- Common::String textureFilename = lexer.readString();
- PathUtil::getDirectoryName(filename);
- setTexture(PathUtil::getDirectoryName(filename) + textureFilename);
- lexer.advanceToNextToken(); // skip semicolon
- } else if (lexer.tokenIsIdentifier()) {
- warning("Material::loadFromX unexpected token %i", lexer.getTypeOfToken());
- return false;
- } else {
- break;
+
+ uint32 numChildren = 0;
+ xobj->getChildren(numChildren);
+
+ for (uint32 i = 0; i < numChildren; i++) {
+ XFileData xchildData;
+ XClassType objectType;
+ bool res = xobj->getChild(i, xchildData);
+ if (res) {
+ res = xchildData.getType(objectType);
+ if (res && objectType == kXClassTextureFilename) {
+ Common::String textureFilename = xchildData.getXTextureFilenameObject()->_filename;
+ setTexture(PathUtil::getDirectoryName(filename) + textureFilename);
+ }
}
}
- lexer.advanceToNextToken(); // skip semicolon
return true;
}
diff --git a/engines/wintermute/base/gfx/xmaterial.h b/engines/wintermute/base/gfx/xmaterial.h
index 566cff160e3..540399291ec 100644
--- a/engines/wintermute/base/gfx/xmaterial.h
+++ b/engines/wintermute/base/gfx/xmaterial.h
@@ -35,7 +35,7 @@ namespace Wintermute {
class BaseSprite;
class BaseSurface;
class VideoTheoraPlayer;
-class XFileLexer;
+class XFileData;
struct ColorValue {
float &r() {
@@ -89,7 +89,7 @@ public:
bool setTheora(VideoTheoraPlayer *theora, bool adoptName = false);
BaseSurface *getSurface();
- bool loadFromX(XFileLexer &lexer, const Common::String &filename);
+ bool loadFromX(XFileData *xobj, const Common::String &filename);
bool invalidateDeviceObjects();
bool restoreDeviceObjects();
diff --git a/engines/wintermute/base/gfx/xmesh.cpp b/engines/wintermute/base/gfx/xmesh.cpp
index 4a5574e0a60..d0e7387c09c 100644
--- a/engines/wintermute/base/gfx/xmesh.cpp
+++ b/engines/wintermute/base/gfx/xmesh.cpp
@@ -29,8 +29,9 @@
#include "engines/wintermute/base/gfx/xmaterial.h"
#include "engines/wintermute/base/gfx/xmesh.h"
#include "engines/wintermute/base/gfx/xframe_node.h"
-#include "engines/wintermute/base/gfx/xloader.h"
+#include "engines/wintermute/base/gfx/xfile_loader.h"
#include "engines/wintermute/base/gfx/xmodel.h"
+#include "engines/wintermute/base/base_engine.h"
#include "engines/wintermute/math/math_util.h"
namespace Wintermute {
@@ -60,10 +61,20 @@ XMesh::~XMesh() {
}
//////////////////////////////////////////////////////////////////////////
-bool XMesh::loadFromX(const Common::String &filename, XFileLexer &lexer, Common::Array<MaterialReference> &materialReferences) {
- lexer.advanceToNextToken(); // skip the name
- lexer.advanceOnOpenBraces();
- _vertexCount = lexer.readInt();
+bool XMesh::loadFromXData(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences) {
+ // get name
+ if (!XModel::loadName(this, xobj)) {
+ BaseEngine::LOG(0, "Error loading mesh name");
+ return false;
+ }
+
+ XMeshObject *mesh = xobj->getXMeshObject();
+ if (!mesh) {
+ BaseEngine::LOG(0, "Error loading skin mesh");
+ return false;
+ }
+
+ _vertexCount = mesh->_numVertices;
// vertex format for .X meshes will be position + normals + textures
_vertexData = new float[kVertexComponentCount * _vertexCount]();
@@ -72,76 +83,47 @@ bool XMesh::loadFromX(const Common::String &filename, XFileLexer &lexer, Common:
// TODO: might have to generate normals if file does not contain any
_vertexNormalData = new float[3 * _vertexCount]();
- parsePositionCoords(lexer);
+ parsePositionCoords(mesh);
- int faceCount = lexer.readInt();
+ int faceCount = mesh->_numFaces;
Common::Array<int> indexCountPerFace;
- parseFaces(lexer, faceCount, indexCountPerFace);
-
- while (!lexer.eof()) {
- if (lexer.tokenIsIdentifier("MeshTextureCoords")) {
- lexer.advanceToNextToken();
- lexer.advanceToNextToken();
- lexer.advanceOnOpenBraces();
-
- parseTextureCoords(lexer);
- } else if (lexer.tokenIsIdentifier("MeshNormals")) {
- lexer.advanceToNextToken();
- lexer.advanceToNextToken();
- lexer.advanceOnOpenBraces();
-
- parseNormalCoords(lexer);
- } else if (lexer.tokenIsIdentifier("MeshMaterialList")) {
- lexer.advanceToNextToken();
- lexer.advanceToNextToken();
- lexer.advanceOnOpenBraces();
-
- parseMaterials(lexer, faceCount, filename, materialReferences, indexCountPerFace);
- } else if (lexer.tokenIsIdentifier("Material")) {
- lexer.advanceToNextToken();
- Material *mat = new Material(_gameRef);
- mat->loadFromX(lexer, filename);
- _materials.add(mat);
-
- // one material = one index range
- _numAttrs = 1;
- _indexRanges.push_back(0);
- _indexRanges.push_back(_indexData.size());
- } else if (lexer.tokenIsIdentifier("XSkinMeshHeader")) {
- lexer.advanceToNextToken();
- lexer.advanceOnOpenBraces();
-
- // if any of this is zero, we should have an unskinned mesh
- lexer.readInt(); // max skin weights per vertex
- lexer.readInt(); // max skin weights per face
- int boneCount = lexer.readInt();
-
- _skinnedMesh = boneCount > 0;
-
- lexer.advanceToNextToken(); // skip semicolon
- } else if (lexer.tokenIsIdentifier("SkinWeights")) {
- // but now we certainly should have a skinned mesh
- _skinnedMesh = true;
- lexer.advanceToNextToken();
- lexer.advanceOnOpenBraces();
-
- parseSkinWeights(lexer);
- } else if (lexer.tokenIsIdentifier("DeclData")) {
- lexer.advanceToNextToken();
- lexer.advanceToNextToken();
- lexer.advanceOnOpenBraces();
-
- parseVertexDeclaration(lexer);
- } else if (lexer.tokenIsIdentifier()) {
- lexer.skipObject();
- } else if (lexer.reachedClosedBraces()) {
- lexer.advanceToNextToken(); // skip closed braces
- break;
- } else {
- warning("XMesh::loadFromX unknown token %i encountered", lexer.getTypeOfToken());
- lexer.advanceToNextToken();
+ parseFaces(mesh, faceCount, indexCountPerFace);
+
+ uint32 numChildren = 0;
+ xobj->getChildren(numChildren);
+
+ for (uint32 i = 0; i < numChildren; i++) {
+ XFileData xchildData;
+ XClassType objectType;
+ if (xobj->getChild(i, xchildData)) {
+ if (xchildData.getType(objectType)) {
+ if (objectType == kXClassMeshTextureCoords) {
+ parseTextureCoords(&xchildData);
+ } else if (objectType == kXClassMeshNormals) {
+ parseNormalCoords(&xchildData);
+ } else if (objectType == kXClassMeshMaterialList) {
+ parseMaterials(&xchildData, faceCount, filename, materialReferences, indexCountPerFace);
+ } else if (objectType == kXClassMaterial) {
+ Material *mat = new Material(_gameRef);
+ mat->loadFromX(&xchildData, filename);
+ _materials.add(mat);
+
+ // one material = one index range
+ _numAttrs = 1;
+ _indexRanges.push_back(0);
+ _indexRanges.push_back(_indexData.size());
+ } else if (objectType == kXClassSkinMeshHeader) {
+ int boneCount = xchildData.getXSkinMeshHeaderObject()->_nBones;
+ _skinnedMesh = boneCount > 0;
+ } else if (objectType == kXClassSkinWeights) {
+ _skinnedMesh = true;
+ parseSkinWeights(&xchildData);
+ } else if (objectType == kXClassDeclData) {
+ parseVertexDeclaration(&xchildData);
+ }
+ }
}
}
@@ -468,43 +450,41 @@ bool XMesh::restoreDeviceObjects() {
}
}
-bool XMesh::parsePositionCoords(XFileLexer &lexer) {
+bool XMesh::parsePositionCoords(XMeshObject *mesh) {
for (uint i = 0; i < _vertexCount; ++i) {
+ _vertexPositionData[i * 3 + 0] = mesh->_vertices[i]._x;
+ _vertexPositionData[i * 3 + 1] = mesh->_vertices[i]._y;
+ _vertexPositionData[i * 3 + 2] = mesh->_vertices[i]._z;
for (int j = 0; j < 3; ++j) {
- _vertexPositionData[i * 3 + j] = lexer.readFloat();
_vertexData[i * kVertexComponentCount + kPositionOffset + j] = _vertexPositionData[i * 3 + j];
}
_vertexPositionData[i * 3 + 2] *= -1.0f;
_vertexData[i * kVertexComponentCount + kPositionOffset + 2] *= -1.0f;
-
- lexer.skipTerminator(); // skip semicolon
}
return true;
}
-bool XMesh::parseFaces(XFileLexer &lexer, int faceCount, Common::Array<int>& indexCountPerFace) {
+bool XMesh::parseFaces(XMeshObject *mesh, int faceCount, Common::Array<int> &indexCountPerFace) {
for (int i = 0; i < faceCount; ++i) {
- int indexCount = lexer.readInt();
-
+ XMeshFace *face = &mesh->_faces[i];
+ int indexCount = face->_numFaceVertexIndices;
if (indexCount == 3) {
- uint16 index1 = lexer.readInt();
- uint16 index2 = lexer.readInt();
- uint16 index3 = lexer.readInt();
+ uint16 index1 = face->_faceVertexIndices[0];
+ uint16 index2 = face->_faceVertexIndices[1];
+ uint16 index3 = face->_faceVertexIndices[2];
_indexData.push_back(index3);
_indexData.push_back(index2);
_indexData.push_back(index1);
- lexer.skipTerminator(); // skip semicolon
-
indexCountPerFace.push_back(3);
} else if (indexCount == 4) {
- uint16 index1 = lexer.readInt();
- uint16 index2 = lexer.readInt();
- uint16 index3 = lexer.readInt();
- uint16 index4 = lexer.readInt();
+ uint16 index1 = face->_faceVertexIndices[0];
+ uint16 index2 = face->_faceVertexIndices[1];
+ uint16 index3 = face->_faceVertexIndices[2];
+ uint16 index4 = face->_faceVertexIndices[3];
_indexData.push_back(index3);
_indexData.push_back(index2);
@@ -514,8 +494,6 @@ bool XMesh::parseFaces(XFileLexer &lexer, int faceCount, Common::Array<int>& ind
_indexData.push_back(index3);
_indexData.push_back(index1);
- lexer.skipTerminator(); // skip semicolon
-
indexCountPerFace.push_back(6);
} else {
warning("XMeshOpenGL::loadFromX faces with more than four vertices are not supported");
@@ -526,62 +504,59 @@ bool XMesh::parseFaces(XFileLexer &lexer, int faceCount, Common::Array<int>& ind
return true;
}
-bool XMesh::parseTextureCoords(XFileLexer &lexer) {
+bool XMesh::parseTextureCoords(XFileData *xobj) {
+ XMeshTextureCoordsObject *texCoords = xobj->getXMeshTextureCoordsObject();
+ if (!texCoords)
+ return false;
// should be the same as _vertexCount
- int textureCoordCount = lexer.readInt();
+ int textureCoordCount = texCoords->_numTextureCoords;
for (int i = 0; i < textureCoordCount; ++i) {
- _vertexData[i * kVertexComponentCount + kTextureCoordOffset + 0] = lexer.readFloat();
- _vertexData[i * kVertexComponentCount + kTextureCoordOffset + 1] = lexer.readFloat();
- lexer.skipTerminator(); // skip semicolon
- }
-
- if (lexer.reachedClosedBraces()) {
- lexer.advanceToNextToken();
- } else {
- warning("Missing } in mesh object");
+ _vertexData[i * kVertexComponentCount + kTextureCoordOffset + 0] = texCoords->_textureCoords[i]._u;
+ _vertexData[i * kVertexComponentCount + kTextureCoordOffset + 1] = texCoords->_textureCoords[i]._v;
}
return true;
}
-bool XMesh::parseNormalCoords(XFileLexer &lexer) {
+bool XMesh::parseNormalCoords(XFileData *xobj) {
+ XMeshNormalsObject *normals = xobj->getXMeshNormalsObject();
+ if (!normals)
+ return false;
// should be the same as _vertex count
- uint vertexNormalCount = lexer.readInt();
-// assert(vertexNormalCount == _vertexCount);
+ uint vertexNormalCount = normals->_numNormals;
+ //assert(vertexNormalCount == _vertexCount);
Common::Array<float> vertexNormalData;
vertexNormalData.resize(3 * vertexNormalCount);
for (uint i = 0; i < vertexNormalCount; ++i) {
- vertexNormalData[i * 3 + 0] = lexer.readFloat();
- vertexNormalData[i * 3 + 1] = lexer.readFloat();
+ vertexNormalData[i * 3 + 0] = normals->_normals[i]._x;
+ vertexNormalData[i * 3 + 1] = normals->_normals[i]._y;
// mirror z coordinate to change to OpenGL coordinate system
- vertexNormalData[i * 3 + 2] = -lexer.readFloat();
- lexer.skipTerminator(); // skip semicolon
+ vertexNormalData[i * 3 + 2] = -normals->_normals[i]._z;
}
- uint faceNormalCount = lexer.readInt();
+ uint faceNormalCount = normals->_numFaceNormals;
Common::Array<int> faceNormals;
for (uint i = 0; i < faceNormalCount; ++i) {
- int indexCount = lexer.readInt();
+ XMeshFace *normalFace = &normals->_faceNormals[i];
+ int indexCount = normalFace->_numFaceVertexIndices;
if (indexCount == 3) {
- uint16 index1 = lexer.readInt();
- uint16 index2 = lexer.readInt();
- uint16 index3 = lexer.readInt();
+ uint16 index1 = normalFace->_faceVertexIndices[0];
+ uint16 index2 = normalFace->_faceVertexIndices[1];
+ uint16 index3 = normalFace->_faceVertexIndices[2];
faceNormals.push_back(index3);
faceNormals.push_back(index2);
faceNormals.push_back(index1);
-
- lexer.skipTerminator(); // skip semicolon
} else if (indexCount == 4) {
- uint16 index1 = lexer.readInt();
- uint16 index2 = lexer.readInt();
- uint16 index3 = lexer.readInt();
- uint16 index4 = lexer.readInt();
+ uint16 index1 = normalFace->_faceVertexIndices[0];
+ uint16 index2 = normalFace->_faceVertexIndices[1];
+ uint16 index3 = normalFace->_faceVertexIndices[2];
+ uint16 index4 = normalFace->_faceVertexIndices[3];
faceNormals.push_back(index3);
faceNormals.push_back(index2);
@@ -590,8 +565,6 @@ bool XMesh::parseNormalCoords(XFileLexer &lexer) {
faceNormals.push_back(index4);
faceNormals.push_back(index3);
faceNormals.push_back(index1);
-
- lexer.skipTerminator(); // skip semicolon
} else {
warning("XMeshOpenGL::loadFromX faces with more than four vertices are not supported");
return false;
@@ -610,27 +583,28 @@ bool XMesh::parseNormalCoords(XFileLexer &lexer) {
}
}
- lexer.advanceToNextToken(); // skip closed braces
-
return true;
}
-bool XMesh::parseMaterials(XFileLexer &lexer, int faceCount, const Common::String &filename, Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace) {
+bool XMesh::parseMaterials(XFileData *xobj, int faceCount, const Common::String &filename, Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace) {
+ XMeshMaterialListObject *materialList = xobj->getXMeshMaterialListObject();
+ if (!materialList)
+ return false;
+
// there can be unused materials inside a .X file
// so this piece of information is probably useless
- lexer.readInt(); // material count
// should be the same as faceCount
- int faceMaterialCount = lexer.readInt();
+ int faceMaterialCount = materialList->_numFaceIndexes;
assert(faceMaterialCount == faceCount);
_indexRanges.push_back(0);
- int currentMaterialIndex = lexer.readInt();
+ int currentMaterialIndex = materialList->_faceIndexes[0];
_materialIndices.push_back(currentMaterialIndex);
int currentIndex = indexCountPerFace[0];
for (int i = 1; i < faceMaterialCount; ++i) {
- int currentMaterialIndexTmp = lexer.readInt();
+ int currentMaterialIndexTmp = materialList->_faceIndexes[i];
if (currentMaterialIndex != currentMaterialIndexTmp) {
currentMaterialIndex = currentMaterialIndexTmp;
@@ -644,82 +618,65 @@ bool XMesh::parseMaterials(XFileLexer &lexer, int faceCount, const Common::Strin
_indexRanges.push_back(currentIndex);
_numAttrs = _indexRanges.size() - 1;
- // Strange Change has an extra semicolon here
- if (lexer.tokenIsOfType(SEMICOLON)) {
- lexer.advanceToNextToken();
- }
-
- while (!lexer.eof()) {
- if (lexer.tokenIsIdentifier("Material")) {
- lexer.advanceToNextToken();
- Material *mat = new Material(_gameRef);
- mat->loadFromX(lexer, filename);
- _materials.add(mat);
-
- MaterialReference materialReference;
- materialReference._material = mat;
- materialReferences.push_back(materialReference);
- } else if (lexer.tokenIsIdentifier()) {
- while (!lexer.reachedClosedBraces()) {
- lexer.advanceToNextToken();
- }
-
- lexer.advanceToNextToken(); // skip closed braces
- } else if (lexer.reachedClosedBraces()) {
- break;
- } else if (lexer.tokenIsOfType(OPEN_BRACES)) {
- lexer.advanceToNextToken();
- Common::String materialReference = lexer.tokenToString();
-
- for (uint i = 0; i < materialReferences.size(); ++i) {
- if (materialReferences[i]._name == materialReference) {
- _materials.add(materialReferences[i]._material);
- break;
+ uint32 numChildren = 0;
+ xobj->getChildren(numChildren);
+
+ for (uint32 i = 0; i < numChildren; i++) {
+ XFileData xchildData;
+ XClassType objectType;
+ bool res = xobj->getChild(i, xchildData);
+ if (res) {
+ res = xchildData.getType(objectType);
+ if (res) {
+ if (xchildData.isReference()) {
+ Common::String materialReference;
+ xchildData.getName(materialReference);
+ for (uint32 j = 0; j < materialReferences.size(); j++) {
+ if (materialReferences[j]._name == materialReference) {
+ _materials.add(materialReferences[j]._material);
+ break;
+ }
+ }
+ } else if (objectType == kXClassMaterial) {
+ Material *mat = new Material(_gameRef);
+ mat->loadFromX(&xchildData, filename);
+ _materials.add(mat);
+ MaterialReference materialReference;
+ materialReference._material = mat;
+ materialReferences.push_back(materialReference);
}
}
-
- lexer.advanceToNextToken();
- lexer.advanceToNextToken();
- } else {
- warning("XMeshOpenGL::loadFromX unknown token %i encountered while loading materials", lexer.getTypeOfToken());
- break;
}
}
- lexer.advanceToNextToken(); // skip closed braces
-
return true;
}
-bool XMesh::parseSkinWeights(XFileLexer &lexer) {
+bool XMesh::parseSkinWeights(XFileData *xobj) {
+ XSkinWeightsObject *skinWeights = xobj->getXSkinWeightsObject();
+ if (!skinWeights)
+ return false;
+
skinWeightsList.resize(skinWeightsList.size() + 1);
SkinWeights &currSkinWeights = skinWeightsList.back();
- currSkinWeights._boneName = lexer.readString();
+ currSkinWeights._boneName = skinWeights->_transformNodeName;
- int weightCount = lexer.readInt();
+ int weightCount = skinWeights->_numWeights;
currSkinWeights._vertexIndices.resize(weightCount);
currSkinWeights._vertexWeights.resize(weightCount);
for (int i = 0; i < weightCount; ++i) {
- currSkinWeights._vertexIndices[i] = lexer.readInt();
- }
-
- if (weightCount == 0 && lexer.tokenIsOfType(SEMICOLON)) {
- lexer.advanceToNextToken();
+ currSkinWeights._vertexIndices[i] = skinWeights->_vertexIndices[i];
}
for (int i = 0; i < weightCount; ++i) {
- currSkinWeights._vertexWeights[i] = lexer.readFloat();
- }
-
- if (weightCount == 0 && lexer.tokenIsOfType(SEMICOLON)) {
- lexer.advanceToNextToken();
+ currSkinWeights._vertexWeights[i] = skinWeights->_weights[i];
}
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
- currSkinWeights._offsetMatrix(c, r) = lexer.readFloat();
+ currSkinWeights._offsetMatrix(c, r) = skinWeights->_matrixOffset[r * 4 + c];
}
}
@@ -734,14 +691,15 @@ bool XMesh::parseSkinWeights(XFileLexer &lexer) {
currSkinWeights._offsetMatrix(0, 2) *= -1.0f;
currSkinWeights._offsetMatrix(1, 2) *= -1.0f;
- lexer.skipTerminator(); // semicolon of matrix
- lexer.advanceToNextToken(); // closed braces of skin weights object
-
return true;
}
-bool XMesh::parseVertexDeclaration(XFileLexer &lexer) {
- int vertexElementCount = lexer.readInt();
+bool XMesh::parseVertexDeclaration(XFileData *xobj) {
+ XDeclDataObject *declData = xobj->getXDeclDataObject();
+ if (!declData)
+ return false;
+
+ int vertexElementCount = declData->_numElements;
// size of a vertex measured in four byte blocks
int vertexSize = 0;
@@ -749,11 +707,10 @@ bool XMesh::parseVertexDeclaration(XFileLexer &lexer) {
int textureOffset = -1;
for (int i = 0; i < vertexElementCount; ++i) {
- int type = lexer.readInt();
- int method = lexer.readInt();
- int usage = lexer.readInt();
- int usageIndex = lexer.readInt();
- lexer.skipTerminator();
+ int type = declData->_elements[i]._type;
+ int method = declData->_elements[i]._method;
+ int usage = declData->_elements[i]._usage;
+ int usageIndex = declData->_elements[i]._usageIndex;
debug("Vertex Element: Type: %i, Method: %i, Usage: %i, Usage index: %i", type, method, usage, usageIndex);
@@ -842,24 +799,14 @@ bool XMesh::parseVertexDeclaration(XFileLexer &lexer) {
}
}
- if (lexer.tokenIsOfType(SEMICOLON)) {
- lexer.advanceToNextToken();
- }
-
- int dataSize = lexer.readInt();
+ int dataSize = declData->_numData;
Common::Array<uint32> data;
data.reserve(dataSize);
for (int i = 0; i < dataSize; ++i) {
- data.push_back(lexer.readUint32());
+ data.push_back(declData->_data[i]);
}
- if (lexer.tokenIsOfType(SEMICOLON)) {
- lexer.advanceToNextToken();
- }
-
- lexer.advanceToNextToken(); // skip closed braces
-
assert(dataSize % vertexSize == 0);
assert(dataSize / vertexSize == static_cast<int>(_vertexCount));
diff --git a/engines/wintermute/base/gfx/xmesh.h b/engines/wintermute/base/gfx/xmesh.h
index 3de79ed2cac..17542d61142 100644
--- a/engines/wintermute/base/gfx/xmesh.h
+++ b/engines/wintermute/base/gfx/xmesh.h
@@ -43,7 +43,7 @@ class Material;
class XModel;
class ShadowVolume;
class VideoTheoraPlayer;
-class XFileLexer;
+struct XMeshObject;
struct SkinWeights {
Common::String _boneName;
@@ -57,7 +57,7 @@ public:
XMesh(BaseGame *inGame);
virtual ~XMesh();
- virtual bool loadFromX(const Common::String &filename, XFileLexer &lexer, Common::Array<MaterialReference> &materialReferences);
+ virtual bool loadFromXData(const Common::String &filename, XFileData *xobj, Common::Array<MaterialReference> &materialReferences);
bool findBones(FrameNode *rootFrame);
virtual bool update(FrameNode *parentFrame);
virtual bool render(XModel *model) = 0;
@@ -84,13 +84,13 @@ protected:
// anything which does not fit into 16 bits would we fine
static const uint32 kNullIndex = 0xFFFFFFFF;
- bool parsePositionCoords(XFileLexer &lexer);
- bool parseFaces(XFileLexer &lexer, int faceCount, Common::Array<int> &indexCountPerFace);
- bool parseTextureCoords(XFileLexer &lexer);
- bool parseNormalCoords(XFileLexer &lexer);
- bool parseMaterials(XFileLexer &lexer, int faceCount, const Common::String &filename, Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace);
- bool parseSkinWeights(XFileLexer &lexer);
- bool parseVertexDeclaration(XFileLexer &lexer);
+ bool parsePositionCoords(XMeshObject *mesh);
+ bool parseFaces(XMeshObject *mesh, int faceCount, Common::Array<int> &indexCountPerFace);
+ bool parseTextureCoords(XFileData *xobj);
+ bool parseNormalCoords(XFileData *xobj);
+ bool parseMaterials(XFileData *xobj, int faceCount, const Common::String &filename, Common::Array<MaterialReference> &materialReferences, const Common::Array<int> &indexCountPerFace);
+ bool parseSkinWeights(XFileData *xobj);
+ bool parseVertexDeclaration(XFileData *xobj);
void updateBoundingBox();
diff --git a/engines/wintermute/base/gfx/xmodel.cpp b/engines/wintermute/base/gfx/xmodel.cpp
index 407d7068835..e3f444a79ba 100644
--- a/engines/wintermute/base/gfx/xmodel.cpp
+++ b/engines/wintermute/base/gfx/xmodel.cpp
@@ -29,6 +29,7 @@
#include "engines/wintermute/base/base_file_manager.h"
#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_engine.h"
#include "engines/wintermute/base/base_parser.h"
#include "engines/wintermute/base/gfx/base_renderer.h"
#include "engines/wintermute/base/gfx/opengl/base_render_opengl3d.h"
@@ -38,7 +39,8 @@
#include "engines/wintermute/base/gfx/xframe_node.h"
#include "engines/wintermute/base/gfx/xmaterial.h"
#include "engines/wintermute/base/gfx/xmodel.h"
-#include "engines/wintermute/base/gfx/xloader.h"
+#include "engines/wintermute/base/gfx/xfile.h"
+#include "engines/wintermute/base/gfx/xfile_loader.h"
#include "engines/wintermute/dcgf.h"
#include "engines/wintermute/utils/path_util.h"
#include "engines/wintermute/utils/utils.h"
@@ -46,106 +48,6 @@
namespace Wintermute {
-static const int kCabBlockSize = 0x8000;
-static const int kCabInputmax = kCabBlockSize + 12;
-
-static byte *DecompressMsZipData(byte *buffer, uint32 inputSize, uint32 &decompressedSize) {
-#ifdef USE_ZLIB
- bool error = false;
-
- Common::MemoryReadStream data(buffer, inputSize);
- byte *compressedBlock = new byte[kCabInputmax];
- byte *decompressedBlock = new byte[kCabBlockSize];
-
- // Read decompressed size of compressed data and minus 16 bytes of xof header
- decompressedSize = data.readUint32LE() - 16;
-
- uint32 remainingData = inputSize;
- uint32 decompressedPos = 0;
- byte *decompressedData = new byte[decompressedSize];
- if (!decompressedData)
- error = true;
-
- while (!error && remainingData) {
- uint16 uncompressedLen = data.readUint16LE();
- uint16 compressedLen = data.readUint16LE();
- remainingData -= 4;
-
- if (data.err()) {
- error = true;
- break;
- }
-
- if (remainingData == 0) {
- break;
- }
-
- if (compressedLen > kCabInputmax || uncompressedLen > kCabBlockSize) {
- error = true;
- break;
- }
-
- // Read the compressed block
- if (data.read(compressedBlock, compressedLen) != compressedLen) {
- error = true;
- break;
- }
- remainingData -= compressedLen;
-
- // Check the CK header
- if (compressedBlock[0] != 'C' || compressedBlock[1] != 'K') {
- error = true;
- break;
- }
-
- // Decompress the block. If it isn't the first, provide the previous block as dictonary
- byte *dict = decompressedPos ? decompressedBlock : nullptr;
- bool decRes = Common::inflateZlibHeaderless(decompressedBlock, uncompressedLen, compressedBlock + 2, compressedLen - 2, dict, kCabBlockSize);
- if (!decRes) {
- error = true;
- break;
- }
-
- // Copy the decompressed data
- memcpy(decompressedData + decompressedPos, decompressedBlock, uncompressedLen);
- decompressedPos += uncompressedLen;
- }
- if (decompressedSize != decompressedPos)
- error = true;
-
- delete[] compressedBlock;
- delete[] decompressedBlock;
-
- if (!error)
- return decompressedData;
-#endif
- warning("DecompressMsZipData: Error decompressing data!");
- decompressedSize = 0;
- return nullptr;
-}
-
-static XFileLexer createXFileLexer(byte *&buffer, uint32 fileSize) {
- // xof header of an .X file consists of 16 bytes
- // bytes 9 to 12 contain a string which can be 'txt ', 'bin ', 'bzip, 'tzip', depending on the format
- byte dataFormatBlock[5];
- Common::copy(buffer + 8, buffer + 12, dataFormatBlock);
- dataFormatBlock[4] = '\0';
-
- bool textMode = (strcmp((char *)dataFormatBlock, "txt ") == 0 || strcmp((char *)dataFormatBlock, "tzip") == 0);
-
- if (strcmp((char *)dataFormatBlock, "bzip") == 0 || strcmp((char *)dataFormatBlock, "tzip") == 0) {
- uint32 decompressedSize;
- // we skip the 16 bytes xof header of the file
- byte *buf = DecompressMsZipData(buffer + 16, fileSize - 16, decompressedSize);
- delete[] buffer;
- buffer = buf;
- return XFileLexer(buffer, decompressedSize, textMode);
- } else {
- // we skip the 16 bytes xof header of the file
- return XFileLexer(buffer + 16, fileSize - 16, textMode);
- }
-}
-
IMPLEMENT_PERSISTENT(XModel, false)
//////////////////////////////////////////////////////////////////////////
@@ -221,98 +123,197 @@ void XModel::cleanup(bool complete) {
bool XModel::loadFromFile(const Common::String &filename, XModel *parentModel) {
cleanup(false);
+ XFile *xfile = new XFile(_gameRef);
+ if (!xfile)
+ return false;
+
+ XFileData xobj;
+ bool resLoop = false;
+
_parentModel = parentModel;
- uint32 fileSize = 0;
- byte *buffer = BaseFileManager::getEngineInstance()->getEngineInstance()->readWholeFile(filename, &fileSize);
- XFileLexer lexer = createXFileLexer(buffer, fileSize);
+ bool res = xfile->openFile(filename);
+ if (!res) {
+ delete xfile;
+ //return false;
+ error("XModel: Error loading X file: %s", filename.c_str());
+ }
_rootFrame = new FrameNode(_gameRef);
- bool res = _rootFrame->loadFromXAsRoot(filename, lexer, this, _materialReferences);
+ uint32 numChildren = 0;
+ xfile->getEnum().getChildren(numChildren);
+ for (uint i = 0; i < numChildren; i++) {
+ resLoop = xfile->getEnum().getChild(i, xobj);
+ if (!resLoop)
+ break;
+
+ bool res = _rootFrame->loadFromXData(filename, this, &xobj, _materialReferences);
+ if (!res) {
+ BaseEngine::LOG(0, "Error loading top level object from '%s'", filename.c_str());
+ break;
+ }
+ }
+
+ if (!_rootFrame->hasChildren()) {
+ BaseEngine::LOG(0, "Error getting any top level objects in '%s'", filename.c_str());
+ res = false;
+ }
+
if (res) {
- findBones(false, parentModel);
+ res = findBones(false, parentModel);
}
+ // setup animation channels
for (int i = 0; i < X_NUM_ANIMATION_CHANNELS; ++i) {
_channels[i] = new AnimationChannel(_gameRef, this);
}
setFilename(filename.c_str());
- delete[] buffer;
+ delete xfile;
return res;
}
+//////////////////////////////////////////////////////////////////////////
bool XModel::mergeFromFile(const Common::String &filename) {
if (!_rootFrame) {
+ BaseEngine::LOG(0, "Error: XModel::mergeFromFile called on an empty model");
return false;
}
- uint32 fileSize = 0;
- byte *buffer = BaseFileManager::getEngineInstance()->getEngineInstance()->readWholeFile(filename, &fileSize);
- XFileLexer lexer = createXFileLexer(buffer, fileSize);
+ XFile *xfile = new XFile(_gameRef);
+ if (!xfile)
+ return false;
- lexer.advanceToNextToken();
- parseFrameDuringMerge(lexer, filename);
+ bool res = xfile->openFile(filename);
+ if (!res) {
+ delete xfile;
+ return false;
+ }
- findBones(false, nullptr);
+ XFileData xobj;
+ bool resLoop = false;
- bool found = false;
+ uint32 numChildren = 0;
+ xfile->getEnum().getChildren(numChildren);
+ for (uint i = 0; i < numChildren; i++) {
+ resLoop = xfile->getEnum().getChild(i, xobj);
+ if (!resLoop)
+ break;
+
+ res = _rootFrame->mergeFromXData(filename, this, &xobj);
+ if (!res) {
+ BaseEngine::LOG(0, "Error loading top level object from '%s'", filename.c_str());
+ break;
+ }
+ }
+
+ if (res) {
+ res = findBones(true);
+ }
+ bool found = false;
for (uint i = 0; i < _mergedModels.size(); ++i) {
if (scumm_stricmp(_mergedModels[i], filename.c_str()) == 0) {
found = true;
break;
}
}
-
if (!found) {
char *path = new char[filename.size() + 1];
strcpy(path, filename.c_str());
_mergedModels.add(path);
}
- delete[] buffer;
+ delete xfile;
return true;
}
//////////////////////////////////////////////////////////////////////////
-bool XModel::loadAnimationSet(XFileLexer &lexer, const Common::String &filename) {
+bool XModel::loadAnimationSet(const Common::String &filename, XFileData *xobj) {
bool res = true;
AnimationSet *animSet = new AnimationSet(_gameRef, this);
-
- if (animSet->loadFromX(lexer, filename)) {
- _animationSets.add(animSet);
- } else {
+ res = loadName(animSet, xobj);
+ if (!res) {
delete animSet;
- res = false;
+ return res;
}
- return res;
+ // use the filename for unnamed animation sets
+ if (animSet->_name[0] == '\0') {
+ animSet->setName(PathUtil::getFileName(filename).c_str());
+ }
+
+ XFileData xchildData;
+ XClassType objectType;
+
+ uint32 numChildren = 0;
+ xobj->getChildren(numChildren);
+
+ for (uint32 i = 0; i < numChildren; i++) {
+ _gameRef->miniUpdate();
+
+ res = xobj->getChild(i, xchildData);
+ if (res) {
+ res = xchildData.getType(objectType);
+ if (!res) {
+ delete animSet;
+ BaseEngine::LOG(0, "Error getting object type while loading animation set");
+ return res;
+ }
+
+ if (objectType == kXClassAnimation) {
+ res = loadAnimation(filename, &xchildData, animSet);
+ if (!res) {
+ delete animSet;
+ return res;
+ }
+ }
+ }
+ }
+
+ _animationSets.add(animSet);
+
+ return true;
}
//////////////////////////////////////////////////////////////////////////
-bool XModel::loadAnimation(const Common::String &filename, AnimationSet *parentAnimSet) {
- // not sure if we need this here (not completely implemented anyways and also not called)
- // are there animation objects in .X outside of an animation set?
-
+bool XModel::loadAnimation(const Common::String &filename, XFileData *xobj, AnimationSet *parentAnimSet) {
// if no parent anim set is specified, create one
bool newAnimSet = false;
if (parentAnimSet == nullptr) {
parentAnimSet = new AnimationSet(_gameRef, this);
parentAnimSet->setName(PathUtil::getFileName(filename).c_str());
+
newAnimSet = true;
}
// create the new object
- Animation *Anim = new Animation(_gameRef);
-
- parentAnimSet->addAnimation(Anim);
+ Animation *anim = new Animation(_gameRef);
+
+ uint32 numChildren = 0;
+ xobj->getChildren(numChildren);
+
+ for (uint32 i = 0; i < numChildren; i++) {
+ XFileData xchildData;
+ bool res = xobj->getChild(i, xchildData);
+ if (res) {
+ res = anim->load(&xchildData, parentAnimSet);
+ if (!res) {
+ delete anim;
+ if (newAnimSet) {
+ delete parentAnimSet;
+ }
+ return res;
+ }
+ }
+ }
+ parentAnimSet->addAnimation(anim);
if (newAnimSet) {
_animationSets.add(parentAnimSet);
@@ -340,22 +341,23 @@ bool XModel::findBones(bool animOnly, XModel *parentModel) {
return true;
}
-void XModel::parseFrameDuringMerge(XFileLexer &lexer, const Common::String &filename) {
- while (!lexer.eof()) {
- if (lexer.tokenIsIdentifier("Frame")) {
- lexer.advanceToNextToken();
- parseFrameDuringMerge(lexer, filename);
- } else if (lexer.tokenIsIdentifier("AnimationSet")) {
- lexer.advanceToNextToken();
- loadAnimationSet(lexer, filename);
- } else if (lexer.tokenIsOfType(IDENTIFIER)) {
- lexer.skipObject();
- } else {
- lexer.advanceToNextToken(); // we ignore anything else here
- }
+//////////////////////////////////////////////////////////////////////////
+bool XModel::loadName(BaseNamedObject *obj, XFileData *data) {
+ Common::String name;
+ if (data->getName(name)) {
+ obj->_name = new char[name.size() + 1];
+ Common::strlcpy(obj->_name, name.c_str(), name.size() + 1);
+ return true;
+ } else {
+ return false;
}
}
+//////////////////////////////////////////////////////////////////////////
+bool XModel::loadName(Common::String &targetStr, XFileData *data) {
+ return data->getName(targetStr);
+}
+
//////////////////////////////////////////////////////////////////////////
bool XModel::update() {
// reset all bones to default position
diff --git a/engines/wintermute/base/gfx/xmodel.h b/engines/wintermute/base/gfx/xmodel.h
index a4798ac600c..71186f654b7 100644
--- a/engines/wintermute/base/gfx/xmodel.h
+++ b/engines/wintermute/base/gfx/xmodel.h
@@ -46,7 +46,7 @@ class AnimationSet;
class FrameNode;
class Material;
class ShadowVolume;
-class XFileLexer;
+class XFileData;
struct MaterialReference {
Common::String _name;
@@ -152,8 +152,11 @@ public:
bool isTransparentAt(int x, int y);
- bool loadAnimationSet(XFileLexer &lexer, const Common::String &filename);
- bool loadAnimation(const Common::String &filename, AnimationSet *parentAnimSet);
+ static bool loadName(BaseNamedObject *obj, XFileData *data);
+ static bool loadName(Common::String &targetStr, XFileData *data);
+
+ bool loadAnimationSet(const Common::String &filename, XFileData *xobj);
+ bool loadAnimation(const Common::String &filename, XFileData *xobj, AnimationSet *parentAnimSet = nullptr);
Math::Matrix4 _lastWorldMat;
Rect32 _boundingRect;
@@ -186,8 +189,6 @@ private:
void cleanup(bool complete = true);
bool findBones(bool animOnly = false, XModel *parentModel = nullptr);
- void parseFrameDuringMerge(XFileLexer &lexer, const Common::String &filename);
-
void updateBoundingRect();
void static inline updateRect(Rect32 *rc, int32 x, int32 y);
Rect32 _drawingViewport;
diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk
index 8a930d19866..e5020797de0 100644
--- a/engines/wintermute/module.mk
+++ b/engines/wintermute/module.mk
@@ -168,11 +168,12 @@ MODULE_OBJS += \
base/gfx/xanimation.o \
base/gfx/xanimation_channel.o \
base/gfx/xanimation_set.o \
+ base/gfx/xfile.o \
+ base/gfx/xfile_loader.o \
base/gfx/xframe_node.o \
base/gfx/xmaterial.o \
base/gfx/xmesh.o \
base/gfx/xmodel.o \
- base/gfx/xloader.o \
base/gfx/opengl/base_surface_opengl3d.o \
base/gfx/opengl/base_render_opengl3d.o \
base/gfx/opengl/base_render_opengl3d_shader.o \
More information about the Scummvm-git-logs
mailing list