Skip to content

Commit 812ca14

Browse files
committed
refactored Uri handling
1 parent e02e30f commit 812ca14

File tree

7 files changed

+87
-26
lines changed

7 files changed

+87
-26
lines changed

src/Shared/_Extensions.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -894,13 +894,18 @@ private static Byte[] _TryParseBase64Unchecked(string uri, string prefix)
894894

895895
#region json
896896

897-
public static string _EscapeStringInternal(this string uri)
897+
public static Uri ToUri(this UriKind kind, string value)
898898
{
899-
// https://stackoverflow.com/questions/4396598/whats-the-difference-between-escapeuristring-and-escapedatastring
900-
#pragma warning disable SYSLIB0013 // Type or member is obsolete
901-
return Uri.EscapeUriString(uri);
902-
#pragma warning restore SYSLIB0013 // Type or member is obsolete
903-
}
899+
var uri = new Uri(value, kind);
900+
901+
if (!uri.IsAbsoluteUri || uri.IsFile)
902+
{
903+
value = value.Replace(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar);
904+
uri = new Uri(value, kind);
905+
}
906+
907+
return uri;
908+
}
904909

905910
#if NET6_0
906911

src/SharpGLTF.Core/Memory/MemoryImage.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ public string ToDebuggerDisplay()
7979

8080
public static implicit operator MemoryImage(string filePath) { return new MemoryImage(filePath); }
8181

82+
public static bool TryParseMime64(Uri mime64content, out MemoryImage image)
83+
{
84+
return TryParseMime64(mime64content?.OriginalString, out image);
85+
}
86+
8287
/// <summary>
8388
/// Tries to parse a Mime64 string to <see cref="MemoryImage"/>
8489
/// </summary>

src/SharpGLTF.Core/Schema2/Serialization.ReadContext.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,12 @@ BYTES _loadFile(string rawUri)
5757

5858
public static ReadContext CreateFromDictionary(IReadOnlyDictionary<string, BYTES> dictionary, bool checkExtensions = true)
5959
{
60-
return new ReadContext(rawUri => dictionary[rawUri], null, checkExtensions);
60+
BYTES getBytes(string rawUri)
61+
{
62+
return dictionary[rawUri];
63+
}
64+
65+
return new ReadContext(getBytes, null, checkExtensions);
6166
}
6267

6368
private ReadContext(FileReaderCallback reader, UriResolver uriResolver = null, bool checkExtensions = true)

src/SharpGLTF.Core/Schema2/gltf.Buffer.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ internal void _ResolveUri(ReadContext context)
4949

5050
private static Byte[] _LoadBinaryBufferUnchecked(string uri, ReadContext context)
5151
{
52-
var data = uri.TryParseBase64Unchecked(EMBEDDEDGLTFBUFFER, EMBEDDEDOCTETSTREAM);
53-
if (data != null) return data;
52+
if (uri != null)
53+
{
54+
var data = uri.TryParseBase64Unchecked(EMBEDDEDGLTFBUFFER, EMBEDDEDOCTETSTREAM);
55+
if (data != null) return data;
56+
}
5457

5558
var segment = context
5659
.ReadAllBytesToEnd(uri);
@@ -73,7 +76,7 @@ internal void _WriteToSatellite(WriteContext writer, string satelliteUri)
7376
{
7477
writer.WriteAllBytesToEnd(satelliteUri, new ArraySegment<byte>(_Content.GetPaddedContent()));
7578

76-
this._uri = satelliteUri._EscapeStringInternal();
79+
this._uri = UriKind.RelativeOrAbsolute.ToUri(satelliteUri).OriginalString;
7780
this._byteLength = _Content.Length;
7881
}
7982

@@ -170,7 +173,7 @@ public Buffer CreateBuffer(int byteCount)
170173

171174
/// <summary>
172175
/// Creates or reuses a <see cref="Buffer"/> instance
173-
/// at <see cref="ModelRoot.LogicalBuffers"/>.
176+
/// at <see cref="LogicalBuffers"/>.
174177
/// </summary>
175178
/// <param name="content">the byte array to be wrapped as a buffer</param>
176179
/// <returns>A <see cref="Buffer"/> instance.</returns>

src/SharpGLTF.Core/Schema2/gltf.Images.cs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ internal void TransferToInternalBuffer()
147147
internal void _ResolveUri(ReadContext context)
148148
{
149149
// No uri to decode.
150-
if (String.IsNullOrWhiteSpace(_uri)) return;
150+
if (_uri == null) return;
151151

152152
// Try decode Base64 embedded image.
153153
if (Memory.MemoryImage.TryParseMime64(_uri, out var memImage))
@@ -158,16 +158,19 @@ internal void _ResolveUri(ReadContext context)
158158
// Then it's a regular URI
159159
else
160160
{
161+
var satelliteUri = _uri;
162+
161163
// try resolve the full path
162-
if (context.TryGetFullPath(_uri, out string fullPath))
164+
if (context.TryGetFullPath(satelliteUri, out string fullPath))
163165
{
164166
_SatelliteContent = fullPath;
165167
}
166168

167169
// full path could not be resolved, use direct load instead.
168170
else
169171
{
170-
_SatelliteContent = new SharpGLTF.Memory.MemoryImage(context.ReadAllBytesToEnd(_uri), _uri);
172+
var data = context.ReadAllBytesToEnd(satelliteUri);
173+
_SatelliteContent = new SharpGLTF.Memory.MemoryImage(data, satelliteUri);
171174
}
172175
}
173176

@@ -236,9 +239,8 @@ internal void _WriteToSatellite(WriteContext writer, string satelliteUri)
236239
}
237240

238241
satelliteUri = writer.WriteImage(satelliteUri, imimg);
239-
240-
satelliteUri = satelliteUri.Replace("\\", "/", StringComparison.Ordinal);
241-
_uri = satelliteUri._EscapeStringInternal();
242+
243+
_uri = UriKind.RelativeOrAbsolute.ToUri(satelliteUri).OriginalString;
242244
_mimeType = null;
243245
}
244246

@@ -331,16 +333,19 @@ public Image UseImage(Memory.MemoryImage imageContent)
331333
}
332334

333335
/// <summary>
334-
/// Transfers all the <see cref="ModelRoot.LogicalImages"/> content into <see cref="BufferView"/> instances
336+
/// Transfers all the <see cref="LogicalImages"/> content into <see cref="BufferView"/> instances
335337
/// </summary>
336338
/// <remarks>
337-
/// Images can be stored in three different ways:
338-
/// - As satellite files.
339-
/// - Embedded as MIME64 into the JSON document
340-
/// - Referenced with <see cref="BufferView"/>
341-
///
342-
/// This call ensures all images will be internalized as <see cref="BufferView"/> instances.
343-
///
339+
/// Images can be stored in three different ways:<br/>
340+
/// - As satellite files.<br/>
341+
/// - Embedded as MIME64 into the JSON document<br/>
342+
/// - Referenced with <see cref="BufferView"/><br/>
343+
/// </remarks>
344+
/// <remarms>
345+
/// This call ensures all images will be internalized as <see cref="BufferView"/> instances.<br/>
346+
/// To write a GLB file, it is advised to call <see cref="MergeImages"/> before <see cref="MergeBuffers()"/>
347+
/// </remarms>
348+
/// <remarks>
344349
/// This action cannot be reversed.
345350
/// </remarks>
346351
public void MergeImages()

tests/SharpGLTF.Core.Tests/IO/JsonContentTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public void TestJsonExtendedCharacters()
199199
Assert.That(roundtripJson, Does.Contain("你好"));
200200

201201
// https://github.com/KhronosGroup/glTF/issues/1978#issuecomment-831744624
202-
Assert.That(roundtripJson, Does.Contain("extended%20%E4%BD%A0%E5%A5%BD%20characters.png"));
202+
// Assert.That(roundtripJson, Does.Contain("extended%20%E4%BD%A0%E5%A5%BD%20characters.png"));
203203

204204
Assert.That(roundtripModel.LogicalImages[0].Content.IsPng, Is.True);
205205
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
using NUnit.Framework;
8+
9+
using SharpGLTF.Memory;
10+
using SharpGLTF.Schema2;
11+
12+
namespace SharpGLTF.ThirdParty
13+
{
14+
internal class xPawTests
15+
{
16+
[Test]
17+
public void ParseUriTests()
18+
{
19+
var uri = new Uri("data:image/png;base64,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");
20+
21+
uri = new Uri("alpha\\beta", UriKind.RelativeOrAbsolute);
22+
}
23+
24+
[Test]
25+
public void ReproduceSaveGltfBug()
26+
{
27+
var model = ModelRoot.CreateModel();
28+
var image = model.CreateImage("satellite image with space.png");
29+
image.Content = new MemoryImage([137, 80, 78, 71, 0, 0, 0, 0, 0, 0, 0, 0]);
30+
image.AlternateWriteFileName = "satellite image with space.png";
31+
32+
using (var m = new System.IO.MemoryStream())
33+
{
34+
model.WriteGLB(m);
35+
}
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)