ofbx - tokanizer errors

This commit is contained in:
Mikulas Florek 2017-07-07 18:10:32 +02:00
parent 37fa393be4
commit 25a7323671
3 changed files with 143 additions and 55 deletions

View file

@ -545,7 +545,7 @@ struct FBXImporter
ofbx::IScene* scene = ofbx::load(&data[0], data.size());
if (!scene)
{
g_log_error.log("FBX") << "Failed to import \"" << filename;
g_log_error.log("FBX") << "Failed to import \"" << filename << ": " << ofbx::getError();
return false;
}

View file

@ -11,6 +11,38 @@ namespace ofbx
{
struct Error
{
Error() {}
Error(const char* msg) { s_message = msg; }
static const char* s_message;
};
const char* Error::s_message = "";
template <typename T>
struct OptionalError
{
OptionalError(Error error)
: is_error(true)
{}
OptionalError(T _value)
: value(_value)
, is_error(false)
{
}
T value;
bool is_error;
};
#pragma pack(1)
struct Header
{
@ -297,29 +329,25 @@ static void decompress(const u8* in, size_t in_size, u8* out, size_t out_size)
}
static u32 readWord(Cursor* cursor)
template <typename T>
static OptionalError<T> read(Cursor* cursor)
{
u32 word = *(const u32*)cursor->current;
cursor->current += 4;
return word;
if (cursor->current + sizeof(T) > cursor->end) return Error("Reading past the end");
T value = *(const T*)cursor->current;
cursor->current += sizeof(T);
return value;
}
static u8 readByte(Cursor* cursor)
{
u8 byte = *(const u8*)cursor->current;
cursor->current += 1;
return byte;
}
static DataView readShortString(Cursor* cursor)
static OptionalError<DataView> readShortString(Cursor* cursor)
{
DataView value;
const u8 length = readByte(cursor);
OptionalError<u8> length = read<u8>(cursor);
if (length.is_error) return Error();
if (cursor->current + length.value > cursor->end) return Error("Reading past the end");
value.begin = cursor->current;
cursor->current += length;
cursor->current += length.value;
value.end = cursor->current;
@ -327,13 +355,15 @@ static DataView readShortString(Cursor* cursor)
}
static DataView readLongString(Cursor* cursor)
static OptionalError<DataView> readLongString(Cursor* cursor)
{
DataView value;
const u32 length = readWord(cursor);
OptionalError<u32> length = read<u32>(cursor);
if (length.is_error) return Error();
if (cursor->current + length.value > cursor->end) return Error("Reading past the end");
value.begin = cursor->current;
cursor->current += length;
cursor->current += length.value;
value.end = cursor->current;
@ -341,7 +371,7 @@ static DataView readLongString(Cursor* cursor)
}
static Property* readProperty(Cursor* cursor)
static OptionalError<Property*> readProperty(Cursor* cursor)
{
Property* prop = new Property;
prop->next = nullptr;
@ -351,7 +381,17 @@ static Property* readProperty(Cursor* cursor)
switch (prop->type)
{
case 'S': prop->value = readLongString(cursor); break;
case 'S':
{
OptionalError<DataView> val = readLongString(cursor);
if (val.is_error)
{
delete prop;
return Error();
}
prop->value = val.value;
break;
}
case 'Y': cursor->current += 2; break;
case 'C': cursor->current += 1; break;
case 'I': cursor->current += 4; break;
@ -360,8 +400,13 @@ static Property* readProperty(Cursor* cursor)
case 'L': cursor->current += 8; break;
case 'R':
{
u32 len = readWord(cursor);
cursor->current += len;
OptionalError<u32> len = read<u32>(cursor);
if (!len.is_error)
{
delete prop;
return Error();
}
cursor->current += len.value;
break;
}
case 'b':
@ -370,63 +415,92 @@ static Property* readProperty(Cursor* cursor)
case 'l':
case 'i':
{
u32 length = readWord(cursor);
u32 encoding = readWord(cursor);
u32 comp_len = readWord(cursor);
cursor->current += comp_len;
OptionalError<u32> length = read<u32>(cursor);
OptionalError<u32> encoding = read<u32>(cursor);
OptionalError<u32> comp_len = read<u32>(cursor);
if (length.is_error || encoding.is_error || comp_len.is_error)
{
delete prop;
return Error();
}
cursor->current += comp_len.value;
break;
}
default: assert(0); break;
default:
delete prop;
return Error("Unknown property type");
}
prop->value.end = cursor->current;
return prop;
}
static Element* readElement(Cursor* cursor)
static OptionalError<Element*> readElement(Cursor* cursor)
{
u32 end_offset = readWord(cursor);
if (end_offset == 0) return nullptr;
u32 prop_count = readWord(cursor);
u32 prop_length = readWord(cursor);
OptionalError<u32> end_offset = read<u32>(cursor);
if (end_offset.is_error) return Error();
if (end_offset.value == 0) return nullptr;
OptionalError<u32> prop_count = read<u32>(cursor);
OptionalError<u32> prop_length = read<u32>(cursor);
if (prop_count.is_error || prop_length.is_error) return Error();
const char* sbeg = 0;
const char* send = 0;
DataView id = readShortString(cursor);
OptionalError<DataView> id = readShortString(cursor);
if (id.is_error) return Error();
Element* element = new Element;
element->first_property = nullptr;
element->id = id;
element->id = id.value;
element->child = nullptr;
element->sibling = nullptr;
Property** prop_link = &element->first_property;
for (u32 i = 0; i < prop_count; ++i)
for (u32 i = 0; i < prop_count.value; ++i)
{
*prop_link = readProperty(cursor);
OptionalError<Property*> prop = readProperty(cursor);
if (prop.is_error)
{
delete element;
return Error();
}
*prop_link = prop.value;
prop_link = &(*prop_link)->next;
}
if (cursor->current - cursor->begin >= end_offset) return element;
if (cursor->current - cursor->begin >= end_offset.value) return element;
enum
{
_BLOCK_SENTINEL_LENGTH = 13
};
constexpr int BLOCK_SENTINEL_LENGTH = 13;
Element** link = &element->child;
while (cursor->current - cursor->begin < (end_offset - _BLOCK_SENTINEL_LENGTH))
while (cursor->current - cursor->begin < (end_offset.value - BLOCK_SENTINEL_LENGTH))
{
*link = readElement(cursor);
OptionalError<Element*> child = readElement(cursor);
if (child.is_error)
{
delete element;
return Error();
}
*link = child.value;
link = &(*link)->sibling;
}
cursor->current += _BLOCK_SENTINEL_LENGTH;
if (cursor->current + BLOCK_SENTINEL_LENGTH > cursor->end)
{
delete element;
return Error("Reading past the end");
}
cursor->current += BLOCK_SENTINEL_LENGTH;
return element;
}
static Element* tokenize(const u8* data, size_t size)
static OptionalError<Element*> tokenize(const u8* data, size_t size)
{
Cursor cursor;
cursor.begin = data;
@ -446,7 +520,13 @@ static Element* tokenize(const u8* data, size_t size)
Element** element = &root->child;
for (;;)
{
*element = readElement(&cursor);
OptionalError<Element*> child = readElement(&cursor);
if (child.is_error)
{
delete root;
return Error();
}
*element = child.value;
if (!*element) return root;
element = &(*element)->sibling;
}
@ -1029,8 +1109,8 @@ struct Scene : IScene
}
Element* m_root_element;
Root* m_root;
Element* m_root_element = nullptr;
Root* m_root = nullptr;
std::unordered_map<u64, ObjectPair> m_object_map;
std::vector<Mesh*> m_meshes;
std::vector<AnimationStack*> m_animation_stacks;
@ -2003,19 +2083,26 @@ IScene* load(const u8* data, int size)
Scene* scene = new Scene;
scene->m_data.resize(size);
memcpy(&scene->m_data[0], data, size);
Element* root = tokenize(&scene->m_data[0], size);
if (!root)
OptionalError<Element*> root = tokenize(&scene->m_data[0], size);
if (root.is_error)
{
delete scene;
return nullptr;
}
scene->m_root_element = root;
parseTemplates(*root);
parseConnections(*root, scene);
assert(root.value);
scene->m_root_element = root.value;
parseTemplates(*root.value);
parseConnections(*root.value, scene);
parseTakes(scene);
parseObjects(*root, scene);
parseObjects(*root.value, scene);
return scene;
}
const char* getError()
{
return Error::s_message;
}
} // namespace ofbx

View file

@ -333,6 +333,7 @@ struct IScene
IScene* load(const u8* data, int size);
const char* getError();
} // namespace ofbx