mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Change most things to new byte strings, introduce new ResourceLoader and Logger fully.
This commit is contained in:
parent
5d77cd5514
commit
f5fccb0235
55 changed files with 2681 additions and 2730 deletions
166
Penumbra.GameData/ByteString/Utf8GamePath.cs
Normal file
166
Penumbra.GameData/ByteString/Utf8GamePath.cs
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Dalamud.Utility;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.Util;
|
||||
|
||||
namespace Penumbra.GameData.ByteString;
|
||||
|
||||
// NewGamePath wrap some additional validity checking around Utf8String,
|
||||
// provide some filesystem helpers, and conversion to Json.
|
||||
[JsonConverter( typeof( Utf8GamePathConverter ) )]
|
||||
public readonly struct Utf8GamePath : IEquatable< Utf8GamePath >, IComparable< Utf8GamePath >, IDisposable
|
||||
{
|
||||
public const int MaxGamePathLength = 256;
|
||||
|
||||
public readonly Utf8String Path;
|
||||
public static readonly Utf8GamePath Empty = new(Utf8String.Empty);
|
||||
|
||||
internal Utf8GamePath( Utf8String s )
|
||||
=> Path = s;
|
||||
|
||||
public int Length
|
||||
=> Path.Length;
|
||||
|
||||
public bool IsEmpty
|
||||
=> Path.IsEmpty;
|
||||
|
||||
public Utf8GamePath ToLower()
|
||||
=> new(Path.AsciiToLower());
|
||||
|
||||
public static unsafe bool FromPointer( byte* ptr, out Utf8GamePath path, bool lower = false )
|
||||
{
|
||||
var utf = new Utf8String( ptr );
|
||||
return ReturnChecked( utf, out path, lower );
|
||||
}
|
||||
|
||||
public static bool FromSpan( ReadOnlySpan< byte > data, out Utf8GamePath path, bool lower = false )
|
||||
{
|
||||
var utf = Utf8String.FromSpanUnsafe( data, false, null, null );
|
||||
return ReturnChecked( utf, out path, lower );
|
||||
}
|
||||
|
||||
// Does not check for Forward/Backslashes due to assuming that SE-strings use the correct one.
|
||||
// Does not check for initial slashes either, since they are assumed to be by choice.
|
||||
// Checks for maxlength, ASCII and lowercase.
|
||||
private static bool ReturnChecked( Utf8String utf, out Utf8GamePath path, bool lower = false )
|
||||
{
|
||||
path = Empty;
|
||||
if( !utf.IsAscii || utf.Length > MaxGamePathLength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
path = new Utf8GamePath( lower ? utf.AsciiToLower() : utf );
|
||||
return true;
|
||||
}
|
||||
|
||||
public Utf8GamePath Clone()
|
||||
=> new(Path.Clone());
|
||||
|
||||
public static explicit operator Utf8GamePath( string s )
|
||||
=> FromString( s, out var p, true ) ? p : Empty;
|
||||
|
||||
public static bool FromString( string? s, out Utf8GamePath path, bool toLower = false )
|
||||
{
|
||||
path = Empty;
|
||||
if( s.IsNullOrEmpty() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var substring = s!.Replace( '\\', '/' );
|
||||
substring.TrimStart( '/' );
|
||||
if( substring.Length > MaxGamePathLength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( substring.Length == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if( !Utf8String.FromString( substring, out var ascii, toLower ) || !ascii.IsAscii )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
path = new Utf8GamePath( ascii );
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool FromFile( FileInfo file, DirectoryInfo baseDir, out Utf8GamePath path, bool toLower = false )
|
||||
{
|
||||
path = Empty;
|
||||
if( !file.FullName.StartsWith( baseDir.FullName ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var substring = file.FullName[ baseDir.FullName.Length.. ];
|
||||
return FromString( substring, out path, toLower );
|
||||
}
|
||||
|
||||
public Utf8String Filename()
|
||||
{
|
||||
var idx = Path.LastIndexOf( ( byte )'/' );
|
||||
return idx == -1 ? Path : Path.Substring( idx + 1 );
|
||||
}
|
||||
|
||||
public Utf8String Extension()
|
||||
{
|
||||
var idx = Path.LastIndexOf( ( byte )'.' );
|
||||
return idx == -1 ? Utf8String.Empty : Path.Substring( idx );
|
||||
}
|
||||
|
||||
public bool Equals( Utf8GamePath other )
|
||||
=> Path.Equals( other.Path );
|
||||
|
||||
public override int GetHashCode()
|
||||
=> Path.GetHashCode();
|
||||
|
||||
public int CompareTo( Utf8GamePath other )
|
||||
=> Path.CompareTo( other.Path );
|
||||
|
||||
public override string ToString()
|
||||
=> Path.ToString();
|
||||
|
||||
public void Dispose()
|
||||
=> Path.Dispose();
|
||||
|
||||
public bool IsRooted()
|
||||
=> Path.Length >= 1 && ( Path[ 0 ] == '/' || Path[ 0 ] == '\\' )
|
||||
|| Path.Length >= 2
|
||||
&& ( Path[ 0 ] >= 'A' && Path[ 0 ] <= 'Z' || Path[ 0 ] >= 'a' && Path[ 0 ] <= 'z' )
|
||||
&& Path[ 1 ] == ':';
|
||||
|
||||
public class Utf8GamePathConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert( Type objectType )
|
||||
=> objectType == typeof( Utf8GamePath );
|
||||
|
||||
public override object ReadJson( JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer )
|
||||
{
|
||||
var token = JToken.Load( reader ).ToString();
|
||||
return FromString( token, out var p, true )
|
||||
? p
|
||||
: throw new JsonException( $"Could not convert \"{token}\" to {nameof( Utf8GamePath )}." );
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
=> true;
|
||||
|
||||
public override void WriteJson( JsonWriter writer, object? value, JsonSerializer serializer )
|
||||
{
|
||||
if( value is Utf8GamePath p )
|
||||
{
|
||||
serializer.Serialize( writer, p.ToString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GamePath ToGamePath()
|
||||
=> GamePath.GenerateUnchecked( ToString() );
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue