/* * Copyright (C) 2022 Riyyi * * SPDX-License-Identifier: MIT */ #include // strcmp ?? #include // std::ifstream #include // std::ios #include "ruc/file.h" #include "ruc/meta/assert.h" #include "inferno/io/gltffile.h" #include "inferno/util/json.h" namespace Inferno { std::pair, std::shared_ptr> GltfFile::read(const std::string& path) { std::string extension = path.substr(path.find_first_of(".")); if (extension.compare(".glb") == 0) { return glb(path); } else if (extension.compare(".gltf") == 0) { return { ruc::File::raw(path), nullptr }; } VERIFY(false, "GltfFile unknown file extension!"); return {}; } std::pair, std::shared_ptr> GltfFile::glb(const std::string& path) { // Create input stream object and open file std::ifstream glb(path, std::ios::in | std::ios::binary); VERIFY(glb.is_open(), "GltfFile could not open '{}'", path); constexpr uint32_t size = 4; constexpr uint32_t header = 12; constexpr uint32_t json = 27; // Minimum valid glTF has an asset property with version specifier // Get the actual length of the file uint32_t length = static_cast(ruc::File::length(path, glb)); VERIFY(length > header + json, "GltfFile too small to be valid '{}'", path); // Read header char magic[size]; char version[size]; char fileLength[size]; glb.read(magic, size); glb.seekg(size * 1); glb.read(version, size); glb.seekg(size * 2); glb.read(fileLength, size); // Validate header uint32_t magicInt = *reinterpret_cast(magic); VERIFY(magicInt == 0x46546c67, "Gltf invalid header magic '{}'", magic); uint32_t versionInt = *reinterpret_cast(version); VERIFY(versionInt == 2, "Gltf unsupported version '{}'", versionInt); uint32_t fileLengthInt = *reinterpret_cast(fileLength); VERIFY(fileLengthInt == static_cast(length), "Gltf file and reported byte size mismatch '{}' '{}'", length, fileLengthInt); // Read JSON data auto jsonChunk = readChunk(glb, header, 0x4e4f534a); VERIFY(jsonChunk.second >= json, "Gltf file invalid JSON content length '{}'", jsonChunk.second); // Read binary data auto binaryChunk = readChunk(glb, header + size * 2 + jsonChunk.second, 0x004e4942); glb.close(); return { jsonChunk.first, binaryChunk.first }; } std::pair, uint32_t> GltfFile::readChunk(std::ifstream& ifstream, uint32_t offset, uint32_t type) { constexpr uint32_t size = 4; char chunkLength[size]; char chunkType[size]; ifstream.seekg(offset); ifstream.read(chunkLength, size); ifstream.seekg(offset + size * 1); ifstream.read(chunkType, size); uint32_t chunkTypeInt = *reinterpret_cast(chunkType); VERIFY(chunkTypeInt == type, "Gltf invalid chunk type '{}' != '{:#08x}'", chunkType, type); uint32_t chunkLengthInt = *reinterpret_cast(chunkLength); // Allocate memory filled with zeros std::shared_ptr chunkData(new char[chunkLengthInt + 1]); ifstream.seekg(offset + size * 2); ifstream.read(chunkData.get(), chunkLengthInt); chunkData.get()[chunkLengthInt] = '\0'; return { chunkData, chunkLengthInt }; } } // namespace Inferno