Penumbra/Penumbra.GameData/ByteString/Utf8RelPath.cs

138 lines
No EOL
4 KiB
C#

using System;
using System.IO;
using Dalamud.Utility;
using Microsoft.VisualBasic.CompilerServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Penumbra.GameData.ByteString;
[JsonConverter( typeof( Utf8RelPathConverter ) )]
public readonly struct Utf8RelPath : IEquatable< Utf8RelPath >, IComparable< Utf8RelPath >, IDisposable
{
public const int MaxRelPathLength = 250;
public readonly Utf8String Path;
public static readonly Utf8RelPath Empty = new(Utf8String.Empty);
internal Utf8RelPath( Utf8String path )
=> Path = path;
public static explicit operator Utf8RelPath( string s )
=> FromString( s, out var p ) ? p : Empty;
public static bool FromString( string? s, out Utf8RelPath path )
{
path = Empty;
if( s.IsNullOrEmpty() )
{
return true;
}
var substring = s!.Replace( '/', '\\' );
substring.TrimStart( '\\' );
if( substring.Length > MaxRelPathLength )
{
return false;
}
if( substring.Length == 0 )
{
return true;
}
if( !Utf8String.FromString( substring, out var ascii ) || !ascii.IsAscii )
{
return false;
}
path = new Utf8RelPath( ascii );
return true;
}
public static bool FromFile( FileInfo file, DirectoryInfo baseDir, out Utf8RelPath path )
{
path = Empty;
if( !file.FullName.StartsWith( baseDir.FullName ) )
{
return false;
}
var substring = file.FullName[ baseDir.FullName.Length.. ];
return FromString( substring, out path );
}
public static bool FromFile( FullPath file, DirectoryInfo baseDir, out Utf8RelPath path )
{
path = Empty;
if( !file.FullName.StartsWith( baseDir.FullName ) )
{
return false;
}
var substring = file.FullName[ baseDir.FullName.Length.. ];
return FromString( substring, out path );
}
public Utf8RelPath( Utf8GamePath gamePath )
=> Path = gamePath.Path.Replace( ( byte )'/', ( byte )'\\' );
public unsafe Utf8GamePath ToGamePath( int skipFolders = 0 )
{
var idx = 0;
while( skipFolders > 0 )
{
idx = Path.IndexOf( ( byte )'\\', idx ) + 1;
--skipFolders;
if( idx <= 0 )
{
return Utf8GamePath.Empty;
}
}
var length = Path.Length - idx;
var ptr = ByteStringFunctions.CopyString( Path.Path + idx, length );
ByteStringFunctions.Replace( ptr, length, ( byte )'\\', ( byte )'/' );
ByteStringFunctions.AsciiToLowerInPlace( ptr, length );
var utf = new Utf8String().Setup( ptr, length, null, true, true, true, true );
return new Utf8GamePath( utf );
}
public int CompareTo( Utf8RelPath rhs )
=> Path.CompareTo( rhs.Path );
public bool Equals( Utf8RelPath other )
=> Path.Equals( other.Path );
public override string ToString()
=> Path.ToString();
public void Dispose()
=> Path.Dispose();
public class Utf8RelPathConverter : JsonConverter
{
public override bool CanConvert( Type objectType )
=> objectType == typeof( Utf8RelPath );
public override object ReadJson( JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer )
{
var token = JToken.Load( reader ).ToString();
return FromString( token, out var p )
? p
: throw new JsonException( $"Could not convert \"{token}\" to {nameof( Utf8RelPath )}." );
}
public override bool CanWrite
=> true;
public override void WriteJson( JsonWriter writer, object? value, JsonSerializer serializer )
{
if( value is Utf8RelPath p )
{
serializer.Serialize( writer, p.ToString() );
}
}
}
}