ofbx - tokanizer errors
This commit is contained in:
parent
37fa393be4
commit
25a7323671
3 changed files with 143 additions and 55 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -333,6 +333,7 @@ struct IScene
|
|||
|
||||
|
||||
IScene* load(const u8* data, int size);
|
||||
const char* getError();
|
||||
|
||||
|
||||
} // namespace ofbx
|
Loading…
Reference in a new issue