Browse Source

Temporary fix for unescaped URI characters.

Vicente Penades 5 years ago
parent
commit
f7fafa852b

+ 8 - 4
src/Shared/Guard.cs

@@ -190,6 +190,11 @@ namespace SharpGLTF
 
         #region specialised
 
+        private static readonly IReadOnlyList<Char> _InvalidRelativePathChars =
+            System.IO.Path.GetInvalidFileNameChars()
+            .Where(c => c != '/' && c != '\\')
+            .ToArray();
+
         public static void IsValidURI(string parameterName, string gltfURI, params string[] validHeaders)
         {
             if (string.IsNullOrEmpty(gltfURI)) return;
@@ -206,12 +211,11 @@ namespace SharpGLTF
                 }
             }
 
-            if (gltfURI.StartsWith("data:")) throw new ArgumentException($"Invalid URI '{gltfURI}'.");
+            if (gltfURI.Any(c => _InvalidRelativePathChars.Contains(c))) throw new ArgumentException($"Invalid URI '{gltfURI}'.");
 
-            gltfURI = Uri.EscapeUriString(gltfURI);
+            if (gltfURI.Any(chr => char.IsWhiteSpace(chr))) gltfURI = Uri.EscapeUriString(gltfURI);
 
-            if (!Uri.IsWellFormedUriString(gltfURI, UriKind.RelativeOrAbsolute)) throw new ArgumentException($"Invalid URI '{gltfURI}'.");
-            if (!Uri.TryCreate(gltfURI, UriKind.RelativeOrAbsolute, out Uri xuri)) throw new ArgumentException($"Invalid URI '{gltfURI}'.");
+            if (!Uri.TryCreate(gltfURI, UriKind.Relative, out Uri xuri)) throw new ArgumentException($"Invalid URI '{gltfURI}'.");
 
             return;
         }

+ 7 - 0
src/Shared/_Extensions.cs

@@ -638,6 +638,13 @@ namespace SharpGLTF
 
         #region serialization
 
+        public static Byte[] ToUnderlayingArray(this ArraySegment<Byte> segment)
+        {
+            if (segment.Offset == 0 && segment.Count == segment.Array.Length) return segment.Array;
+
+            return segment.ToArray();
+        }
+
         public static ArraySegment<Byte> ToArraySegment(this System.IO.MemoryStream m)
         {
             if (m.TryGetBuffer(out ArraySegment<Byte> data)) return data;

+ 12 - 2
src/SharpGLTF.Core/IO/ReadContext.cs

@@ -46,12 +46,22 @@ namespace SharpGLTF.IO
 
         public static ReadContext CreateFromDirectory(string directoryPath)
         {
-            return new ReadContext(assetFileName => new BYTES(File.ReadAllBytes(Path.Combine(directoryPath, assetFileName))));
+            BYTES _loadFile(string rawUri)
+            {
+                var path = Uri.UnescapeDataString(rawUri);
+                path = Path.Combine(directoryPath, path);
+
+                var content = File.ReadAllBytes(path);
+
+                return new BYTES(content);
+            }
+
+            return new ReadContext(_loadFile);
         }
 
         public static ReadContext CreateFromDictionary(IReadOnlyDictionary<string, BYTES> dictionary)
         {
-            return new ReadContext(fn => dictionary[fn]);
+            return new ReadContext(rawUri => dictionary[rawUri]);
         }
 
         private ReadContext(FileReaderCallback reader)

+ 12 - 4
src/SharpGLTF.Core/IO/WriteContext.cs

@@ -46,11 +46,19 @@ namespace SharpGLTF.IO
             return CreateFromDirectory(dir);
         }
 
-        public static WriteContext CreateFromDirectory(string dirPath)
+        public static WriteContext CreateFromDirectory(string directoryPath)
         {
-            Guard.DirectoryPathMustExist(dirPath, nameof(dirPath));
+            Guard.DirectoryPathMustExist(directoryPath, nameof(directoryPath));
 
-            var context = Create((fn, d) => File.WriteAllBytes(Path.Combine(dirPath, fn), d.ToArray()));
+            void _saveFile(string rawUri, BYTES data)
+            {
+                var path = Uri.UnescapeDataString(rawUri);
+                path = Path.Combine(directoryPath, path);
+
+                File.WriteAllBytes(path, data.ToUnderlayingArray());
+            }
+
+            var context = Create(_saveFile);
             context.ImageWriting = ResourceWriteMode.SatelliteFile;
             context.JsonIndented = true;
             return context;
@@ -60,7 +68,7 @@ namespace SharpGLTF.IO
         {
             Guard.NotNull(dict, nameof(dict));
 
-            var context = Create((fn, buff) => dict[fn] = buff);
+            var context = Create((rawUri, data) => dict[rawUri] = data);
             context.ImageWriting = ResourceWriteMode.SatelliteFile;
             context.MergeBuffers = false;
             context.JsonIndented = false;

+ 9 - 6
src/SharpGLTF.Core/Schema2/gltf.Buffer.cs

@@ -48,14 +48,17 @@ namespace SharpGLTF.Schema2
         {
             _Content = _LoadBinaryBufferUnchecked(_uri, context);
 
-            // if (_uri == null) _byteLength = _Content.Length; // fixes "valid_placeholder.glb" case
-
             _uri = null; // When _Data is not empty, clear URI
         }
 
         private static Byte[] _LoadBinaryBufferUnchecked(string uri, IO.ReadContext context)
         {
-            return uri.TryParseBase64Unchecked(EMBEDDEDGLTFBUFFER, EMBEDDEDOCTETSTREAM) ?? context.ReadAllBytesToEnd(uri).ToArray();
+            var data = uri.TryParseBase64Unchecked(EMBEDDEDGLTFBUFFER, EMBEDDEDOCTETSTREAM);
+            if (data != null) return data;
+
+            return context
+                .ReadAllBytesToEnd(uri)
+                .ToUnderlayingArray();
         }
 
         #endregion
@@ -69,10 +72,10 @@ namespace SharpGLTF.Schema2
         /// <param name="satelliteUri">A local satellite URI</param>
         internal void _WriteToSatellite(IO.WriteContext writer, string satelliteUri)
         {
-            this._uri = satelliteUri;
-            this._byteLength = _Content.Length;
+            writer.WriteAllBytesToEnd(satelliteUri, new ArraySegment<byte>(_Content.GetPaddedContent()));
 
-            writer.WriteAllBytesToEnd(satelliteUri, new ArraySegment<byte>(_Content.GetPaddedContent()) );
+            this._uri = Uri.EscapeDataString(satelliteUri);
+            this._byteLength = _Content.Length;
         }
 
         /// <summary>

+ 15 - 21
src/SharpGLTF.Core/Schema2/gltf.Images.cs

@@ -181,27 +181,17 @@ namespace SharpGLTF.Schema2
         {
             if (String.IsNullOrWhiteSpace(_uri)) return;
 
-            byte[] data = null;
-
-            try
-            {
-                data = Memory.MemoryImage.TryParseBytes(_uri);
-            }
-            catch (ArgumentException argex)
-            {
-                throw new Validation.DataException(this, argex.Message);
-            }
+            var data = Memory.MemoryImage.TryParseBytes(_uri);
 
             if (data == null)
             {
-                var bytes = context.ReadAllBytesToEnd(_uri);
-
-                // let's try to avoid making a copy if it's not neccesary.
-                if (bytes.Offset == 0 && bytes.Array.Length == bytes.Count) data = bytes.Array;
-                else data = bytes.ToArray();
+                data = context
+                    .ReadAllBytesToEnd(_uri)
+                    .ToUnderlayingArray();
             }
 
             _SatelliteImageContent = data;
+
             _uri = null;
             _mimeType = null;
         }
@@ -226,8 +216,9 @@ namespace SharpGLTF.Schema2
             if (_SatelliteImageContent == null) { _WriteAsBufferView(); return; }
 
             var imimg = new Memory.MemoryImage(_SatelliteImageContent);
-            _mimeType = imimg.MimeType;
+
             _uri = imimg.ToMime64();
+            _mimeType = imimg.MimeType;
         }
 
         /// <summary>
@@ -243,13 +234,15 @@ namespace SharpGLTF.Schema2
                 return;
             }
 
-            _mimeType = null;
-
             var imimg = new Memory.MemoryImage(_SatelliteImageContent);
             if (!imimg.IsValid) throw new InvalidOperationException();
 
-            _uri = System.IO.Path.ChangeExtension(satelliteUri, imimg.FileExtension);
-            writer.WriteAllBytesToEnd(_uri, imimg.GetBuffer());
+            satelliteUri = System.IO.Path.ChangeExtension(satelliteUri, imimg.FileExtension);
+
+            writer.WriteAllBytesToEnd(satelliteUri, imimg.GetBuffer());
+
+            _uri = Uri.EscapeDataString(satelliteUri);
+            _mimeType = null;
         }
 
         private void _WriteAsBufferView()
@@ -259,6 +252,7 @@ namespace SharpGLTF.Schema2
             var imimg = this.MemoryImage;
             if (!imimg.IsValid) throw new InvalidOperationException();
 
+            _uri = null;
             _mimeType = imimg.MimeType;
         }
 
@@ -269,8 +263,8 @@ namespace SharpGLTF.Schema2
         /// </summary>
         internal void _ClearAfterWrite()
         {
-            _mimeType = null;
             _uri = null;
+            _mimeType = null;
         }
 
         #endregion

+ 3 - 1
src/SharpGLTF.Toolkit/IO/Zip.cs

@@ -79,8 +79,10 @@ namespace SharpGLTF.IO
             }
         }
 
-        private ArraySegment<Byte> _ReadAsset(string filePath)
+        private ArraySegment<Byte> _ReadAsset(string rawUri)
         {
+            var filePath = Uri.UnescapeDataString(rawUri);
+
             System.IO.Compression.ZipArchiveEntry entry = _FindEntry(filePath);
 
             using (var s = entry.Open())

+ 2 - 2
src/SharpGLTF.Toolkit/Scenes/readme.md

@@ -26,8 +26,8 @@ and how you want to render it, so every AddMesh method adds an mesh instance to
 
 ```c#
 scene = new SceneBuilder();
-scene.AddMesh(...);
-scene.AddMesh(...);
+scene.AddRigidMesh(...);
+scene.AddRigidMesh(...);
 scene.AddSkinnedMesh(...);
 scene.SaveGLB("scene.glb");
 ```

+ 3 - 0
tests/Assets/white space.gltf

@@ -5,6 +5,9 @@
     "images": [
         {
             "uri": "white space.jpg"
+        },
+{
+            "uri": "white%20space.jpg"
         }
     ]
 }

+ 1 - 1
tests/SharpGLTF.NUnit/gltf_validator.cs

@@ -97,7 +97,7 @@ namespace SharpGLTF
             public bool HasWarnings => Warnings.Count > 0;
             public bool HasErrors => Errors.Count > 0;
 
-            public string ToString()
+            public override string ToString()
             {
                 return RawReport;
 

+ 3 - 3
tests/SharpGLTF.Tests/Schema2/Authoring/ExtensionsCreationTests.cs

@@ -34,7 +34,7 @@ namespace SharpGLTF.Schema2.Authoring
                 .PunctualLight = root.CreatePunctualLight(PunctualLightType.Directional)
                 .WithColor(Vector3.UnitX, 2);
 
-            var node2 = scene.CreateNode()
+            scene.CreateNode()
                 .PunctualLight = root.CreatePunctualLight(PunctualLightType.Spot)
                 .WithColor(Vector3.UnitY, 3, 10)
                 .WithSpotCone(0.2f, 0.3f);
@@ -79,7 +79,7 @@ namespace SharpGLTF.Schema2.Authoring
                 );
 
             var scene = new Scenes.SceneBuilder();
-            scene.AddMesh(mesh, Matrix4x4.Identity);
+            scene.AddRigidMesh(mesh, Matrix4x4.Identity);
 
             scene.AttachToCurrentTest("result.glb");
             scene.AttachToCurrentTest("result.gltf");
@@ -115,7 +115,7 @@ namespace SharpGLTF.Schema2.Authoring
                 );
 
             var scene = new Scenes.SceneBuilder();
-            scene.AddMesh(mesh, Matrix4x4.Identity);
+            scene.AddRigidMesh(mesh, Matrix4x4.Identity);
 
             scene.AttachToCurrentTest("result.glb");
             scene.AttachToCurrentTest("result.gltf");

+ 1 - 1
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSpecialModelsTest.cs

@@ -25,7 +25,7 @@ namespace SharpGLTF.Schema2.LoadAndSave
         #endregion
 
         [Test]
-        public void LoadWithWhiteSpaceTexture()
+        public void LoadEscapedUriModel()
         {
             TestContext.CurrentContext.AttachShowDirLink();
 

+ 2 - 3
tests/SharpGLTF.Tests/Validation/InvalidFilesTests.cs

@@ -60,7 +60,7 @@ namespace SharpGLTF.Validation
 
             foreach (var f in files)
             {
-                // System.Diagnostics.Debug.Assert(!f.EndsWith("unresolved_source.gltf"));
+                // System.Diagnostics.Debug.Assert(!f.EndsWith("invalid_uri_scheme.gltf"));
 
                 var gltfJson = f.EndsWith(".gltf") ? System.IO.File.ReadAllText(f) : string.Empty;
 
@@ -76,7 +76,6 @@ namespace SharpGLTF.Validation
 
                 Assert.AreEqual(report.Issues.NumErrors > 0, result.HasErrors);                                
             }
-        }        
-
+        }
     }
 }

+ 1 - 1
tests/SharpGLTF.Toolkit.Tests/Scenes/SceneBuilderTests.cs

@@ -542,7 +542,7 @@ namespace SharpGLTF.Scenes
             // create a scenebuilder with an empty mesh
             var sb = new SceneBuilder();
 
-            sb.AddMesh(VPOSNRM.CreateCompatibleMesh("Empty"), Matrix4x4.Identity);
+            sb.AddRigidMesh(VPOSNRM.CreateCompatibleMesh("Empty"), Matrix4x4.Identity);
 
             var schema = sb.ToGltf2();