/// <summary> /// Reads a BCFv2 zip archive /// </summary> /// <param name="zipFileStream">The zip archive of the physical file</param> /// <returns></returns> public static BCFv21Container ReadStream(Stream zipFileStream) { var container = new BCFv21Container(); var bcfZipArchive = new ZipArchive(zipFileStream, ZipArchiveMode.Read); // Check if version info is compliant with this implementation (2.1) var versionEntry = bcfZipArchive.Entries.FirstOrDefault(e => string.Equals(e.FullName, "bcf.version", StringComparison.OrdinalIgnoreCase)); if (versionEntry != null) { var fileVersionInfo = Version.Deserialize(versionEntry.Open()); if (fileVersionInfo.VersionId != "2.1" || !fileVersionInfo.DetailedVersion.Contains("2.1")) { throw new NotSupportedException("BCFzip version"); } } // Get project info if present if (bcfZipArchive.Entries.Any(e => e.FullName == "project.bcfp")) { var deserializedProject = ProjectExtension.Deserialize(bcfZipArchive.Entries.First(Entry => Entry.FullName == "project.bcfp").Open()); if (!(string.IsNullOrWhiteSpace(deserializedProject.ExtensionSchema) && (deserializedProject.Project == null || string.IsNullOrWhiteSpace(deserializedProject.Project.Name) && string.IsNullOrWhiteSpace(deserializedProject.Project.ProjectId)))) { if (string.IsNullOrWhiteSpace(deserializedProject.ExtensionSchema)) { deserializedProject.ExtensionSchema = null; } container.BcfProject = deserializedProject; if (!string.IsNullOrWhiteSpace(container.BcfProject.ExtensionSchema) && bcfZipArchive.Entries.Any(e => e.FullName == container.BcfProject.ExtensionSchema)) { using (var extensionsReader = new StreamReader(bcfZipArchive.Entries.First(e => e.FullName == container.BcfProject.ExtensionSchema).Open())) { container.ProjectExtensions = new ProjectExtensions(extensionsReader.ReadToEnd()); } } } } // Get each topic's GUID and read the topic var topicIds = new List <string>(); foreach (var entry in bcfZipArchive.Entries) { var topicId = entry.FullName; if (Regex.IsMatch(topicId, @"^\b[A-Fa-f0-9]{8}(?:-[A-Fa-f0-9]{4}){3}-[A-Fa-f0-9]{12}/markup.bcf\b")) { if (!topicIds.Contains(Regex.Match(topicId, @"^\b[A-Fa-f0-9]{8}(?:-[A-Fa-f0-9]{4}){3}-[A-Fa-f0-9]{12}\b").Value)) { container.Topics.Add(ReadSingleTopic(bcfZipArchive, Regex.Match(topicId, @"^\b[A-Fa-f0-9]{8}(?:-[A-Fa-f0-9]{4}){3}-[A-Fa-f0-9]{12}\b").Value, container)); } topicIds.Add(Regex.Match(topicId, @"^\b[A-Fa-f0-9]{8}(?:-[A-Fa-f0-9]{4}){3}-[A-Fa-f0-9]{12}\b").Value); } } // Check if there is no extension but a reference to it; delete the reference then if (container.ProjectExtensions == null && !string.IsNullOrWhiteSpace(container.BcfProject?.ExtensionSchema)) { container.BcfProject.ExtensionSchema = null; } return(container); }
private static BCFTopic ReadSingleTopic(ZipArchive archive, string topicId, BCFv21Container container) { var topic = new BCFTopic(); // Get the markup topic.Markup = Markup.Deserialize(archive.Entries.First(e => e.FullName == topicId + "/" + "markup.bcf").Open()); // Check if any comments have a Viewpoint object without any value, then set it to null foreach (var comment in topic.Markup.Comment.Where(c => c.ShouldSerializeViewpoint() && string.IsNullOrWhiteSpace(c.Viewpoint.Guid))) { comment.Viewpoint = null; } if (topic.Markup.Topic.ShouldSerializeBimSnippet() && !topic.Markup.Topic.BimSnippet.isExternal) { // Read the snippet var snippetPathInArchive = GetAbsolutePath(topicId, topic.Markup.Topic.BimSnippet.Reference); var entry = archive.Entries.FirstOrDefault(Curr => Curr.FullName == snippetPathInArchive); if (entry == null) { topic.Markup.Topic.BimSnippet.isExternal = true; } else { using (var memStream = new MemoryStream()) { entry.Open().CopyTo(memStream); topic.SnippetData = memStream.ToArray(); } } } // See if any internal header files are referenced if (topic.Markup.ShouldSerializeHeader() && topic.Markup.Header.Any(h => !h.isExternal)) { foreach (var internalFile in topic.Markup.Header.Where(h => !h.isExternal)) { if (internalFile.Reference == null) { continue; } var filePathInArchive = GetAbsolutePath(topicId, internalFile.Reference); var entry = archive.Entries.FirstOrDefault(e => e.FullName == filePathInArchive); // Only append if not known already and the file is actually present if (entry != null && !container.FileAttachments.ContainsKey(entry.Name)) { using (var memStream = new MemoryStream()) { entry.Open().CopyTo(memStream); container.FileAttachments.Add(entry.Name, memStream.ToArray()); } } } } // Get referenced documents if (topic.Markup.Topic.ShouldSerializeDocumentReference() && topic.Markup.Topic.DocumentReference.Any(d => !d.isExternal)) { foreach (var internalDocument in topic.Markup.Topic.DocumentReference.Where(d => !d.isExternal)) { var filePathInArchive = GetAbsolutePath(topicId, internalDocument.ReferencedDocument); var entry = archive.Entries.FirstOrDefault(e => e.FullName == filePathInArchive); // Only append if not known already and the file is actually present if (entry != null && !container.FileAttachments.ContainsKey(entry.Name)) { using (var memStream = new MemoryStream()) { entry.Open().CopyTo(memStream); container.FileAttachments.Add(entry.Name, memStream.ToArray()); } } } } // Get viewpoints for (var i = 0; i < topic.Markup.Viewpoints.Count; i++) { var deserializedViewpoint = VisualizationInfo.Deserialize(archive.Entries.First(e => e.FullName == topicId + "/" + topic.Markup.Viewpoints[i].Viewpoint).Open()); deserializedViewpoint.Guid = topic.Markup.Viewpoints[i].Guid; topic.Viewpoints.Add(deserializedViewpoint); // Get viewpoint bitmaps if present if (topic.Viewpoints[i].Bitmap.Count > 0) { foreach (var viewpointBitmap in topic.Viewpoints[i].Bitmap) { using (var bytesMemoryStream = new MemoryStream()) { var bitmapPathInArchive = GetAbsolutePath(topicId, viewpointBitmap.Reference); var bitmapFileEntry = archive.Entries.FirstOrDefault(e => e.FullName == bitmapPathInArchive); if (bitmapFileEntry == null) { // File entry was not found, possible because it was referenced with an absolute path bitmapPathInArchive = GetAbsolutePath(topicId, TransformToRelativePath(viewpointBitmap.Reference, topicId)); bitmapFileEntry = archive.Entries.FirstOrDefault(e => e.FullName == bitmapPathInArchive); if (bitmapFileEntry != null) { viewpointBitmap.Reference = TransformToRelativePath(viewpointBitmap.Reference, topicId); } else { throw new ArgumentNullException(nameof(bitmapFileEntry), "Could not locate bitmap file in archive"); } } bitmapFileEntry.Open().CopyTo(bytesMemoryStream); if (!topic.ViewpointBitmaps.ContainsKey(topic.Viewpoints[i])) { topic.ViewpointBitmaps.Add(topic.Viewpoints[i], new List <byte[]>()); } topic.ViewpointBitmaps[topic.Viewpoints[i]].Add(bytesMemoryStream.ToArray()); } } } if (!string.IsNullOrWhiteSpace(topic.Markup.Viewpoints[i].Snapshot)) { using (var bytesMemoryStream = new MemoryStream()) { archive.Entries.First(e => e.FullName == topicId + "/" + topic.Markup.Viewpoints[i].Snapshot).Open().CopyTo(bytesMemoryStream); topic.AddOrUpdateSnapshot(topic.Viewpoints[i].Guid, bytesMemoryStream.ToArray()); } } } return(topic); }