Index: chrome/browser/android/vr_shell/gltf_parser.cc |
diff --git a/chrome/browser/android/vr_shell/gltf_parser.cc b/chrome/browser/android/vr_shell/gltf_parser.cc |
index 6dbde77ac9e81adbab0a662e92cc178073a9c0f0..210afcd2652cbe644a5a236a6cfcc5a8051939ad 100644 |
--- a/chrome/browser/android/vr_shell/gltf_parser.cc |
+++ b/chrome/browser/android/vr_shell/gltf_parser.cc |
@@ -9,14 +9,35 @@ |
#include "base/callback_helpers.h" |
#include "base/files/file_util.h" |
#include "base/json/json_file_value_serializer.h" |
+#include "base/json/json_string_value_serializer.h" |
#include "base/logging.h" |
#include "base/memory/ptr_util.h" |
+#include "base/strings/string_util.h" |
+#include "base/sys_byteorder.h" |
#include "net/base/data_url.h" |
#include "net/base/filename_util.h" |
#include "url/gurl.h" |
namespace vr_shell { |
+namespace { |
+constexpr const char kFailedtoReadBinaryGltfMsg[] = |
+ "Failed to read binary glTF: "; |
+constexpr const char kGltfMagic[] = "glTF"; |
+constexpr const char kBinaryGltfBufferName[] = "binary_glTF"; |
+constexpr uint32_t kJsonGltfFormat = 0; |
+constexpr size_t kVersionStart = 4; |
+constexpr size_t kLengthStart = 8; |
+constexpr size_t kContentLengthStart = 12; |
+constexpr size_t kContentFormatStart = 16; |
+constexpr size_t kContentStart = 20; |
+ |
+inline uint32_t GetLE32(const char* data) { |
+ return base::ByteSwapToLE32(*reinterpret_cast<const uint32_t*>(data)); |
+} |
+ |
+} // namespace |
+ |
GltfParser::GltfParser() {} |
GltfParser::~GltfParser() = default; |
@@ -25,6 +46,7 @@ std::unique_ptr<gltf::Asset> GltfParser::Parse( |
const base::DictionaryValue& dict, |
std::vector<std::unique_ptr<gltf::Buffer>>* buffers, |
const base::FilePath& path) { |
+ DCHECK(buffers && buffers->size() <= 1); |
path_ = path; |
asset_ = base::MakeUnique<gltf::Asset>(); |
@@ -40,6 +62,7 @@ std::unique_ptr<gltf::Asset> GltfParser::Parse( |
std::unique_ptr<gltf::Asset> GltfParser::Parse( |
const base::FilePath& gltf_path, |
std::vector<std::unique_ptr<gltf::Buffer>>* buffers) { |
+ DCHECK(buffers && buffers->size() <= 1); |
JSONFileValueDeserializer json_deserializer(gltf_path); |
int error_code; |
std::string error_msg; |
@@ -89,12 +112,19 @@ bool GltfParser::ParseInternal( |
bool GltfParser::SetBuffers( |
const base::DictionaryValue& dict, |
std::vector<std::unique_ptr<gltf::Buffer>>* buffers) { |
- buffers->clear(); |
+ size_t buffer_count = 0; |
for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) { |
const base::DictionaryValue* buffer_dict; |
if (!it.value().GetAsDictionary(&buffer_dict)) |
return false; |
+ if (it.key() == kBinaryGltfBufferName) { |
+ if (buffers->size() == buffer_count) |
+ return false; |
+ buffer_ids_[it.key()] = 0; |
+ continue; |
+ } |
+ |
std::string uri_str; |
if (!buffer_dict->GetString("uri", &uri_str)) |
return false; |
@@ -109,6 +139,7 @@ bool GltfParser::SetBuffers( |
buffer_ids_[it.key()] = buffers->size(); |
buffers->push_back(std::move(buffer)); |
+ ++buffer_count; |
} |
return true; |
} |
@@ -333,4 +364,68 @@ void GltfParser::Clear() { |
scene_ids_.clear(); |
} |
+std::unique_ptr<gltf::Asset> BinaryGltfParser::Parse( |
+ base::StringPiece glb_content, |
+ std::vector<std::unique_ptr<gltf::Buffer>>* buffers, |
+ const base::FilePath& path) { |
+ DCHECK(buffers && buffers->empty()); |
+ if (glb_content.length() < kContentStart) { |
+ DLOG(ERROR) << kFailedtoReadBinaryGltfMsg << "Incomplete data"; |
+ return nullptr; |
+ } |
+ if (!glb_content.starts_with(kGltfMagic)) { |
+ DLOG(ERROR) << kFailedtoReadBinaryGltfMsg << "Unknown magic number"; |
+ return nullptr; |
+ } |
+ if (GetLE32(glb_content.data() + kVersionStart) != 1) { |
+ DLOG(ERROR) << kFailedtoReadBinaryGltfMsg << "Unsupported version"; |
+ return nullptr; |
+ } |
+ if (GetLE32(glb_content.data() + kLengthStart) != glb_content.length()) { |
+ DLOG(ERROR) << kFailedtoReadBinaryGltfMsg << "Incorrect file size"; |
+ return nullptr; |
+ } |
+ uint32_t content_length = GetLE32(glb_content.data() + kContentLengthStart); |
+ if (kContentStart + content_length > glb_content.length()) { |
+ DLOG(ERROR) << kFailedtoReadBinaryGltfMsg << "Invalid content length"; |
+ return nullptr; |
+ } |
+ if (GetLE32(glb_content.data() + kContentFormatStart) != kJsonGltfFormat) { |
+ DLOG(ERROR) << kFailedtoReadBinaryGltfMsg << "Unsupported glTF format"; |
+ return nullptr; |
+ } |
+ |
+ base::StringPiece gltf_content = |
+ glb_content.substr(kContentStart, content_length); |
+ int error_code; |
+ std::string error_msg; |
+ JSONStringValueDeserializer json_deserializer(gltf_content); |
+ std::unique_ptr<base::Value> gltf_value = |
+ json_deserializer.Deserialize(&error_code, &error_msg); |
+ if (!gltf_value) { |
+ DLOG(ERROR) << kFailedtoReadBinaryGltfMsg |
+ << "Content not a valid JSON - Error " << error_code << " - " |
+ << error_msg; |
+ return nullptr; |
+ } |
+ base::DictionaryValue* gltf_dict; |
+ if (!gltf_value->GetAsDictionary(&gltf_dict)) { |
+ DLOG(ERROR) << kFailedtoReadBinaryGltfMsg << "Content is not a JSON object"; |
+ return nullptr; |
+ } |
+ |
+ auto glb_buffer = base::MakeUnique<gltf::Buffer>( |
+ glb_content.substr(kContentStart + content_length)); |
+ buffers->push_back(std::move(glb_buffer)); |
+ GltfParser gltf_parser; |
+ std::unique_ptr<gltf::Asset> gltf_asset = |
+ gltf_parser.Parse(*gltf_dict, buffers); |
+ if (!gltf_asset) { |
+ DLOG(ERROR) << kFailedtoReadBinaryGltfMsg << "Content is not a valid glTF"; |
+ buffers->clear(); |
+ return nullptr; |
+ } |
+ return gltf_asset; |
+} |
+ |
} // namespace vr_shell |