mirror of
https://github.com/AssetRipper/AssetRipper.git
synced 2025-12-11 20:15:29 +01:00
parent
07d415a794
commit
2f43a7abe5
@ -0,0 +1,17 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.IO.Files.SourceGenerator;
|
||||
|
||||
internal static class ParameterExtensions
|
||||
{
|
||||
public static bool IsParams(this ParameterInfo parameter)
|
||||
{
|
||||
return parameter.GetCustomAttribute<ParamCollectionAttribute>() is not null;
|
||||
}
|
||||
|
||||
public static string GetParamsPrefix(this ParameterInfo parameter)
|
||||
{
|
||||
return parameter.IsParams() ? "params " : "";
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.IO.Files.SourceGenerator;
|
||||
@ -17,6 +16,7 @@ internal static class Program
|
||||
WriteVirtualFileSystemClass();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="File.WriteAllBytesAsync(string, byte[], CancellationToken)"/>
|
||||
private static void WriteFileSystemClass()
|
||||
{
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(OutputDirectory, "FileSystem");
|
||||
@ -49,9 +49,11 @@ internal static class Program
|
||||
|
||||
foreach (FileSystemApi api in classApiList)
|
||||
{
|
||||
// Inherit documentation from System.IO
|
||||
writer.WriteLine($"/// <inheritdoc cref=\"{api.FullName}({api.ParametersWithoutNames})\"/>");
|
||||
|
||||
string virtualKeyword = api.Type is FileSystemApiType.Sealed ? "" : "virtual ";
|
||||
string parametersWithTypes = string.Join(", ", api.Parameters.Select(parameter => $"{parameter.Item1} {parameter.Item2}"));
|
||||
writer.WriteLine($"public {virtualKeyword}{api.BaseReturnType} {api.Name}({parametersWithTypes})");
|
||||
writer.WriteLine($"public {virtualKeyword}{api.BaseReturnType} {api.Name}({api.ParametersWithTypes})");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
if (api.Type is FileSystemApiType.Throw)
|
||||
@ -61,8 +63,7 @@ internal static class Program
|
||||
else
|
||||
{
|
||||
string returnKeyword = api.VoidReturn ? "" : "return ";
|
||||
string parametersWithoutTypes = string.Join(", ", api.Parameters.Select(parameter => parameter.Item2));
|
||||
writer.WriteLine($"{returnKeyword}{api.FullName}({parametersWithoutTypes});");
|
||||
writer.WriteLine($"{returnKeyword}{api.FullName}({api.ParametersWithoutTypes});");
|
||||
}
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
@ -101,13 +102,11 @@ internal static class Program
|
||||
continue;
|
||||
}
|
||||
|
||||
string parametersWithTypes = string.Join(", ", api.Parameters.Select(parameter => $"{parameter.Item1} {parameter.Item2}"));
|
||||
writer.WriteLine($"public override {api.DerivedReturnType} {api.Name}({parametersWithTypes})");
|
||||
writer.WriteLine($"public override {api.DerivedReturnType} {api.Name}({api.ParametersWithTypes})");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
string returnKeyword = api.VoidReturn ? "" : "return ";
|
||||
string parametersWithoutTypes = string.Join(", ", api.Parameters.Select(parameter => parameter.Item2));
|
||||
writer.WriteLine($"{returnKeyword}{api.FullName}({parametersWithoutTypes});");
|
||||
writer.WriteLine($"{returnKeyword}{api.FullName}({api.ParametersWithoutTypes});");
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
}
|
||||
@ -170,10 +169,10 @@ internal static class Program
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<FileSystemApi>> apiDictionary = new()
|
||||
private static readonly Dictionary<string, List<FileSystemApi>> apiDictionary = new()
|
||||
{
|
||||
[nameof(File)] = new()
|
||||
{
|
||||
[nameof(File)] =
|
||||
[
|
||||
new((Func<string, FileStream>)File.Create),
|
||||
new(File.Delete),
|
||||
new(File.Exists),
|
||||
@ -185,9 +184,9 @@ internal static class Program
|
||||
new((Action<string, ReadOnlySpan<byte>>)File.WriteAllBytes),
|
||||
new((Action<string, ReadOnlySpan<char>>)File.WriteAllText),
|
||||
new((Action<string, ReadOnlySpan<char>, Encoding>)File.WriteAllText),
|
||||
},
|
||||
[nameof(Directory)] = new()
|
||||
{
|
||||
],
|
||||
[nameof(Directory)] =
|
||||
[
|
||||
new((Func<string, string, SearchOption, IEnumerable<string>>)Directory.EnumerateDirectories),
|
||||
new((Func<string, string, SearchOption, IEnumerable<string>>)Directory.EnumerateFiles),
|
||||
new((Func<string, string, SearchOption, IEnumerable<string>>)Directory.GetDirectories),
|
||||
@ -201,9 +200,9 @@ internal static class Program
|
||||
new((Func<string, IEnumerable<string>>)Directory.GetDirectories),
|
||||
new((Func<string, IEnumerable<string>>)Directory.GetFiles),
|
||||
new(Directory.Exists),
|
||||
},
|
||||
[nameof(Path)] = new()
|
||||
{
|
||||
],
|
||||
[nameof(Path)] =
|
||||
[
|
||||
new((Func<string, string, string>)Path.Join) { Type = FileSystemApiType.Virtual },
|
||||
new((Func<string, string, string, string>)Path.Join) { Type = FileSystemApiType.Virtual },
|
||||
new((Func<string, string, string, string, string>)Path.Join) { Type = FileSystemApiType.Virtual },
|
||||
@ -219,7 +218,7 @@ internal static class Program
|
||||
new((Func<string, string>)Path.GetFullPath),
|
||||
new(Path.GetRelativePath) { Type = FileSystemApiType.Sealed },
|
||||
new((Func<ReadOnlySpan<char>, bool>)Path.IsPathRooted),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
private enum FileSystemApiType
|
||||
@ -228,6 +227,7 @@ internal static class Program
|
||||
Virtual,
|
||||
Sealed,
|
||||
}
|
||||
|
||||
private sealed record class FileSystemApi
|
||||
{
|
||||
public required Delegate Delegate { get; init; }
|
||||
@ -250,6 +250,7 @@ internal static class Program
|
||||
.Select(parameter => (parameter.GetParamsPrefix() + parameter.ParameterType.GetGlobalQualifiedName(), parameter.Name!));
|
||||
public string ParametersWithTypes => string.Join(", ", Parameters.Select(parameter => $"{parameter.Item1} {parameter.Item2}"));
|
||||
public string ParametersWithoutTypes => string.Join(", ", Parameters.Select(parameter => parameter.Item2));
|
||||
public string ParametersWithoutNames => string.Join(", ", Parameters.Select(parameter => parameter.Item1));
|
||||
|
||||
public FileSystemApi()
|
||||
{
|
||||
@ -262,42 +263,3 @@ internal static class Program
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static class ParameterExtensions
|
||||
{
|
||||
public static bool IsParams(this ParameterInfo parameter)
|
||||
{
|
||||
return parameter.GetCustomAttribute<ParamCollectionAttribute>() is not null;
|
||||
}
|
||||
|
||||
public static string GetParamsPrefix(this ParameterInfo parameter)
|
||||
{
|
||||
return parameter.IsParams() ? "params " : "";
|
||||
}
|
||||
}
|
||||
internal static class TypeExtensions
|
||||
{
|
||||
public static string GetGlobalQualifiedName(this Type type)
|
||||
{
|
||||
if (type == typeof(void))
|
||||
{
|
||||
return "void";
|
||||
}
|
||||
else if (type.IsGenericType)
|
||||
{
|
||||
// Handle generic types by appending generic arguments
|
||||
string genericTypeDefinition = type.GetGenericTypeDefinition().FullName!;
|
||||
string genericArguments = string.Join(", ", type.GetGenericArguments()
|
||||
.Select(t => t.GetGlobalQualifiedName()));
|
||||
return $"global::{genericTypeDefinition[..genericTypeDefinition.IndexOf('`')]}<{genericArguments}>";
|
||||
}
|
||||
else if (type.IsArray)
|
||||
{
|
||||
// Handle arrays
|
||||
return $"{type.GetElementType()!.GetGlobalQualifiedName()}[{new string(',', type.GetArrayRank() - 1)}]";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"global::{type.FullName}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
namespace AssetRipper.IO.Files.SourceGenerator;
|
||||
|
||||
internal static class TypeExtensions
|
||||
{
|
||||
public static string GetGlobalQualifiedName(this Type type)
|
||||
{
|
||||
if (type == typeof(void))
|
||||
{
|
||||
return "void";
|
||||
}
|
||||
else if (type.IsGenericType)
|
||||
{
|
||||
// Handle generic types by appending generic arguments
|
||||
string genericTypeDefinition = type.GetGenericTypeDefinition().FullName!;
|
||||
string genericArguments = string.Join(", ", type.GetGenericArguments().Select(GetGlobalQualifiedName));
|
||||
return $"global::{genericTypeDefinition[..genericTypeDefinition.IndexOf('`')]}<{genericArguments}>";
|
||||
}
|
||||
else if (type.IsArray)
|
||||
{
|
||||
// Handle arrays
|
||||
return $"{type.GetElementType()!.GetGlobalQualifiedName()}[{new string(',', type.GetArrayRank() - 1)}]";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"global::{type.FullName}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,56 +12,67 @@ public abstract partial class FileSystem
|
||||
protected FileImplementation File => this;
|
||||
protected DirectoryImplementation Directory => Parent.Directory;
|
||||
protected PathImplementation Path => Parent.Path;
|
||||
/// <inheritdoc cref="global::System.IO.File.Create(global::System.String)"/>
|
||||
public virtual global::System.IO.Stream Create(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.File.Delete(global::System.String)"/>
|
||||
public virtual void Delete(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.File.Exists(global::System.String)"/>
|
||||
public virtual global::System.Boolean Exists(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.File.OpenRead(global::System.String)"/>
|
||||
public virtual global::System.IO.Stream OpenRead(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.File.OpenWrite(global::System.String)"/>
|
||||
public virtual global::System.IO.Stream OpenWrite(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.File.ReadAllBytes(global::System.String)"/>
|
||||
public virtual global::System.Byte[] ReadAllBytes(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.File.ReadAllText(global::System.String)"/>
|
||||
public virtual global::System.String ReadAllText(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.File.ReadAllText(global::System.String, global::System.Text.Encoding)"/>
|
||||
public virtual global::System.String ReadAllText(global::System.String path, global::System.Text.Encoding encoding)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.File.WriteAllBytes(global::System.String, global::System.ReadOnlySpan<global::System.Byte>)"/>
|
||||
public virtual void WriteAllBytes(global::System.String path, global::System.ReadOnlySpan<global::System.Byte> bytes)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.File.WriteAllText(global::System.String, global::System.ReadOnlySpan<global::System.Char>)"/>
|
||||
public virtual void WriteAllText(global::System.String path, global::System.ReadOnlySpan<global::System.Char> contents)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.File.WriteAllText(global::System.String, global::System.ReadOnlySpan<global::System.Char>, global::System.Text.Encoding)"/>
|
||||
public virtual void WriteAllText(global::System.String path, global::System.ReadOnlySpan<global::System.Char> contents, global::System.Text.Encoding encoding)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
@ -81,66 +92,79 @@ public abstract partial class FileSystem
|
||||
protected FileImplementation File => Parent.File;
|
||||
protected DirectoryImplementation Directory => this;
|
||||
protected PathImplementation Path => Parent.Path;
|
||||
/// <inheritdoc cref="global::System.IO.Directory.EnumerateDirectories(global::System.String, global::System.String, global::System.IO.SearchOption)"/>
|
||||
public virtual global::System.Collections.Generic.IEnumerable<global::System.String> EnumerateDirectories(global::System.String path, global::System.String searchPattern, global::System.IO.SearchOption searchOption)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.EnumerateFiles(global::System.String, global::System.String, global::System.IO.SearchOption)"/>
|
||||
public virtual global::System.Collections.Generic.IEnumerable<global::System.String> EnumerateFiles(global::System.String path, global::System.String searchPattern, global::System.IO.SearchOption searchOption)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.GetDirectories(global::System.String, global::System.String, global::System.IO.SearchOption)"/>
|
||||
public virtual global::System.String[] GetDirectories(global::System.String path, global::System.String searchPattern, global::System.IO.SearchOption searchOption)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.GetFiles(global::System.String, global::System.String, global::System.IO.SearchOption)"/>
|
||||
public virtual global::System.String[] GetFiles(global::System.String path, global::System.String searchPattern, global::System.IO.SearchOption searchOption)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.EnumerateDirectories(global::System.String, global::System.String)"/>
|
||||
public virtual global::System.Collections.Generic.IEnumerable<global::System.String> EnumerateDirectories(global::System.String path, global::System.String searchPattern)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.EnumerateFiles(global::System.String, global::System.String)"/>
|
||||
public virtual global::System.Collections.Generic.IEnumerable<global::System.String> EnumerateFiles(global::System.String path, global::System.String searchPattern)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.GetDirectories(global::System.String, global::System.String)"/>
|
||||
public virtual global::System.String[] GetDirectories(global::System.String path, global::System.String searchPattern)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.GetFiles(global::System.String, global::System.String)"/>
|
||||
public virtual global::System.String[] GetFiles(global::System.String path, global::System.String searchPattern)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.EnumerateDirectories(global::System.String)"/>
|
||||
public virtual global::System.Collections.Generic.IEnumerable<global::System.String> EnumerateDirectories(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.EnumerateFiles(global::System.String)"/>
|
||||
public virtual global::System.Collections.Generic.IEnumerable<global::System.String> EnumerateFiles(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.GetDirectories(global::System.String)"/>
|
||||
public virtual global::System.String[] GetDirectories(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.GetFiles(global::System.String)"/>
|
||||
public virtual global::System.String[] GetFiles(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Directory.Exists(global::System.String)"/>
|
||||
public virtual global::System.Boolean Exists(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
@ -160,76 +184,91 @@ public abstract partial class FileSystem
|
||||
protected FileImplementation File => Parent.File;
|
||||
protected DirectoryImplementation Directory => Parent.Directory;
|
||||
protected PathImplementation Path => this;
|
||||
/// <inheritdoc cref="global::System.IO.Path.Join(global::System.String, global::System.String)"/>
|
||||
public virtual global::System.String Join(global::System.String path1, global::System.String path2)
|
||||
{
|
||||
return global::System.IO.Path.Join(path1, path2);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.Join(global::System.String, global::System.String, global::System.String)"/>
|
||||
public virtual global::System.String Join(global::System.String path1, global::System.String path2, global::System.String path3)
|
||||
{
|
||||
return global::System.IO.Path.Join(path1, path2, path3);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.Join(global::System.String, global::System.String, global::System.String, global::System.String)"/>
|
||||
public virtual global::System.String Join(global::System.String path1, global::System.String path2, global::System.String path3, global::System.String path4)
|
||||
{
|
||||
return global::System.IO.Path.Join(path1, path2, path3, path4);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.Join(params global::System.ReadOnlySpan<global::System.String>)"/>
|
||||
public virtual global::System.String Join(params global::System.ReadOnlySpan<global::System.String> paths)
|
||||
{
|
||||
return global::System.IO.Path.Join(paths);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.GetDirectoryName(global::System.ReadOnlySpan<global::System.Char>)"/>
|
||||
public global::System.ReadOnlySpan<global::System.Char> GetDirectoryName(global::System.ReadOnlySpan<global::System.Char> path)
|
||||
{
|
||||
return global::System.IO.Path.GetDirectoryName(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.GetDirectoryName(global::System.String)"/>
|
||||
public global::System.String GetDirectoryName(global::System.String path)
|
||||
{
|
||||
return global::System.IO.Path.GetDirectoryName(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.GetExtension(global::System.ReadOnlySpan<global::System.Char>)"/>
|
||||
public global::System.ReadOnlySpan<global::System.Char> GetExtension(global::System.ReadOnlySpan<global::System.Char> path)
|
||||
{
|
||||
return global::System.IO.Path.GetExtension(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.GetExtension(global::System.String)"/>
|
||||
public global::System.String GetExtension(global::System.String path)
|
||||
{
|
||||
return global::System.IO.Path.GetExtension(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.GetFileName(global::System.ReadOnlySpan<global::System.Char>)"/>
|
||||
public global::System.ReadOnlySpan<global::System.Char> GetFileName(global::System.ReadOnlySpan<global::System.Char> path)
|
||||
{
|
||||
return global::System.IO.Path.GetFileName(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.GetFileName(global::System.String)"/>
|
||||
public global::System.String GetFileName(global::System.String path)
|
||||
{
|
||||
return global::System.IO.Path.GetFileName(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.GetFileNameWithoutExtension(global::System.ReadOnlySpan<global::System.Char>)"/>
|
||||
public global::System.ReadOnlySpan<global::System.Char> GetFileNameWithoutExtension(global::System.ReadOnlySpan<global::System.Char> path)
|
||||
{
|
||||
return global::System.IO.Path.GetFileNameWithoutExtension(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.GetFileNameWithoutExtension(global::System.String)"/>
|
||||
public global::System.String GetFileNameWithoutExtension(global::System.String path)
|
||||
{
|
||||
return global::System.IO.Path.GetFileNameWithoutExtension(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.GetFullPath(global::System.String)"/>
|
||||
public virtual global::System.String GetFullPath(global::System.String path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.GetRelativePath(global::System.String, global::System.String)"/>
|
||||
public global::System.String GetRelativePath(global::System.String relativeTo, global::System.String path)
|
||||
{
|
||||
return global::System.IO.Path.GetRelativePath(relativeTo, path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="global::System.IO.Path.IsPathRooted(global::System.ReadOnlySpan<global::System.Char>)"/>
|
||||
public virtual global::System.Boolean IsPathRooted(global::System.ReadOnlySpan<global::System.Char> path)
|
||||
{
|
||||
throw new global::System.NotSupportedException();
|
||||
|
||||
@ -481,5 +481,25 @@ public partial class VirtualFileSystem : FileSystem
|
||||
string fullPath = GetFullPath(path);
|
||||
return fullPath.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public override string Join(string path1, string path2)
|
||||
{
|
||||
return GetFullPath(base.Join(path1, path2));
|
||||
}
|
||||
|
||||
public override string Join(string path1, string path2, string path3)
|
||||
{
|
||||
return GetFullPath(base.Join(path1, path2, path3));
|
||||
}
|
||||
|
||||
public override string Join(string path1, string path2, string path3, string path4)
|
||||
{
|
||||
return GetFullPath(base.Join(path1, path2, path3, path4));
|
||||
}
|
||||
|
||||
public override string Join(params ReadOnlySpan<string> paths)
|
||||
{
|
||||
return GetFullPath(base.Join(paths));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ public sealed class GameStructure : IDisposable
|
||||
|
||||
public static GameStructure Load(IEnumerable<string> paths, FileSystem fileSystem, CoreConfiguration configuration)
|
||||
{
|
||||
List<string> toProcess = ZipExtractor.Process(paths);
|
||||
List<string> toProcess = ZipExtractor.Process(paths, fileSystem);
|
||||
if (toProcess.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("Game files not found", nameof(paths));
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
using AssetRipper.Import.Logging;
|
||||
using AssetRipper.IO.Files;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace AssetRipper.Import.Structure;
|
||||
|
||||
public static class ZipExtractor
|
||||
internal static class ZipExtractor
|
||||
{
|
||||
private const string ZipExtension = ".zip";
|
||||
private const string ApkExtension = ".apk";
|
||||
@ -19,24 +20,24 @@ public static class ZipExtractor
|
||||
private const uint ZipEmptyMagic = 0x06054B50;
|
||||
private const uint ZipSpannedMagic = 0x08074B50;
|
||||
|
||||
public static List<string> Process(IEnumerable<string> paths)
|
||||
public static List<string> Process(IEnumerable<string> paths, FileSystem fileSystem)
|
||||
{
|
||||
List<string> result = new();
|
||||
List<string> result = [];
|
||||
foreach (string path in paths)
|
||||
{
|
||||
switch (GetFileExtension(path))
|
||||
switch (GetFileExtension(path, fileSystem))
|
||||
{
|
||||
case ZipExtension:
|
||||
case ApkExtension:
|
||||
case ObbExtension:
|
||||
case VpkExtension:
|
||||
case IpaExtension:
|
||||
result.Add(ExtractZip(path));
|
||||
result.Add(ExtractZip(path, fileSystem));
|
||||
break;
|
||||
case ApksExtension:
|
||||
case ApkPlusExtension:
|
||||
case XapkExtension:
|
||||
result.Add(ExtractXapk(path));
|
||||
result.Add(ExtractXapk(path, fileSystem));
|
||||
break;
|
||||
default:
|
||||
result.Add(path);
|
||||
@ -46,55 +47,101 @@ public static class ZipExtractor
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string ExtractZip(string zipFilePath)
|
||||
private static string ExtractZip(string zipFilePath, FileSystem fileSystem)
|
||||
{
|
||||
if (!HasCompatibleMagic(zipFilePath))
|
||||
if (!HasCompatibleMagic(zipFilePath, fileSystem))
|
||||
{
|
||||
return zipFilePath;
|
||||
}
|
||||
|
||||
string outputDirectory = LocalFileSystem.Instance.Directory.CreateTemporary();
|
||||
DecompressZipArchive(zipFilePath, outputDirectory);
|
||||
string outputDirectory = fileSystem.Directory.CreateTemporary();
|
||||
DecompressZipArchive(zipFilePath, outputDirectory, fileSystem);
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
private static string ExtractXapk(string xapkFilePath)
|
||||
private static string ExtractXapk(string xapkFilePath, FileSystem fileSystem)
|
||||
{
|
||||
if (!HasCompatibleMagic(xapkFilePath))
|
||||
if (!HasCompatibleMagic(xapkFilePath, fileSystem))
|
||||
{
|
||||
return xapkFilePath;
|
||||
}
|
||||
|
||||
string intermediateDirectory = LocalFileSystem.Instance.Directory.CreateTemporary();
|
||||
string outputDirectory = LocalFileSystem.Instance.Directory.CreateTemporary();
|
||||
DecompressZipArchive(xapkFilePath, intermediateDirectory);
|
||||
foreach (string filePath in Directory.GetFiles(intermediateDirectory))
|
||||
string intermediateDirectory = fileSystem.Directory.CreateTemporary();
|
||||
string outputDirectory = fileSystem.Directory.CreateTemporary();
|
||||
DecompressZipArchive(xapkFilePath, intermediateDirectory, fileSystem);
|
||||
foreach (string filePath in fileSystem.Directory.GetFiles(intermediateDirectory))
|
||||
{
|
||||
if (GetFileExtension(filePath) == ApkExtension)
|
||||
if (GetFileExtension(filePath, fileSystem) == ApkExtension)
|
||||
{
|
||||
DecompressZipArchive(filePath, outputDirectory);
|
||||
DecompressZipArchive(filePath, outputDirectory, fileSystem);
|
||||
}
|
||||
}
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
private static void DecompressZipArchive(string zipFilePath, string outputDirectory)
|
||||
private static void DecompressZipArchive(string zipFilePath, string outputDirectory, FileSystem fileSystem)
|
||||
{
|
||||
Logger.Info(LogCategory.Import, $"Decompressing files...{Environment.NewLine}\tFrom: {zipFilePath}{Environment.NewLine}\tTo: {outputDirectory}");
|
||||
using ZipArchive archive = ZipArchive.Open(zipFilePath);
|
||||
using Stream stream = fileSystem.File.OpenRead(zipFilePath);
|
||||
using ZipArchive archive = ZipArchive.Open(stream);
|
||||
using IReader reader = archive.ExtractAllEntries();
|
||||
reader.WriteAllToDirectory(outputDirectory, new SharpCompress.Common.ExtractionOptions()
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
WriteEntryToDirectory(reader, outputDirectory, fileSystem);
|
||||
}
|
||||
}
|
||||
|
||||
private static string? GetFileExtension(string path)
|
||||
private static void WriteEntryToDirectory(IReader reader, string outputDirectory, FileSystem fileSystem)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
IEntry entry = reader.Entry;
|
||||
string filePath;
|
||||
string fullOutputDirectory = fileSystem.Path.GetFullPath(outputDirectory);
|
||||
|
||||
if (!fileSystem.Directory.Exists(fullOutputDirectory))
|
||||
{
|
||||
return Path.GetExtension(path);
|
||||
throw new ExtractionException($"Directory does not exist to extract to: {fullOutputDirectory}");
|
||||
}
|
||||
|
||||
string fileName = fileSystem.Path.GetFileName(entry.Key ?? throw new NullReferenceException("Entry Key is null")) ?? throw new NullReferenceException("File is null");
|
||||
fileName = FileSystem.FixInvalidFileNameCharacters(fileName);
|
||||
|
||||
string directory = fileSystem.Path.GetDirectoryName(entry.Key ?? throw new NullReferenceException("Entry Key is null")) ?? throw new NullReferenceException("Directory is null");
|
||||
string fullDirectory = fileSystem.Path.GetFullPath(fileSystem.Path.Join(fullOutputDirectory, directory));
|
||||
|
||||
if (!fileSystem.Directory.Exists(fullDirectory))
|
||||
{
|
||||
if (!fullDirectory.StartsWith(fullOutputDirectory, StringComparison.Ordinal))
|
||||
{
|
||||
throw new ExtractionException("Entry is trying to create a directory outside of the destination directory.");
|
||||
}
|
||||
|
||||
fileSystem.Directory.Create(fullDirectory);
|
||||
}
|
||||
filePath = fileSystem.Path.Join(fullDirectory, fileName);
|
||||
|
||||
if (!entry.IsDirectory)
|
||||
{
|
||||
filePath = fileSystem.Path.GetFullPath(filePath);
|
||||
|
||||
if (!filePath.StartsWith(fullOutputDirectory,StringComparison.Ordinal))
|
||||
{
|
||||
throw new ExtractionException("Entry is trying to write a file outside of the destination directory.");
|
||||
}
|
||||
|
||||
using Stream stream = fileSystem.File.Create(filePath);
|
||||
reader.WriteEntryTo(stream);
|
||||
}
|
||||
else if (!fileSystem.Directory.Exists(filePath))
|
||||
{
|
||||
fileSystem.Directory.Create(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
private static string? GetFileExtension(string path, FileSystem fileSystem)
|
||||
{
|
||||
if (fileSystem.File.Exists(path))
|
||||
{
|
||||
return fileSystem.Path.GetExtension(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -102,14 +149,15 @@ public static class ZipExtractor
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasCompatibleMagic(string path)
|
||||
private static bool HasCompatibleMagic(string path, FileSystem fileSystem)
|
||||
{
|
||||
uint magic = GetMagicNumber(path);
|
||||
uint magic = GetMagicNumber(path, fileSystem);
|
||||
return magic == ZipNormalMagic || magic == ZipEmptyMagic || magic == ZipSpannedMagic;
|
||||
}
|
||||
|
||||
private static uint GetMagicNumber(string path)
|
||||
private static uint GetMagicNumber(string path, FileSystem fileSystem)
|
||||
{
|
||||
return new BinaryReader(File.OpenRead(path)).ReadUInt32();
|
||||
using Stream stream = fileSystem.File.OpenRead(path);
|
||||
return new BinaryReader(stream).ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user