mirror of
https://git.mirrors.martin98.com/https://github.com/syoyo/tinygltf.git
synced 2025-08-14 23:06:14 +08:00
Merge pull request #382 from syoyo/glb-zero-chunk
Fix parsing GLB file with empty Chunk1(BIN data).
This commit is contained in:
commit
18450eafe7
7
.gitignore
vendored
7
.gitignore
vendored
@ -68,4 +68,11 @@ loader_example
|
|||||||
tests/tester
|
tests/tester
|
||||||
tests/tester_noexcept
|
tests/tester_noexcept
|
||||||
tests/issue-97.gltf
|
tests/issue-97.gltf
|
||||||
|
tests/issue-261.gltf
|
||||||
|
|
||||||
|
# unignore
|
||||||
|
!Makefile
|
||||||
|
!examples/build-gltf/Makefile
|
||||||
|
!examples/raytrace/cornellbox_suzanne.obj
|
||||||
|
!tests/Makefile
|
||||||
|
!tools/windows/premake5.exe
|
||||||
|
@ -482,3 +482,61 @@ TEST_CASE("expandpath-utf-8", "[pr-226]") {
|
|||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("empty-bin-buffer", "[issue-382]") {
|
||||||
|
tinygltf::Model model;
|
||||||
|
tinygltf::TinyGLTF ctx;
|
||||||
|
std::string err;
|
||||||
|
std::string warn;
|
||||||
|
|
||||||
|
tinygltf::Model model_empty;
|
||||||
|
std::stringstream stream;
|
||||||
|
bool ret = ctx.WriteGltfSceneToStream(&model_empty, stream, false, true);
|
||||||
|
REQUIRE(ret == true);
|
||||||
|
std::string str = stream.str();
|
||||||
|
const unsigned char* bytes = (unsigned char*)str.data();
|
||||||
|
ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, bytes, str.size());
|
||||||
|
if (!err.empty()) {
|
||||||
|
std::cerr << err << std::endl;
|
||||||
|
}
|
||||||
|
REQUIRE(true == ret);
|
||||||
|
|
||||||
|
err.clear();
|
||||||
|
warn.clear();
|
||||||
|
|
||||||
|
tinygltf::Model model_empty_buffer;
|
||||||
|
model_empty_buffer.buffers.push_back(tinygltf::Buffer());
|
||||||
|
stream = std::stringstream();
|
||||||
|
ret = ctx.WriteGltfSceneToStream(&model_empty_buffer, stream, false, true);
|
||||||
|
REQUIRE(ret == true);
|
||||||
|
str = stream.str();
|
||||||
|
bytes = (unsigned char*)str.data();
|
||||||
|
ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, bytes, str.size());
|
||||||
|
if (err.empty()) {
|
||||||
|
std::cerr << "there should have been an error reported" << std::endl;
|
||||||
|
}
|
||||||
|
REQUIRE(false == ret);
|
||||||
|
|
||||||
|
err.clear();
|
||||||
|
warn.clear();
|
||||||
|
|
||||||
|
tinygltf::Model model_single_byte_buffer;
|
||||||
|
tinygltf::Buffer buffer;
|
||||||
|
buffer.data.push_back(0);
|
||||||
|
model_single_byte_buffer.buffers.push_back(buffer);
|
||||||
|
stream = std::stringstream();
|
||||||
|
ret = ctx.WriteGltfSceneToStream(&model_single_byte_buffer, stream, false, true);
|
||||||
|
REQUIRE(ret == true);
|
||||||
|
str = stream.str();
|
||||||
|
{
|
||||||
|
std::ofstream ofs("tmp.glb");
|
||||||
|
ofs.write(str.data(), str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = (unsigned char*)str.data();
|
||||||
|
ret = ctx.LoadBinaryFromMemory(&model_single_byte_buffer, &err, &warn, bytes, str.size());
|
||||||
|
if (!err.empty()) {
|
||||||
|
std::cerr << err << std::endl;
|
||||||
|
}
|
||||||
|
REQUIRE(true == ret);
|
||||||
|
}
|
||||||
|
42
tiny_gltf.h
42
tiny_gltf.h
@ -4113,7 +4113,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
|
|||||||
|
|
||||||
if ((bin_size == 0) || (bin_data == nullptr)) {
|
if ((bin_size == 0) || (bin_data == nullptr)) {
|
||||||
if (err) {
|
if (err) {
|
||||||
(*err) += "Invalid binary data in `Buffer'.\n";
|
(*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -6286,15 +6286,15 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
|
|||||||
//
|
//
|
||||||
// https://github.com/syoyo/tinygltf/issues/372
|
// https://github.com/syoyo/tinygltf/issues/372
|
||||||
// Use 64bit uint to avoid integer overflow.
|
// Use 64bit uint to avoid integer overflow.
|
||||||
uint64_t json_size = 20ull + uint64_t(chunk0_length);
|
uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
|
||||||
|
|
||||||
if (json_size > std::numeric_limits<uint32_t>::max()) {
|
if (header_and_json_size > std::numeric_limits<uint32_t>::max()) {
|
||||||
// Do not allow 4GB or more GLB data.
|
// Do not allow 4GB or more GLB data.
|
||||||
(*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
|
(*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
|
if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
|
||||||
(json_size > uint64_t(length)) ||
|
(header_and_json_size > uint64_t(length)) ||
|
||||||
(chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
|
(chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
|
||||||
if (err) {
|
if (err) {
|
||||||
(*err) = "Invalid glTF binary.";
|
(*err) = "Invalid glTF binary.";
|
||||||
@ -6305,29 +6305,42 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
|
|||||||
// Padding check
|
// Padding check
|
||||||
// The start and the end of each chunk must be aligned to a 4-byte boundary.
|
// The start and the end of each chunk must be aligned to a 4-byte boundary.
|
||||||
// No padding check for chunk0 start since its 4byte-boundary is ensured.
|
// No padding check for chunk0 start since its 4byte-boundary is ensured.
|
||||||
if ((json_size % 4) != 0) {
|
if ((header_and_json_size % 4) != 0) {
|
||||||
if (err) {
|
if (err) {
|
||||||
(*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
|
(*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//std::cout << "header_and_json_size = " << header_and_json_size << "\n";
|
||||||
|
//std::cout << "length = " << length << "\n";
|
||||||
|
|
||||||
|
// Chunk1(BIN) data
|
||||||
|
// The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted.
|
||||||
|
// So when header + JSON data == binary size, Chunk1 is omitted.
|
||||||
|
if (header_and_json_size == uint64_t(length)) {
|
||||||
|
|
||||||
|
bin_data_ = nullptr;
|
||||||
|
bin_size_ = 0;
|
||||||
|
} else {
|
||||||
// Read Chunk1 info(BIN data)
|
// Read Chunk1 info(BIN data)
|
||||||
if ((json_size + 8ull) > uint64_t(length)) {
|
// At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aliged to 4 bytes)
|
||||||
|
if ((header_and_json_size + 12ull) > uint64_t(length)) {
|
||||||
if (err) {
|
if (err) {
|
||||||
(*err) = "Insufficient storage space for Chunk1(BIN data).";
|
(*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n";
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int chunk1_length; // 4 bytes
|
unsigned int chunk1_length; // 4 bytes
|
||||||
unsigned int chunk1_format; // 4 bytes;
|
unsigned int chunk1_format; // 4 bytes;
|
||||||
memcpy(&chunk1_length, bytes + json_size, 4); // JSON data length
|
memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length
|
||||||
swap4(&chunk1_length);
|
swap4(&chunk1_length);
|
||||||
memcpy(&chunk1_format, bytes + json_size + 4, 4);
|
memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
|
||||||
swap4(&chunk1_format);
|
swap4(&chunk1_format);
|
||||||
|
|
||||||
|
//std::cout << "chunk1_length = " << chunk1_length << "\n";
|
||||||
|
|
||||||
if (chunk1_length < 4) {
|
if (chunk1_length < 4) {
|
||||||
// TODO: Do we allow 0byte BIN data?
|
|
||||||
if (err) {
|
if (err) {
|
||||||
(*err) = "Insufficient Chunk1(BIN) data size.";
|
(*err) = "Insufficient Chunk1(BIN) data size.";
|
||||||
}
|
}
|
||||||
@ -6341,7 +6354,7 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uint64_t(chunk1_length) + json_size > uint64_t(length)) {
|
if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
|
||||||
if (err) {
|
if (err) {
|
||||||
(*err) = "BIN Chunk data length exceeds the GLB size.";
|
(*err) = "BIN Chunk data length exceeds the GLB size.";
|
||||||
}
|
}
|
||||||
@ -6355,10 +6368,13 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bin_data_ = bytes + json_size +
|
//std::cout << "chunk1_length = " << chunk1_length << "\n";
|
||||||
|
|
||||||
|
bin_data_ = bytes + header_and_json_size +
|
||||||
8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
|
8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
|
||||||
|
|
||||||
bin_size_ = size_t(chunk1_length);
|
bin_size_ = size_t(chunk1_length);
|
||||||
|
}
|
||||||
|
|
||||||
// Extract JSON string.
|
// Extract JSON string.
|
||||||
std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
|
std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user