From d416ad03181fc405c1eb7d85d2025d800a2fffd8 Mon Sep 17 00:00:00 2001 From: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> Date: Tue, 5 Sep 2023 23:19:30 -0400 Subject: [PATCH] Astc tests --- ...Ripper.TextureDecoder.TestGenerator.csproj | 1 + .../Program.cs | 3 +- .../TextureDataClassGenerator.cs | 129 ++++ .../TextureMetadata.cs | 27 + .../TextureMetadataSerializerContext.cs | 9 + .../Usings.cs | 1 + .../AndroidTextures.g.cs | 720 ++++++++++++++++++ AssetRipper.TextureDecoder.Tests/AstcTests.cs | 77 ++ AssetRipper.TextureDecoder.Tests/BcTests.cs | 40 +- .../ByteArrayDeviation.cs | 40 + .../ITexture.g.cs | 17 + .../TextureFormat.g.cs | 30 + .../TextureType.g.cs | 8 + 13 files changed, 1063 insertions(+), 39 deletions(-) create mode 100644 AssetRipper.TextureDecoder.TestGenerator/TextureDataClassGenerator.cs create mode 100644 AssetRipper.TextureDecoder.TestGenerator/TextureMetadata.cs create mode 100644 AssetRipper.TextureDecoder.TestGenerator/TextureMetadataSerializerContext.cs create mode 100644 AssetRipper.TextureDecoder.TestGenerator/Usings.cs create mode 100644 AssetRipper.TextureDecoder.Tests/AndroidTextures.g.cs create mode 100644 AssetRipper.TextureDecoder.Tests/AstcTests.cs create mode 100644 AssetRipper.TextureDecoder.Tests/ByteArrayDeviation.cs create mode 100644 AssetRipper.TextureDecoder.Tests/ITexture.g.cs create mode 100644 AssetRipper.TextureDecoder.Tests/TextureFormat.g.cs create mode 100644 AssetRipper.TextureDecoder.Tests/TextureType.g.cs diff --git a/AssetRipper.TextureDecoder.TestGenerator/AssetRipper.TextureDecoder.TestGenerator.csproj b/AssetRipper.TextureDecoder.TestGenerator/AssetRipper.TextureDecoder.TestGenerator.csproj index 35997c7..7b51730 100644 --- a/AssetRipper.TextureDecoder.TestGenerator/AssetRipper.TextureDecoder.TestGenerator.csproj +++ b/AssetRipper.TextureDecoder.TestGenerator/AssetRipper.TextureDecoder.TestGenerator.csproj @@ -8,6 +8,7 @@ + diff --git a/AssetRipper.TextureDecoder.TestGenerator/Program.cs b/AssetRipper.TextureDecoder.TestGenerator/Program.cs index 834f2e9..4caf5d9 100644 --- a/AssetRipper.TextureDecoder.TestGenerator/Program.cs +++ b/AssetRipper.TextureDecoder.TestGenerator/Program.cs @@ -22,6 +22,7 @@ namespace AssetRipper.TextureDecoder.TestGenerator { TestClassGenerator.GenerateTestClasses(); GenerateRgbConsoleMethod(); + TextureDataClassGenerator.Generate(); Console.WriteLine("Done!"); } @@ -92,4 +93,4 @@ namespace AssetRipper.TextureDecoder.TestGenerator } } } -} \ No newline at end of file +} diff --git a/AssetRipper.TextureDecoder.TestGenerator/TextureDataClassGenerator.cs b/AssetRipper.TextureDecoder.TestGenerator/TextureDataClassGenerator.cs new file mode 100644 index 0000000..68bf635 --- /dev/null +++ b/AssetRipper.TextureDecoder.TestGenerator/TextureDataClassGenerator.cs @@ -0,0 +1,129 @@ +using AssetRipper.Text.SourceGeneration; +using System.CodeDom.Compiler; + +namespace AssetRipper.TextureDecoder.TestGenerator; + +internal static class TextureDataClassGenerator +{ + private const string TestFilesFolder = $"{nameof(TestFileFolders)}.{nameof(TestFileFolders.AndroidTestFiles)}"; + private const string TextureType = "TextureType"; + private const string TextureFormat = "TextureFormat"; + private const string ITexture = "ITexture"; + private const string Namespace = "AssetRipper.TextureDecoder.Tests"; + + public static void Generate() + { + IReadOnlyList metadataList = LoadMetadata(TestFileFolders.AndroidTestFiles); + GenerateTextureType(metadataList); + GenerateTextureFormat(metadataList); + GenerateITexture(); + GenerateAndroidTextures(metadataList); + } + + private static void GenerateTextureType(IReadOnlyList metadataList) + { + GenerateEnum(TextureType, metadataList.Select(m => m.Type).Distinct().Order()); + } + + private static void GenerateTextureFormat(IReadOnlyList metadataList) + { + GenerateEnum(TextureFormat, metadataList.Select(m => m.Format).Distinct().Order()); + } + + private static void GenerateEnum(string name, IEnumerable values) + { + using IndentedTextWriter writer = IndentedTextWriterFactory.Create(ProjectFolders.TestProject, name); + writer.WriteGeneratedCodeWarning(); + writer.WriteLine(); + writer.WriteFileScopedNamespace(Namespace); + writer.WriteLine(); + writer.WriteLine($"public enum {name}"); + using (new CurlyBrackets(writer)) + { + foreach (string? value in values) + { + writer.WriteLine($"{value},"); + } + } + } + + private static void GenerateITexture() + { + using IndentedTextWriter writer = IndentedTextWriterFactory.Create(ProjectFolders.TestProject, ITexture); + writer.WriteGeneratedCodeWarning(); + writer.WriteLine(); + writer.WriteFileScopedNamespace(Namespace); + writer.WriteLine(); + writer.WriteLine($"public partial interface {ITexture}"); + using (new CurlyBrackets(writer)) + { + writer.WriteLine($"static abstract {TextureType} {nameof(TextureMetadata.Type)} {{ get; }}"); + writer.WriteLine($"static abstract {TextureFormat} {nameof(TextureMetadata.Format)} {{ get; }}"); + writer.WriteLine($"static abstract int {nameof(TextureMetadata.FileSize)} {{ get; }}"); + writer.WriteLine($"static abstract int {nameof(TextureMetadata.ImageSize)} {{ get; }}"); + writer.WriteLine($"static abstract int {nameof(TextureMetadata.ImageCount)} {{ get; }}"); + writer.WriteLine($"static abstract bool {nameof(TextureMetadata.Mips)} {{ get; }}"); + writer.WriteLine($"static abstract int {nameof(TextureMetadata.Width)} {{ get; }}"); + writer.WriteLine($"static abstract int {nameof(TextureMetadata.Height)} {{ get; }}"); + writer.WriteLine("static abstract string Path { get; }"); + writer.WriteLine("static abstract byte[] Data { get; }"); + } + } + + private static IReadOnlyList LoadMetadata(string directory) + { + string[] jsonFiles = Directory.GetFiles(directory, "*.json"); + List metadataList = new(jsonFiles.Length); + foreach (string jsonFile in jsonFiles) + { + metadataList.Add(TextureMetadata.FromFile(jsonFile)); + } + return metadataList; + } + + private static void GenerateAndroidTextures(IReadOnlyList metadataList) + { + const string AndroidTextures = "AndroidTextures"; + using IndentedTextWriter writer = IndentedTextWriterFactory.Create(ProjectFolders.TestProject, AndroidTextures); + writer.WriteGeneratedCodeWarning(); + writer.WriteLine(); + writer.WriteFileScopedNamespace(Namespace); + writer.WriteLine(); + writer.WriteLine($"public static partial class {AndroidTextures}"); + using (new CurlyBrackets(writer)) + { + foreach (TextureMetadata metadata in metadataList) + { + string withOrWithout = metadata.Mips ? "with" : "without"; + writer.WriteSummaryDocumentation($"{metadata.Format} {metadata.Width}x{metadata.Height} {withOrWithout} mips"); + writer.WriteLine($"public sealed class {metadata.Name} : {ITexture}"); + using (new CurlyBrackets(writer)) + { + writer.WriteLine($"public const {TextureType} {nameof(TextureMetadata.Type)} = {TextureType}.{metadata.Type};"); + writer.WriteLine($"public const {TextureFormat} {nameof(TextureMetadata.Format)} = {TextureFormat}.{metadata.Format};"); + writer.WriteLine($"public const int {nameof(TextureMetadata.FileSize)} = {metadata.FileSize};"); + writer.WriteLine($"public const int {nameof(TextureMetadata.ImageSize)} = {metadata.ImageSize};"); + writer.WriteLine($"public const int {nameof(TextureMetadata.ImageCount)} = {metadata.ImageCount};"); + writer.WriteLine($"public const bool {nameof(TextureMetadata.Mips)} = {metadata.Mips.ToString().ToLowerInvariant()};"); + writer.WriteLine($"public const int {nameof(TextureMetadata.Width)} = {metadata.Width};"); + writer.WriteLine($"public const int {nameof(TextureMetadata.Height)} = {metadata.Height};"); + writer.WriteLine($"public const string Path = {TestFilesFolder} + \"{metadata.Name}\";"); + writer.WriteSummaryDocumentation($"Length: {metadata.FileSize}"); + writer.WriteLine("public static byte[] Data => File.ReadAllBytes(Path);"); + writer.WriteLine(); + writer.WriteLine($"private {metadata.Name}() {{ }}"); + writer.WriteLine($"static {TextureType} {ITexture}.{nameof(TextureMetadata.Type)} => {nameof(TextureMetadata.Type)};"); + writer.WriteLine($"static {TextureFormat} {ITexture}.{nameof(TextureMetadata.Format)} => {nameof(TextureMetadata.Format)};"); + writer.WriteLine($"static int {ITexture}.{nameof(TextureMetadata.FileSize)} => {nameof(TextureMetadata.FileSize)};"); + writer.WriteLine($"static int {ITexture}.{nameof(TextureMetadata.ImageSize)} => {nameof(TextureMetadata.ImageSize)};"); + writer.WriteLine($"static int {ITexture}.{nameof(TextureMetadata.ImageCount)} => {nameof(TextureMetadata.ImageCount)};"); + writer.WriteLine($"static bool {ITexture}.{nameof(TextureMetadata.Mips)} => {nameof(TextureMetadata.Mips)};"); + writer.WriteLine($"static int {ITexture}.{nameof(TextureMetadata.Width)} => {nameof(TextureMetadata.Width)};"); + writer.WriteLine($"static int {ITexture}.{nameof(TextureMetadata.Height)} => {nameof(TextureMetadata.Height)};"); + writer.WriteLine($"static string {ITexture}.Path => Path;"); + } + writer.WriteLine(); + } + } + } +} diff --git a/AssetRipper.TextureDecoder.TestGenerator/TextureMetadata.cs b/AssetRipper.TextureDecoder.TestGenerator/TextureMetadata.cs new file mode 100644 index 0000000..798a883 --- /dev/null +++ b/AssetRipper.TextureDecoder.TestGenerator/TextureMetadata.cs @@ -0,0 +1,27 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace AssetRipper.TextureDecoder.TestGenerator; + +public sealed class TextureMetadata +{ + [JsonIgnore] + public string? Name { get; set; } + public string? Type { get; set; } + public string? Format { get; set; } + public int FileSize { get; set; } + public int ImageSize { get; set; } + public int ImageCount { get; set; } + public bool Mips { get; set; } + public int Width { get; set; } + public int Height { get; set; } + + public static TextureMetadata FromFile(string path) + { + using FileStream stream = File.OpenRead(path); + TextureMetadata result = JsonSerializer.Deserialize(stream, TextureMetadataSerializerContext.Default.TextureMetadata) + ?? throw new NullReferenceException(nameof(TextureMetadata)); + result.Name = Path.GetFileNameWithoutExtension(path); + return result; + } +} diff --git a/AssetRipper.TextureDecoder.TestGenerator/TextureMetadataSerializerContext.cs b/AssetRipper.TextureDecoder.TestGenerator/TextureMetadataSerializerContext.cs new file mode 100644 index 0000000..bc2dd0d --- /dev/null +++ b/AssetRipper.TextureDecoder.TestGenerator/TextureMetadataSerializerContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace AssetRipper.TextureDecoder.TestGenerator; + +[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] +[JsonSerializable(typeof(TextureMetadata))] +internal sealed partial class TextureMetadataSerializerContext : JsonSerializerContext +{ +} diff --git a/AssetRipper.TextureDecoder.TestGenerator/Usings.cs b/AssetRipper.TextureDecoder.TestGenerator/Usings.cs new file mode 100644 index 0000000..42e6a5a --- /dev/null +++ b/AssetRipper.TextureDecoder.TestGenerator/Usings.cs @@ -0,0 +1 @@ +global using AssetRipper.TextureDecoder.Paths; diff --git a/AssetRipper.TextureDecoder.Tests/AndroidTextures.g.cs b/AssetRipper.TextureDecoder.Tests/AndroidTextures.g.cs new file mode 100644 index 0000000..f3c0ce1 --- /dev/null +++ b/AssetRipper.TextureDecoder.Tests/AndroidTextures.g.cs @@ -0,0 +1,720 @@ +// Auto-generated code. Do not modify manually. + +namespace AssetRipper.TextureDecoder.Tests; + +public static partial class AndroidTextures +{ + /// + /// BGRA32_14 512x512 without mips + /// + public sealed class Logo_00 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.BGRA32_14; + public const int FileSize = 1048576; + public const int ImageSize = 1048576; + public const int ImageCount = 1; + public const bool Mips = false; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_00"; + /// + /// Length: 1048576 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_00() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_12x12 512x512 with mips + /// + public sealed class Logo_01 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_12x12; + public const int FileSize = 40112; + public const int ImageSize = 40112; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_01"; + /// + /// Length: 40112 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_01() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_RGB_10x10 512x512 with mips + /// + public sealed class Logo_02 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_RGB_10x10; + public const int FileSize = 57952; + public const int ImageSize = 57952; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_02"; + /// + /// Length: 57952 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_02() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_RGB_8x8 512x512 with mips + /// + public sealed class Logo_03 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_RGB_8x8; + public const int FileSize = 87424; + public const int ImageSize = 87424; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_03"; + /// + /// Length: 87424 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_03() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_6x6 512x512 with mips + /// + public sealed class Logo_04 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_6x6; + public const int FileSize = 158432; + public const int ImageSize = 158432; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_04"; + /// + /// Length: 158432 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_04() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_5x5 512x512 with mips + /// + public sealed class Logo_05 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_5x5; + public const int FileSize = 227680; + public const int ImageSize = 227680; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_05"; + /// + /// Length: 227680 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_05() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_RGB_4x4 512x512 with mips + /// + public sealed class Logo_06 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_RGB_4x4; + public const int FileSize = 349552; + public const int ImageSize = 349552; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_06"; + /// + /// Length: 349552 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_06() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ETC2_RGBA8 512x512 with mips + /// + public sealed class Logo_07 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ETC2_RGBA8; + public const int FileSize = 349552; + public const int ImageSize = 349552; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_07"; + /// + /// Length: 349552 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_07() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ETC2_RGBA1 512x512 with mips + /// + public sealed class Logo_08 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ETC2_RGBA1; + public const int FileSize = 174776; + public const int ImageSize = 174776; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_08"; + /// + /// Length: 174776 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_08() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// PVRTC_4BPP_RGBA 512x512 with mips + /// + public sealed class Logo_09 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.PVRTC_4BPP_RGBA; + public const int FileSize = 174848; + public const int ImageSize = 174848; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_09"; + /// + /// Length: 174848 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_09() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// PVRTC_RGBA2 512x512 with mips + /// + public sealed class Logo_10 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.PVRTC_RGBA2; + public const int FileSize = 87488; + public const int ImageSize = 87488; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_10"; + /// + /// Length: 87488 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_10() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ETC2_RGB 512x512 with mips + /// + public sealed class Logo_11 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ETC2_RGB; + public const int FileSize = 174776; + public const int ImageSize = 174776; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_11"; + /// + /// Length: 174776 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_11() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ETC_RGB4 512x512 with mips + /// + public sealed class Logo_12 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ETC_RGB4; + public const int FileSize = 174776; + public const int ImageSize = 174776; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_12"; + /// + /// Length: 174776 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_12() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// PVRTC_4BPP_RGB 512x512 with mips + /// + public sealed class Logo_13 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.PVRTC_4BPP_RGB; + public const int FileSize = 174848; + public const int ImageSize = 174848; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_13"; + /// + /// Length: 174848 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_13() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// PVRTC_2BPP_RGB 512x512 with mips + /// + public sealed class Logo_14 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.PVRTC_2BPP_RGB; + public const int FileSize = 87488; + public const int ImageSize = 87488; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_14"; + /// + /// Length: 87488 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_14() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// EAC_RG 512x512 with mips + /// + public sealed class Logo_15 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.EAC_RG; + public const int FileSize = 349552; + public const int ImageSize = 349552; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_15"; + /// + /// Length: 349552 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_15() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// EAC_R 512x512 with mips + /// + public sealed class Logo_16 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.EAC_R; + public const int FileSize = 174776; + public const int ImageSize = 174776; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_16"; + /// + /// Length: 174776 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_16() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_HDR_12x12 512x512 with mips + /// + public sealed class Logo_17 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_HDR_12x12; + public const int FileSize = 40112; + public const int ImageSize = 40112; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_17"; + /// + /// Length: 40112 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_17() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_HDR_10x10 512x512 with mips + /// + public sealed class Logo_18 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_HDR_10x10; + public const int FileSize = 57952; + public const int ImageSize = 57952; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_18"; + /// + /// Length: 57952 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_18() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_HDR_8x8 512x512 with mips + /// + public sealed class Logo_19 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_HDR_8x8; + public const int FileSize = 87424; + public const int ImageSize = 87424; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_19"; + /// + /// Length: 87424 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_19() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_HDR_6x6 512x512 with mips + /// + public sealed class Logo_20 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_HDR_6x6; + public const int FileSize = 158432; + public const int ImageSize = 158432; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_20"; + /// + /// Length: 158432 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_20() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_HDR_5x5 512x512 with mips + /// + public sealed class Logo_21 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_HDR_5x5; + public const int FileSize = 227680; + public const int ImageSize = 227680; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_21"; + /// + /// Length: 227680 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_21() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + + /// + /// ASTC_HDR_4x4 512x512 with mips + /// + public sealed class Logo_22 : ITexture + { + public const TextureType Type = TextureType.Texture2D; + public const TextureFormat Format = TextureFormat.ASTC_HDR_4x4; + public const int FileSize = 349552; + public const int ImageSize = 349552; + public const int ImageCount = 1; + public const bool Mips = true; + public const int Width = 512; + public const int Height = 512; + public const string Path = TestFileFolders.AndroidTestFiles + "Logo_22"; + /// + /// Length: 349552 + /// + public static byte[] Data => File.ReadAllBytes(Path); + + private Logo_22() { } + static TextureType ITexture.Type => Type; + static TextureFormat ITexture.Format => Format; + static int ITexture.FileSize => FileSize; + static int ITexture.ImageSize => ImageSize; + static int ITexture.ImageCount => ImageCount; + static bool ITexture.Mips => Mips; + static int ITexture.Width => Width; + static int ITexture.Height => Height; + static string ITexture.Path => Path; + } + +} diff --git a/AssetRipper.TextureDecoder.Tests/AstcTests.cs b/AssetRipper.TextureDecoder.Tests/AstcTests.cs new file mode 100644 index 0000000..9fc10d3 --- /dev/null +++ b/AssetRipper.TextureDecoder.Tests/AstcTests.cs @@ -0,0 +1,77 @@ +using AssetRipper.TextureDecoder.Astc; + +namespace AssetRipper.TextureDecoder.Tests; + +public sealed class AstcTests +{ + private const double MaxMeanDeviation4x4 = 0.2; + private const double MaxStandardDeviation4x4 = 1; + + private const double MaxMeanDeviation5x5 = 0.2; + private const double MaxStandardDeviation5x5 = 1; + + private const double MaxMeanDeviation6x6 = 0.2; + private const double MaxStandardDeviation6x6 = 1; + + private const double MaxMeanDeviation8x8 = 0.2; + private const double MaxStandardDeviation8x8 = 2; + + private const double MaxMeanDeviation10x10 = 0.2; + private const double MaxStandardDeviation10x10 = 2.5; + + private const double MaxMeanDeviation12x12 = 0.3; + private const double MaxStandardDeviation12x12 = 3; + + [Test] + public void DecompressAstc4x4() => AssertCorrectDecompression(4, 4, MaxMeanDeviation4x4, MaxStandardDeviation4x4); + + [Test] + public void DecompressAstc5x5() => AssertCorrectDecompression(5, 5, MaxMeanDeviation5x5, MaxStandardDeviation5x5); + + [Test] + public void DecompressAstc6x6() => AssertCorrectDecompression(6, 6, MaxMeanDeviation6x6, MaxStandardDeviation6x6); + + [Test] + public void DecompressAstc8x8() => AssertCorrectDecompression(8, 8, MaxMeanDeviation8x8, MaxStandardDeviation8x8); + + [Test] + public void DecompressAstc10x10() => AssertCorrectDecompression(10, 10, MaxMeanDeviation10x10, MaxStandardDeviation10x10); + + [Test] + public void DecompressAstc12x12() => AssertCorrectDecompression(12, 12, MaxMeanDeviation12x12, MaxStandardDeviation12x12); + + [Test] + [Ignore("High dynamic range textures are not supported yet.")] + public void DecompressAstc4x4HDR() => AssertCorrectDecompression(4, 4, MaxMeanDeviation4x4, MaxStandardDeviation4x4); + + [Test] + [Ignore("High dynamic range textures are not supported yet.")] + public void DecompressAstc5x5HDR() => AssertCorrectDecompression(5, 5, MaxMeanDeviation5x5, MaxStandardDeviation5x5); + + [Test] + [Ignore("High dynamic range textures are not supported yet.")] + public void DecompressAstc6x6HDR() => AssertCorrectDecompression(6, 6, MaxMeanDeviation6x6, MaxStandardDeviation6x6); + + [Test] + [Ignore("High dynamic range textures are not supported yet.")] + public void DecompressAstc8x8HDR() => AssertCorrectDecompression(8, 8, MaxMeanDeviation8x8, MaxStandardDeviation8x8); + + [Test] + [Ignore("High dynamic range textures are not supported yet.")] + public void DecompressAstc10x10HDR() => AssertCorrectDecompression(10, 10, MaxMeanDeviation10x10, MaxStandardDeviation10x10); + + [Test] + [Ignore("High dynamic range textures are not supported yet.")] + public void DecompressAstc12x12HDR() => AssertCorrectDecompression(4, 4, MaxMeanDeviation12x12, MaxStandardDeviation12x12); + + private static void AssertCorrectDecompression(int blockWidth, int blockHeight, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture + { + ReadOnlySpan data = T.Data; + int bytesRead = AstcDecoder.DecodeASTC(data, T.Width, T.Height, blockWidth, blockHeight, out byte[] decompressedData); + if (!T.Mips) + { + Assert.That(bytesRead, Is.EqualTo(data.Length)); + } + ByteArrayDeviation.AssertMinimalDeviation(decompressedData, AndroidTextures.Logo_00.Data, maxMeanDeviation, maxStandardDeviation); + } +} diff --git a/AssetRipper.TextureDecoder.Tests/BcTests.cs b/AssetRipper.TextureDecoder.Tests/BcTests.cs index 66935b2..402407a 100644 --- a/AssetRipper.TextureDecoder.Tests/BcTests.cs +++ b/AssetRipper.TextureDecoder.Tests/BcTests.cs @@ -120,7 +120,7 @@ namespace AssetRipper.TextureDecoder.Tests ReadOnlySpan data = File.ReadAllBytes(path); int bytesRead = Bc6h.Decompress(data, width, height, isSigned, out byte[] decodedData); Assert.That(bytesRead, Is.EqualTo(data.Length)); - AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc6h, MaxStandardDeviationBc6h); + ByteArrayDeviation.AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc6h, MaxStandardDeviationBc6h); } private static void AssertCorrectBC7Decompression(string path, int width, int height) @@ -128,7 +128,7 @@ namespace AssetRipper.TextureDecoder.Tests ReadOnlySpan data = File.ReadAllBytes(path); int bytesRead = Bc7.Decompress(data, width, height, out byte[] decodedData); Assert.That(bytesRead, Is.EqualTo(data.Length)); - AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc7, MaxStandardDeviationBc7); + ByteArrayDeviation.AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc7, MaxStandardDeviationBc7); } private static void AssertAlmostEqual(byte[] array1, byte[] array2, int maxDifference = 2) @@ -149,41 +149,5 @@ namespace AssetRipper.TextureDecoder.Tests } } } - - private static void AssertMinimalDeviation(byte[] decoded, byte[] original, double maxMeanDeviation, double maxStandardDeviation) - { - if (decoded.Length != original.Length) - { - Assert.Fail($"Differing array lengths\nDecoded length {decoded.Length}\nOriginal length {original.Length}"); - return; - } - - long differenceSum = 0; - - for (int i = 0; i < decoded.Length; i++) - { - differenceSum += decoded[i] - original[i]; - } - - double mean = differenceSum / (double)decoded.Length; - - double sumOfSquaredDeviations = 0; - - for (int i = 0; i < decoded.Length; i++) - { - double deviation = decoded[i] - original[i] - mean; - sumOfSquaredDeviations += deviation * deviation; - } - - double standardDeviation = Math.Sqrt(sumOfSquaredDeviations / (decoded.Length - 1)); - //Not sure if Bessel's correction is needed here, but it doesn't hurt, especially since length is around 1 million in the current use. - - Assert.Multiple(() => - { - Assert.That(mean, Is.LessThan(maxMeanDeviation), "Mean too far positive"); - Assert.That(mean, Is.GreaterThan(-maxMeanDeviation), "Mean too far negative"); - Assert.That(standardDeviation, Is.LessThan(maxStandardDeviation), "Standard deviation too large"); - }); - } } } diff --git a/AssetRipper.TextureDecoder.Tests/ByteArrayDeviation.cs b/AssetRipper.TextureDecoder.Tests/ByteArrayDeviation.cs new file mode 100644 index 0000000..f132b4d --- /dev/null +++ b/AssetRipper.TextureDecoder.Tests/ByteArrayDeviation.cs @@ -0,0 +1,40 @@ +namespace AssetRipper.TextureDecoder.Tests; + +internal static class ByteArrayDeviation +{ + internal static void AssertMinimalDeviation(byte[] decoded, byte[] original, double maxMeanDeviation, double maxStandardDeviation) + { + if (decoded.Length != original.Length) + { + Assert.Fail($"Differing array lengths\nDecoded length {decoded.Length}\nOriginal length {original.Length}"); + return; + } + + long differenceSum = 0; + + for (int i = 0; i < decoded.Length; i++) + { + differenceSum += decoded[i] - original[i]; + } + + double mean = differenceSum / (double)decoded.Length; + + double sumOfSquaredDeviations = 0; + + for (int i = 0; i < decoded.Length; i++) + { + double deviation = decoded[i] - original[i] - mean; + sumOfSquaredDeviations += deviation * deviation; + } + + double standardDeviation = Math.Sqrt(sumOfSquaredDeviations / (decoded.Length - 1)); + //Not sure if Bessel's correction is needed here, but it doesn't hurt, especially since length is around 1 million in the current use. + + Assert.Multiple(() => + { + Assert.That(mean, Is.LessThan(maxMeanDeviation), "Mean too far positive"); + Assert.That(mean, Is.GreaterThan(-maxMeanDeviation), "Mean too far negative"); + Assert.That(standardDeviation, Is.LessThan(maxStandardDeviation), "Standard deviation too large"); + }); + } +} diff --git a/AssetRipper.TextureDecoder.Tests/ITexture.g.cs b/AssetRipper.TextureDecoder.Tests/ITexture.g.cs new file mode 100644 index 0000000..193355b --- /dev/null +++ b/AssetRipper.TextureDecoder.Tests/ITexture.g.cs @@ -0,0 +1,17 @@ +// Auto-generated code. Do not modify manually. + +namespace AssetRipper.TextureDecoder.Tests; + +public partial interface ITexture +{ + static abstract TextureType Type { get; } + static abstract TextureFormat Format { get; } + static abstract int FileSize { get; } + static abstract int ImageSize { get; } + static abstract int ImageCount { get; } + static abstract bool Mips { get; } + static abstract int Width { get; } + static abstract int Height { get; } + static abstract string Path { get; } + static abstract byte[] Data { get; } +} diff --git a/AssetRipper.TextureDecoder.Tests/TextureFormat.g.cs b/AssetRipper.TextureDecoder.Tests/TextureFormat.g.cs new file mode 100644 index 0000000..f701d88 --- /dev/null +++ b/AssetRipper.TextureDecoder.Tests/TextureFormat.g.cs @@ -0,0 +1,30 @@ +// Auto-generated code. Do not modify manually. + +namespace AssetRipper.TextureDecoder.Tests; + +public enum TextureFormat +{ + ASTC_12x12, + ASTC_5x5, + ASTC_6x6, + ASTC_HDR_10x10, + ASTC_HDR_12x12, + ASTC_HDR_4x4, + ASTC_HDR_5x5, + ASTC_HDR_6x6, + ASTC_HDR_8x8, + ASTC_RGB_10x10, + ASTC_RGB_4x4, + ASTC_RGB_8x8, + BGRA32_14, + EAC_R, + EAC_RG, + ETC_RGB4, + ETC2_RGB, + ETC2_RGBA1, + ETC2_RGBA8, + PVRTC_2BPP_RGB, + PVRTC_4BPP_RGB, + PVRTC_4BPP_RGBA, + PVRTC_RGBA2, +} diff --git a/AssetRipper.TextureDecoder.Tests/TextureType.g.cs b/AssetRipper.TextureDecoder.Tests/TextureType.g.cs new file mode 100644 index 0000000..74a0f33 --- /dev/null +++ b/AssetRipper.TextureDecoder.Tests/TextureType.g.cs @@ -0,0 +1,8 @@ +// Auto-generated code. Do not modify manually. + +namespace AssetRipper.TextureDecoder.Tests; + +public enum TextureType +{ + Texture2D, +}