void GLBResourceReader::Init()

in GLTFSDK/Source/GLBResourceReader.cpp [95:173]


void GLBResourceReader::Init()
{
    // Get the length of the stream before reading anything, to validate against later
    // NOTE: The approach used below with seekg to the end and then tellg may be problematic since
    // seekg is not guaranteed to give the number of bytes from the start of the file:
    // see http://stackoverflow.com/a/22986486. This is used elsewhere in the code though,
    // and seems to Do The Right Thing as of right now. TODO: discuss fixing this problem.
    const auto curPos = m_buffer->tellg();
    m_buffer->seekg(0, std::ios::end);
    uint32_t trueStreamLength = static_cast<uint32_t>(m_buffer->tellg()); // in bytes (i.e. sizeof(char))
    m_buffer->seekg(curPos); // reset the stream pointer where it was

    char magic[GLB_HEADER_MAGIC_STRING_SIZE];
    m_buffer->read(magic, sizeof(magic));
    if (m_buffer->fail())
    {
        throw InvalidGLTFException("Cannot read the magic number");
    }

    const uint32_t version = StreamUtils::ReadBinary<uint32_t>(*m_buffer);
    const uint32_t length = StreamUtils::ReadBinary<uint32_t>(*m_buffer);

    // Verify that the length we just read actually matches the length of the stream
    if (trueStreamLength != length)
    {
        throw InvalidGLTFException("File-reported file length does not match actual file length");
    }

    const uint32_t jsonChunkLength = StreamUtils::ReadBinary<uint32_t>(*m_buffer);

    if (!ParseChunkType(GLB_CHUNK_TYPE_JSON, *m_buffer))
    {
        throw InvalidGLTFException("JSON chunk should appear first");
    }

    // validate header
    if (strncmp(magic, GLB_HEADER_MAGIC_STRING, GLB_HEADER_MAGIC_STRING_SIZE) != 0)
    {
        throw InvalidGLTFException("Cannot find GLB magic bytes");
    }

    if (version != GLB_HEADER_VERSION_2)
    {
        throw InvalidGLTFException("Unsupported GLB Version: " + std::to_string(version));
    }

    // Length has been validated as the actual length of the file, but make sure to include the header bytes in this check
    if (length < (GLB_HEADER_BYTE_SIZE + jsonChunkLength))
    {
        throw InvalidGLTFException("File length " + std::to_string(length) + " less than content length " + std::to_string(jsonChunkLength) + 
            " plus header length " + std::to_string(GLB_HEADER_BYTE_SIZE));
    }

    m_json = ReadJson(*m_buffer, jsonChunkLength);

    // If length is exactly equal to the json chunk length, plus the header, it means there is no binary buffer chunk
    if (length == (GLB_HEADER_BYTE_SIZE + jsonChunkLength))
    {
        return;
    }

    // Read the length of the binary buffer chunk
    const uint32_t bufferChunkLength = StreamUtils::ReadBinary<uint32_t>(*m_buffer);

    if (!ParseChunkType(GLB_CHUNK_TYPE_BIN, *m_buffer))
    {
        throw InvalidGLTFException("Binary chunk should appear second");
    }

    // Verify that the sum of the sizes of the chunks (plus the headers) matches the size of the file
    const uint32_t chunkSizeSum = GLB_HEADER_BYTE_SIZE + jsonChunkLength + sizeof(bufferChunkLength) + GLB_CHUNK_TYPE_SIZE +  bufferChunkLength;

    if (chunkSizeSum != length)
    {
        throw InvalidGLTFException("File length does not match sum of length of component chunks");
    }

    m_bufferOffset = m_buffer->tellg();
}