[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