public static void Pack(string inputFilePath, string outputFilePath) { var inputDirectoryPath = Path.GetDirectoryName(inputFilePath); JObject json; using (var jsonStream = File.OpenRead(inputFilePath)) using (var jsonStreamReader = new StreamReader(jsonStream)) using (var jsonTextReader = new JsonTextReader(jsonStreamReader)) { json = (JObject)JToken.ReadFrom(jsonTextReader); } var position = 0; var memoryMappedFiles = new Dictionary <string, MemoryMappedFile>(); var viewStreams = new List <MemoryMappedViewStream>(); var buffers = (JArray)json["buffers"]; var bufferViews = (JArray)json["bufferViews"]; var images = (JArray)json["images"]; if (buffers != null) { for (var index = buffers.Count - 1; index >= 0; index--) { var buffer = (JObject)buffers[index]; var uri = (string)buffer["uri"]; if (uri != null && !Tools.IsBase64(uri)) { foreach (JObject bufferView in bufferViews) { var bufferIndex = (int)bufferView["buffer"]; if (bufferIndex == index) { bufferView["buffer"] = -1; var byteOffset = (int?)bufferView["byteOffset"] ?? 0; bufferView.SetValue("byteOffset", position + byteOffset, 0); } } var filePath = Path.Combine(inputDirectoryPath, uri); if (!memoryMappedFiles.TryGetValue(filePath, out MemoryMappedFile memoryMappedFile)) { memoryMappedFile = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open); memoryMappedFiles.Add(filePath, memoryMappedFile); } var fileLength = Tools.GetFileLength(filePath); viewStreams.Add(memoryMappedFile.CreateViewStream(0, fileLength, MemoryMappedFileAccess.Read)); position += fileLength; position = Tools.Align(position); buffers.RemoveAt(index); } } } if (images != null) { foreach (JObject image in images) { var uri = (string)image["uri"]; if (uri != null && !Tools.IsBase64(uri)) { var filePath = Path.Combine(inputDirectoryPath, uri); if (!memoryMappedFiles.TryGetValue(filePath, out MemoryMappedFile memoryMappedFile)) { memoryMappedFile = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open); memoryMappedFiles.Add(filePath, memoryMappedFile); } var fileLength = Tools.GetFileLength(filePath); viewStreams.Add(memoryMappedFile.CreateViewStream(0, fileLength, MemoryMappedFileAccess.Read)); image.Remove("uri"); image["bufferView"] = bufferViews.Count; image["mimeType"] = MimeType.FromFileExtension(Path.GetExtension(uri)); position = Tools.Align(position); var bufferView = new JObject { ["buffer"] = -1, ["byteLength"] = fileLength }; bufferView.SetValue("byteOffset", position, 0); bufferViews.Add(bufferView); position += fileLength; } } } if (viewStreams.Count != 0) { if (buffers == null) { json["buffers"] = new JArray(); } buffers.Insert(0, new JObject { ["byteLength"] = position }); foreach (var bufferView in bufferViews) { var bufferIndex = (int)bufferView["buffer"]; bufferView["buffer"] = bufferIndex + 1; } } using (var fileStream = File.Create(outputFilePath)) using (var binaryWriter = new BinaryWriter(fileStream)) { binaryWriter.Write(Binary.Magic); binaryWriter.Write(Binary.Version); var chunksPosition = binaryWriter.BaseStream.Position; binaryWriter.Write(0U); // length var jsonChunkPosition = binaryWriter.BaseStream.Position; binaryWriter.Write(0U); // json chunk length binaryWriter.Write(Binary.ChunkFormatJson); using (var streamWriter = new StreamWriter(binaryWriter.BaseStream, new UTF8Encoding(false, true), 1024, true)) using (var jsonTextWriter = new JsonTextWriter(streamWriter)) { json.WriteTo(jsonTextWriter); } binaryWriter.BaseStream.Align(0x20); var jsonChunkLength = checked ((uint)(binaryWriter.BaseStream.Length - jsonChunkPosition)) - Binary.ChunkHeaderLength; binaryWriter.BaseStream.Seek(jsonChunkPosition, SeekOrigin.Begin); binaryWriter.Write(jsonChunkLength); if (viewStreams.Count != 0) { binaryWriter.BaseStream.Seek(0, SeekOrigin.End); var binChunkPosition = binaryWriter.BaseStream.Position; binaryWriter.Write(0); // bin chunk length binaryWriter.Write(Binary.ChunkFormatBin); foreach (var viewStream in viewStreams) { binaryWriter.BaseStream.Align(); viewStream.CopyTo(binaryWriter.BaseStream); } binaryWriter.BaseStream.Align(0x20); var binChunkLength = checked ((uint)(binaryWriter.BaseStream.Length - binChunkPosition)) - Binary.ChunkHeaderLength; binaryWriter.BaseStream.Seek(binChunkPosition, SeekOrigin.Begin); binaryWriter.Write(binChunkLength); } var length = checked ((uint)binaryWriter.BaseStream.Length); binaryWriter.BaseStream.Seek(chunksPosition, SeekOrigin.Begin); binaryWriter.Write(length); } foreach (var viewStream in viewStreams) { viewStream.Dispose(); } foreach (var memoryMappedFile in memoryMappedFiles.Values) { memoryMappedFile.Dispose(); } }
private void ProcessImageFiles(JObject json, MemoryMappedFile memoryMappedFile, long binChunkOffset) { var accessors = (JArray)json["accessors"]; var bufferViews = (JArray)json["bufferViews"]; var images = (JArray)json["images"]; var bufferViewIndicesToRemove = new List <int>(); if (images != null) { for (var index = 0; index < images.Count; index++) { var image = (JObject)images[index]; var uri = (string)image["uri"]; if (uri != null) { if (!Tools.IsBase64(uri)) { var sourceFilePath = Path.Combine(this.inputDirectoryPath, Path.GetFileName(uri)); var fileExtension = Path.GetExtension(uri); var fileName = $"{this.inputFileName}_image{index}.{fileExtension}"; if (File.Exists(sourceFilePath)) { var destinationFilePath = Path.Combine(this.outputDirectoryPath, fileName); File.Copy(sourceFilePath, destinationFilePath, true); } image["uri"] = fileName; } } else if (this.unpackImages && bufferViews != null && binChunkOffset != -1) { var bufferViewIndex = (int)image["bufferView"]; var bufferView = bufferViews[bufferViewIndex]; var bufferIndex = (int)bufferView["buffer"]; if (bufferIndex == 0) { var mimeType = (string)image["mimeType"]; var fileExtension = MimeType.ToFileExtension(mimeType); var fileName = $"{this.inputFileName}_image{index}{fileExtension}"; using (var fileStream = File.Create(Path.Combine(this.outputDirectoryPath, fileName))) { var byteOffset = (long?)bufferView["byteOffset"] ?? 0; var byteLength = (int)bufferView["byteLength"]; using (var viewStream = memoryMappedFile.CreateViewStream(binChunkOffset + byteOffset, byteLength, MemoryMappedFileAccess.Read)) { viewStream.CopyTo(fileStream); } } image.Remove("bufferView"); image.Remove("mimeType"); image["uri"] = fileName; bufferViewIndicesToRemove.Add(bufferViewIndex); } } } } var bufferViewIndexMap = new Dictionary <int, int>(); if (bufferViews != null && bufferViewIndicesToRemove.Any()) { var newBufferViews = new JArray(); for (var index = 0; index < bufferViews.Count; index++) { if (!bufferViewIndicesToRemove.Contains(index)) { var newIndex = newBufferViews.Count; bufferViewIndexMap.Add(index, newIndex); newBufferViews.Add(bufferViews[index]); } } if (newBufferViews.Count == 0) { json.Remove("bufferViews"); } else { json["bufferViews"] = newBufferViews; } } if (accessors != null && bufferViewIndexMap.Any()) { foreach (var accessor in accessors) { var bufferViewIndex = (int?)accessor["bufferView"]; if (bufferViewIndex.HasValue) { if (bufferViewIndexMap.TryGetValue(bufferViewIndex.Value, out int newBufferViewIndex)) { accessor["bufferView"] = newBufferViewIndex; } } } } }