Add ultimate xr
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9b4b01a8c9bab94e9b674feafc6b02e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0855abdfb7ddf945ab79e51a5024864
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,309 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="FileExt.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UltimateXR.Exceptions;
|
||||
using UltimateXR.Extensions.Unity.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace UltimateXR.Extensions.System.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// File extensions.
|
||||
/// </summary>
|
||||
public static class FileExt
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Reads bytes from a file asynchronously.
|
||||
/// Multiple file locations are supported:
|
||||
/// <list type="bullet">
|
||||
/// <item>Files in <see cref="Application.streamingAssetsPath" /></item>
|
||||
/// <item>Files in an http:// location</item>
|
||||
/// <item>Files in a file:// location</item>
|
||||
/// </list>
|
||||
/// All other Uris will be considered file paths and the file:// location will be added.
|
||||
/// </summary>
|
||||
/// <param name="uri">File full path to be opened for reading</param>
|
||||
/// <param name="ct">
|
||||
/// Optional cancellation token, to be able to cancel the asynchronous operation
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Bytes read
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <see cref="UnityWebRequest.Get(string)">UnityWebRequest.Get()</see> is used internally to perform the actual
|
||||
/// reading
|
||||
/// </remarks>
|
||||
/// <exception cref="OperationCanceledException">
|
||||
/// Task canceled using <paramref name="ct" />
|
||||
/// </exception>
|
||||
/// <exception cref="FileNotFoundException">
|
||||
/// The file specified in <paramref name="uri" /> was not found.
|
||||
/// </exception>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// <paramref name="uri" /> is in an invalid format.
|
||||
/// </exception>
|
||||
/// <exception cref="IOException">
|
||||
/// An I/O error occurred while opening the file.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The stream is currently in use by a previous read operation.
|
||||
/// </exception>
|
||||
public static async Task<byte[]> Read(string uri, CancellationToken ct = default)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
uri.ThrowIfNullOrWhitespace(nameof(uri));
|
||||
|
||||
if (UnityWebRequestExt.IsUwrUri(uri))
|
||||
{
|
||||
try
|
||||
{
|
||||
return await UnityWebRequestExt.ReadBytesAsync(uri, ct);
|
||||
}
|
||||
catch (UwrException e)
|
||||
{
|
||||
throw new FileNotFoundException(e.Message, uri, e);
|
||||
}
|
||||
}
|
||||
|
||||
List<byte> bytes = new List<byte>();
|
||||
byte[] buffer = new byte[0x1000];
|
||||
using (var fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read, buffer.Length, true))
|
||||
{
|
||||
while (await fs.ReadAsync(buffer, 0, buffer.Length, ct).ConfigureAwait(false) != 0)
|
||||
{
|
||||
bytes.AddRange(buffer);
|
||||
}
|
||||
}
|
||||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads text from a file asynchronously.
|
||||
/// Multiple file locations are supported:
|
||||
/// <list type="bullet">
|
||||
/// <item>Files in <see cref="Application.streamingAssetsPath" /></item>
|
||||
/// <item>Files in an http:// location</item>
|
||||
/// <item>Files in a file:// location</item>
|
||||
/// </list>
|
||||
/// All other Uris will be considered file paths and the file:// location will be added.
|
||||
/// </summary>
|
||||
/// <param name="uri">File location</param>
|
||||
/// <param name="encoding">Optional file encoding</param>
|
||||
/// <param name="ct">Optional cancellation token, to cancel the asynchronous operation</param>
|
||||
/// <returns>A pair describing a boolean success value and the text read</returns>
|
||||
/// <remarks>
|
||||
/// <see cref="UnityWebRequest.Get(string)">UnityWebRequest.Get()</see> is used internally to perform the actual
|
||||
/// reading
|
||||
/// </remarks>
|
||||
public static async Task<(bool success, string text)> TryReadText(string uri, Encoding encoding = default, CancellationToken ct = default)
|
||||
{
|
||||
(bool success, string text) result;
|
||||
try
|
||||
{
|
||||
result.text = await ReadText(uri, encoding, ct).ConfigureAwait(false);
|
||||
result.success = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
result.text = null;
|
||||
result.success = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads text from a file asynchronously.
|
||||
/// Multiple file locations are supported:
|
||||
/// <list type="bullet">
|
||||
/// <item>Files in <see cref="Application.streamingAssetsPath" /></item>
|
||||
/// <item>Files in an http:// location</item>
|
||||
/// <item>Files in a file:// location</item>
|
||||
/// </list>
|
||||
/// All other Uris will be considered file paths and the file:// location will be added.
|
||||
/// </summary>
|
||||
/// <param name="uri">File full path to be opened for reading</param>
|
||||
/// <param name="encoding">Optional file encoding</param>
|
||||
/// <param name="ct">Optional cancellation token, to cancel the asynchronous operation</param>
|
||||
/// <returns>Text content of the file or <see langword="null" /> if not found.</returns>
|
||||
/// <remarks>
|
||||
/// <see cref="UnityWebRequest.Get(string)">UnityWebRequest.Get()</see> is used internally to perform the actual
|
||||
/// reading
|
||||
/// </remarks>
|
||||
/// <exception cref="OperationCanceledException">
|
||||
/// Task canceled using <paramref name="ct" />
|
||||
/// </exception>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// <paramref name="uri" /> is in an invalid format.
|
||||
/// </exception>
|
||||
/// <exception cref="IOException">
|
||||
/// An I/O error occurred while opening the file.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The stream is currently in use by a previous read operation.
|
||||
/// </exception>
|
||||
public static async Task<string> ReadText(string uri, Encoding encoding = default, CancellationToken ct = default)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
uri.ThrowIfNullOrWhitespace(nameof(uri));
|
||||
encoding ??= DefaultEncoding;
|
||||
|
||||
if (UnityWebRequestExt.IsUwrUri(uri))
|
||||
{
|
||||
try
|
||||
{
|
||||
return await UnityWebRequestExt.ReadTextAsync(uri, ct);
|
||||
}
|
||||
catch (UwrException e)
|
||||
{
|
||||
throw new FileNotFoundException(e.Message, uri, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!File.Exists(uri))
|
||||
{
|
||||
throw new FileNotFoundException("File does not exist", uri);
|
||||
}
|
||||
|
||||
using StreamReader sr = new StreamReader(uri, encoding, true);
|
||||
return await sr.ReadToEndAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously writes an <see cref="Array" /> of <see cref="byte" /> to a file at <paramref name="path" />.
|
||||
/// </summary>
|
||||
/// <param name="bytes">File content as <see cref="Array" /> of <see cref="byte" /></param>
|
||||
/// <param name="path">File full path to be opened for writing</param>
|
||||
/// <param name="ct">Optional cancellation token, to cancel the asynchronous operation</param>
|
||||
/// <returns>An awaitable writing <see cref="Task" /></returns>
|
||||
/// <exception cref="IOException">
|
||||
/// An I/O error occurred while creating the file.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="path" /> is a zero-length string, contains only white space, or
|
||||
/// contains one or more invalid characters. You can query for invalid characters by using the
|
||||
/// <see cref="Path.GetInvalidPathChars" /> method.
|
||||
/// </exception>
|
||||
/// <exception cref="PathTooLongException">
|
||||
/// The <paramref name="path" /> parameter is longer than the system-defined maximum length.
|
||||
/// </exception>
|
||||
/// <exception cref="UnauthorizedAccessException">
|
||||
/// The caller does not have the required permission.
|
||||
/// </exception>
|
||||
/// <exception cref="OperationCanceledException">
|
||||
/// Task canceled using <paramref name="ct" />
|
||||
/// </exception>
|
||||
public static async Task Write(byte[] bytes, string path, CancellationToken ct = default)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
path.ThrowIfNullOrWhitespace(nameof(path));
|
||||
|
||||
using var stream = new MemoryStream(bytes);
|
||||
await Write(path, stream, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously writes the content of an <paramref name="sourceStream" /> to a file at <paramref name="path" />.
|
||||
/// </summary>
|
||||
/// <param name="path">File full path to be opened for writing</param>
|
||||
/// <param name="sourceStream"><see cref="Stream" /> to be written into a file.</param>
|
||||
/// <param name="ct">Optional cancellation token, to cancel the asynchronous operation</param>
|
||||
/// <returns>An awaitable writing <see cref="Task" /></returns>
|
||||
/// <exception cref="IOException">
|
||||
/// An I/O error occurred while creating the file.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <paramref name="path" /> is a zero-length string, contains only white space, or
|
||||
/// contains one or more invalid characters. You can query for invalid characters by using the
|
||||
/// <see cref="Path.GetInvalidPathChars" /> method.
|
||||
/// </exception>
|
||||
/// <exception cref="PathTooLongException">
|
||||
/// The <paramref name="path" /> parameter is longer than the system-defined maximum length.
|
||||
/// </exception>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// <paramref name="sourceStream" /> does not support reading, or the destination stream does not support writing.
|
||||
/// </exception>
|
||||
/// <exception cref="UnauthorizedAccessException">
|
||||
/// The caller does not have the required permission.
|
||||
/// </exception>
|
||||
/// <exception cref="OperationCanceledException">
|
||||
/// Task canceled using <paramref name="ct" />
|
||||
/// </exception>
|
||||
public static Task Write(string path, Stream sourceStream, CancellationToken ct = default)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
path.ThrowIfNullOrWhitespace(nameof(path));
|
||||
sourceStream.ThrowIfNull(nameof(sourceStream));
|
||||
|
||||
string dirName = Path.GetDirectoryName(path);
|
||||
const int bufferSize = 81920;
|
||||
|
||||
async Task WriteInternal()
|
||||
{
|
||||
if (dirName != null)
|
||||
{
|
||||
Directory.CreateDirectory(dirName);
|
||||
}
|
||||
|
||||
using FileStream outputStream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, FileOptions.Asynchronous);
|
||||
await sourceStream.CopyToAsync(outputStream, bufferSize, ct).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Creating the new file has a performance impact that requires a new thread.
|
||||
return Task.Run(WriteInternal, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously writes text to a file location.
|
||||
/// </summary>
|
||||
/// <param name="path">File full path to be opened for writing</param>
|
||||
/// <param name="text">Text to write</param>
|
||||
/// <param name="encoding">Optional file encoding</param>
|
||||
/// <param name="append">Optional boolean telling whether to append or override. Default behaviour is to override.</param>
|
||||
/// <param name="ct">Optional cancellation token, to cancel the asynchronous operation</param>
|
||||
/// <returns>Awaitable task</returns>
|
||||
/// <exception cref="OperationCanceledException">
|
||||
/// Task canceled using <paramref name="ct" />
|
||||
/// </exception>
|
||||
public static Task WriteText(string path, string text, Encoding encoding = default, bool append = false, CancellationToken ct = default)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
path.ThrowIfNullOrWhitespace(nameof(path));
|
||||
|
||||
encoding ??= DefaultEncoding;
|
||||
string dirName = Path.GetDirectoryName(path);
|
||||
|
||||
async Task WriteInternal()
|
||||
{
|
||||
if (dirName != null)
|
||||
{
|
||||
Directory.CreateDirectory(dirName);
|
||||
}
|
||||
|
||||
using StreamWriter sw = new StreamWriter(path, append, encoding);
|
||||
await sw.WriteAsync(text).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Creating the new file has a performance impact that requires a new thread.
|
||||
return Task.Run(WriteInternal, ct);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private static readonly Encoding DefaultEncoding = new UTF8Encoding(false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f1327e2dd4ce0241b881966c84b67a6
|
||||
timeCreated: 1620127914
|
||||
@@ -0,0 +1,124 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="PathExt.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UltimateXR.Core;
|
||||
using UltimateXR.Core.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Extensions.System.IO
|
||||
{
|
||||
public static class PathExt
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Like .NET's <see cref="Path.Combine(string,string)" /> but addressing some issues discussed in
|
||||
/// https://www.davidboike.dev/2020/06/path-combine-isnt-as-cross-platform-as-you-think-it-is/
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base path</param>
|
||||
/// <param name="additional">Additional segments or multi-segment paths</param>
|
||||
/// <returns>Path result of combining the base path and the additional segments or multi-segment paths</returns>
|
||||
public static string Combine(string basePath, params string[] additional)
|
||||
{
|
||||
string[][] splits = additional.Select(s => s.Split(PathSplitCharacters)).ToArray();
|
||||
int totalLength = splits.Sum(arr => arr.Length);
|
||||
string[] segments = new string[totalLength + 1];
|
||||
|
||||
segments[0] = basePath;
|
||||
var i = 0;
|
||||
|
||||
foreach (string[] split in splits)
|
||||
{
|
||||
foreach (string value in split)
|
||||
{
|
||||
i++;
|
||||
segments[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return Path.Combine(segments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a path or sub-path so that any wrong directory separator char is fixed for the current platform.
|
||||
/// </summary>
|
||||
/// <param name="multiSegment">pathOrSubPath</param>
|
||||
/// <returns>Normalized path</returns>
|
||||
public static string Normalize(string pathOrSubPath)
|
||||
{
|
||||
if (Path.IsPathFullyQualified(pathOrSubPath))
|
||||
{
|
||||
return Path.GetFullPath(new Uri(pathOrSubPath).LocalPath).TrimEnd(PathSplitCharacters);
|
||||
}
|
||||
|
||||
foreach (char separator in PathSplitCharacters.Where(c => c != Path.DirectorySeparatorChar))
|
||||
{
|
||||
pathOrSubPath = pathOrSubPath.Replace(separator, Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
return pathOrSubPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a path is a child of another path.
|
||||
/// Adapted from https://stackoverflow.com/questions/8091829/how-to-check-if-one-path-is-a-child-of-another-path
|
||||
/// </summary>
|
||||
/// <param name="candidate">Path candidate</param>
|
||||
/// <param name="other">Path to check against</param>
|
||||
/// <param name="canBeSame">Whether to also consider the same directory as valid</param>
|
||||
/// <returns>Whether the path is child of the parent path</returns>
|
||||
public static bool IsSubDirectoryOf(string candidate, string other, bool canBeSame = true)
|
||||
{
|
||||
var isChild = false;
|
||||
try
|
||||
{
|
||||
// Some initial corrections to avoid false negatives:
|
||||
|
||||
var candidateInfo = new DirectoryInfo(candidate.Replace(@"\", @"/").TrimEnd('/'));
|
||||
var otherInfo = new DirectoryInfo(other.Replace(@"\", @"/").TrimEnd('/'));
|
||||
|
||||
// Check if same directory
|
||||
|
||||
if (canBeSame && string.Compare(candidateInfo.FullName, otherInfo.FullName, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Start traversing upwards
|
||||
|
||||
while (candidateInfo.Parent != null)
|
||||
{
|
||||
if (string.Equals(candidateInfo.Parent.FullName, otherInfo.FullName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
isChild = true;
|
||||
break;
|
||||
}
|
||||
|
||||
candidateInfo = candidateInfo.Parent;
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
|
||||
{
|
||||
Debug.LogError($"{UxrConstants.CoreModule} Unable to check directories {candidate} and {other}: {error}");
|
||||
}
|
||||
}
|
||||
|
||||
return isChild;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Types & Data
|
||||
|
||||
private static readonly char[] PathSplitCharacters = { '/', '\\' };
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e021f51e7aad3b4889b7e7ff42776c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user