Files
2026-02-02 04:50:13 +01:00

1515 lines
34 KiB
C++

/* 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/compression/deflate.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;
_bufferLeft = 0;
_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() {
bool error = false;
byte *compressedBlock = new byte[kCabInputmax];
byte *decompressedBlock = new byte[kCabBlockSize];
uint32 decompressedSize = 0;
if (!readLE32(&decompressedSize)) {
error = true;
} else {
if (decompressedSize < 16) {
delete[] compressedBlock;
delete[] decompressedBlock;
return false;
}
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;
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 XVector3[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 XVector3[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