Penumbra/Penumbra.GameData/ByteString/NewRelPath.cs

133 lines
No EOL
3.8 KiB
C#

using System;
using System.IO;
using Dalamud.Utility;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Penumbra.GameData.ByteString;
[JsonConverter( typeof( NewRelPathConverter ) )]
public readonly struct NewRelPath : IEquatable< NewRelPath >, IComparable< NewRelPath >, IDisposable
{
public const int MaxRelPathLength = 250;
public readonly Utf8String Path;
public static readonly NewRelPath Empty = new(Utf8String.Empty);
internal NewRelPath( Utf8String path )
=> Path = path;
public static bool FromString( string? s, out NewRelPath 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 NewRelPath( ascii );
return true;
}
public static bool FromFile( FileInfo file, DirectoryInfo baseDir, out NewRelPath 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 NewRelPath path )
{
path = Empty;
if( !file.FullName.StartsWith( baseDir.FullName ) )
{
return false;
}
var substring = file.FullName[ baseDir.FullName.Length.. ];
return FromString( substring, out path );
}
public NewRelPath( NewGamePath gamePath )
=> Path = gamePath.Path.Replace( ( byte )'/', ( byte )'\\' );
public unsafe NewGamePath ToGamePath( int skipFolders = 0 )
{
var idx = 0;
while( skipFolders > 0 )
{
idx = Path.IndexOf( ( byte )'\\', idx ) + 1;
--skipFolders;
if( idx <= 0 )
{
return NewGamePath.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 NewGamePath( utf );
}
public int CompareTo( NewRelPath rhs )
=> Path.CompareTo( rhs.Path );
public bool Equals( NewRelPath other )
=> Path.Equals( other.Path );
public override string ToString()
=> Path.ToString();
public void Dispose()
=> Path.Dispose();
private class NewRelPathConverter : JsonConverter
{
public override bool CanConvert( Type objectType )
=> objectType == typeof( NewRelPath );
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( NewRelPath )}." );
}
public override bool CanWrite
=> true;
public override void WriteJson( JsonWriter writer, object? value, JsonSerializer serializer )
{
if( value is NewRelPath p )
{
serializer.Serialize( writer, p.ToString() );
}
}
}
}