From 4b4fec9236a3fa37433183c05b973fe70f5770a4 Mon Sep 17 00:00:00 2001
From: Benedikt Werner <1benediktwerner@gmail.com>
Date: Tue, 10 Nov 2020 09:01:41 +0100
Subject: [PATCH 1/3] Add support for Texture2DArray
---
AssetStudio/AssetStudio.csproj | 3 +
AssetStudio/AssetsManager.cs | 3 +
AssetStudio/Classes/GLTextureSettings.cs | 29 +++++
AssetStudio/Classes/StreamingInfo.cs | 25 ++++
AssetStudio/Classes/Texture.cs | 67 +++++++++++
AssetStudio/Classes/Texture2D.cs | 121 +------------------
AssetStudio/Classes/Texture2DArray.cs | 46 +++++++
AssetStudioGUI/AssetStudioGUIForm.cs | 140 ++++++++++++++--------
AssetStudioGUI/Exporter.cs | 106 ++++++++++------
AssetStudioGUI/Studio.cs | 6 +
AssetStudioUtility/Texture2DConverter.cs | 14 +++
AssetStudioUtility/Texture2DExtensions.cs | 6 +
12 files changed, 362 insertions(+), 204 deletions(-)
create mode 100644 AssetStudio/Classes/GLTextureSettings.cs
create mode 100644 AssetStudio/Classes/StreamingInfo.cs
create mode 100644 AssetStudio/Classes/Texture2DArray.cs
diff --git a/AssetStudio/AssetStudio.csproj b/AssetStudio/AssetStudio.csproj
index a457361b3..5703d3eb1 100644
--- a/AssetStudio/AssetStudio.csproj
+++ b/AssetStudio/AssetStudio.csproj
@@ -90,6 +90,7 @@
+
@@ -109,9 +110,11 @@
+
+
diff --git a/AssetStudio/AssetsManager.cs b/AssetStudio/AssetsManager.cs
index 762d3dcba..0f46ffe70 100644
--- a/AssetStudio/AssetsManager.cs
+++ b/AssetStudio/AssetsManager.cs
@@ -327,6 +327,9 @@ private void ReadAssets()
case ClassIDType.Texture2D:
obj = new Texture2D(objectReader);
break;
+ case ClassIDType.Texture2DArray:
+ obj = new Texture2DArray(objectReader);
+ break;
case ClassIDType.Transform:
obj = new Transform(objectReader);
break;
diff --git a/AssetStudio/Classes/GLTextureSettings.cs b/AssetStudio/Classes/GLTextureSettings.cs
new file mode 100644
index 000000000..d0c812078
--- /dev/null
+++ b/AssetStudio/Classes/GLTextureSettings.cs
@@ -0,0 +1,29 @@
+namespace AssetStudio
+{
+ public class GLTextureSettings
+ {
+ public int m_FilterMode;
+ public int m_Aniso;
+ public float m_MipBias;
+ public int m_WrapMode;
+
+ public GLTextureSettings(ObjectReader reader)
+ {
+ var version = reader.version;
+
+ m_FilterMode = reader.ReadInt32();
+ m_Aniso = reader.ReadInt32();
+ m_MipBias = reader.ReadSingle();
+ if (version[0] >= 2017)//2017.x and up
+ {
+ m_WrapMode = reader.ReadInt32(); //m_WrapU
+ int m_WrapV = reader.ReadInt32();
+ int m_WrapW = reader.ReadInt32();
+ }
+ else
+ {
+ m_WrapMode = reader.ReadInt32();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/AssetStudio/Classes/StreamingInfo.cs b/AssetStudio/Classes/StreamingInfo.cs
new file mode 100644
index 000000000..971b23dbf
--- /dev/null
+++ b/AssetStudio/Classes/StreamingInfo.cs
@@ -0,0 +1,25 @@
+namespace AssetStudio
+{
+ public class StreamingInfo
+ {
+ public ulong offset;
+ public uint size;
+ public string path;
+
+ public StreamingInfo(ObjectReader reader)
+ {
+ var version = reader.version;
+
+ if (version[0] >= 2020) //2020.1 and up
+ {
+ offset = reader.ReadUInt64();
+ }
+ else
+ {
+ offset = reader.ReadUInt32();
+ }
+ size = reader.ReadUInt32();
+ path = reader.ReadAlignedString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/AssetStudio/Classes/Texture.cs b/AssetStudio/Classes/Texture.cs
index bcd219690..b1a1ac14d 100644
--- a/AssetStudio/Classes/Texture.cs
+++ b/AssetStudio/Classes/Texture.cs
@@ -17,4 +17,71 @@ protected Texture(ObjectReader reader) : base(reader)
}
}
}
+
+ public enum TextureFormat
+ {
+ Alpha8 = 1,
+ ARGB4444,
+ RGB24,
+ RGBA32,
+ ARGB32,
+ RGB565 = 7,
+ R16 = 9,
+ DXT1,
+ DXT5 = 12,
+ RGBA4444,
+ BGRA32,
+ RHalf,
+ RGHalf,
+ RGBAHalf,
+ RFloat,
+ RGFloat,
+ RGBAFloat,
+ YUY2,
+ RGB9e5Float,
+ BC4 = 26,
+ BC5,
+ BC6H = 24,
+ BC7,
+ DXT1Crunched = 28,
+ DXT5Crunched,
+ PVRTC_RGB2,
+ PVRTC_RGBA2,
+ PVRTC_RGB4,
+ PVRTC_RGBA4,
+ ETC_RGB4,
+ ATC_RGB4,
+ ATC_RGBA8,
+ EAC_R = 41,
+ EAC_R_SIGNED,
+ EAC_RG,
+ EAC_RG_SIGNED,
+ ETC2_RGB,
+ ETC2_RGBA1,
+ ETC2_RGBA8,
+ ASTC_RGB_4x4,
+ ASTC_RGB_5x5,
+ ASTC_RGB_6x6,
+ ASTC_RGB_8x8,
+ ASTC_RGB_10x10,
+ ASTC_RGB_12x12,
+ ASTC_RGBA_4x4,
+ ASTC_RGBA_5x5,
+ ASTC_RGBA_6x6,
+ ASTC_RGBA_8x8,
+ ASTC_RGBA_10x10,
+ ASTC_RGBA_12x12,
+ ETC_RGB4_3DS,
+ ETC_RGBA8_3DS,
+ RG16,
+ R8,
+ ETC_RGB4Crunched,
+ ETC2_RGBA8Crunched,
+ ASTC_HDR_4x4,
+ ASTC_HDR_5x5,
+ ASTC_HDR_6x6,
+ ASTC_HDR_8x8,
+ ASTC_HDR_10x10,
+ ASTC_HDR_12x12,
+ }
}
diff --git a/AssetStudio/Classes/Texture2D.cs b/AssetStudio/Classes/Texture2D.cs
index 583f9c71a..862fb8569 100644
--- a/AssetStudio/Classes/Texture2D.cs
+++ b/AssetStudio/Classes/Texture2D.cs
@@ -1,57 +1,5 @@
-using System;
-
-namespace AssetStudio
+namespace AssetStudio
{
- public class StreamingInfo
- {
- public ulong offset;
- public uint size;
- public string path;
-
- public StreamingInfo(ObjectReader reader)
- {
- var version = reader.version;
-
- if (version[0] >= 2020) //2020.1 and up
- {
- offset = reader.ReadUInt64();
- }
- else
- {
- offset = reader.ReadUInt32();
- }
- size = reader.ReadUInt32();
- path = reader.ReadAlignedString();
- }
- }
-
- public class GLTextureSettings
- {
- public int m_FilterMode;
- public int m_Aniso;
- public float m_MipBias;
- public int m_WrapMode;
-
- public GLTextureSettings(ObjectReader reader)
- {
- var version = reader.version;
-
- m_FilterMode = reader.ReadInt32();
- m_Aniso = reader.ReadInt32();
- m_MipBias = reader.ReadSingle();
- if (version[0] >= 2017)//2017.x and up
- {
- m_WrapMode = reader.ReadInt32(); //m_WrapU
- int m_WrapV = reader.ReadInt32();
- int m_WrapW = reader.ReadInt32();
- }
- else
- {
- m_WrapMode = reader.ReadInt32();
- }
- }
- }
-
public sealed class Texture2D : Texture
{
public int m_Width;
@@ -138,71 +86,4 @@ public Texture2D(ObjectReader reader) : base(reader)
image_data = resourceReader;
}
}
-
- public enum TextureFormat
- {
- Alpha8 = 1,
- ARGB4444,
- RGB24,
- RGBA32,
- ARGB32,
- RGB565 = 7,
- R16 = 9,
- DXT1,
- DXT5 = 12,
- RGBA4444,
- BGRA32,
- RHalf,
- RGHalf,
- RGBAHalf,
- RFloat,
- RGFloat,
- RGBAFloat,
- YUY2,
- RGB9e5Float,
- BC4 = 26,
- BC5,
- BC6H = 24,
- BC7,
- DXT1Crunched = 28,
- DXT5Crunched,
- PVRTC_RGB2,
- PVRTC_RGBA2,
- PVRTC_RGB4,
- PVRTC_RGBA4,
- ETC_RGB4,
- ATC_RGB4,
- ATC_RGBA8,
- EAC_R = 41,
- EAC_R_SIGNED,
- EAC_RG,
- EAC_RG_SIGNED,
- ETC2_RGB,
- ETC2_RGBA1,
- ETC2_RGBA8,
- ASTC_RGB_4x4,
- ASTC_RGB_5x5,
- ASTC_RGB_6x6,
- ASTC_RGB_8x8,
- ASTC_RGB_10x10,
- ASTC_RGB_12x12,
- ASTC_RGBA_4x4,
- ASTC_RGBA_5x5,
- ASTC_RGBA_6x6,
- ASTC_RGBA_8x8,
- ASTC_RGBA_10x10,
- ASTC_RGBA_12x12,
- ETC_RGB4_3DS,
- ETC_RGBA8_3DS,
- RG16,
- R8,
- ETC_RGB4Crunched,
- ETC2_RGBA8Crunched,
- ASTC_HDR_4x4,
- ASTC_HDR_5x5,
- ASTC_HDR_6x6,
- ASTC_HDR_8x8,
- ASTC_HDR_10x10,
- ASTC_HDR_12x12,
- }
}
\ No newline at end of file
diff --git a/AssetStudio/Classes/Texture2DArray.cs b/AssetStudio/Classes/Texture2DArray.cs
new file mode 100644
index 000000000..0958b26ed
--- /dev/null
+++ b/AssetStudio/Classes/Texture2DArray.cs
@@ -0,0 +1,46 @@
+namespace AssetStudio
+{
+ public sealed class Texture2DArray : Texture
+ {
+ public int m_Width;
+ public int m_Height;
+ public int m_Depth;
+ public TextureFormat m_TextureFormat;
+ public int m_MipCount;
+ public uint m_DataSize;
+ public GLTextureSettings m_TextureSettings;
+ public int m_ColorSpace;
+ public bool m_IsReadable;
+ public ResourceReader image_data;
+ public StreamingInfo m_StreamData;
+
+ public Texture2DArray(ObjectReader reader) : base(reader)
+ {
+ m_Width = reader.ReadInt32();
+ m_Height = reader.ReadInt32();
+ m_Depth = reader.ReadInt32();
+ m_TextureFormat = (TextureFormat)reader.ReadInt32();
+ m_MipCount = reader.ReadInt32();
+ m_DataSize = reader.ReadUInt32();
+ m_TextureSettings = new GLTextureSettings(reader);
+ m_ColorSpace = reader.ReadInt32();
+ m_IsReadable = reader.ReadBoolean();
+ reader.AlignStream();
+
+ var image_data_size = reader.ReadInt32();
+ if (image_data_size == 0)
+ {
+ m_StreamData = new StreamingInfo(reader);
+ }
+
+ if (!string.IsNullOrEmpty(m_StreamData?.path))
+ {
+ image_data = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, (int)m_StreamData.size);
+ }
+ else
+ {
+ image_data = new ResourceReader(reader, reader.BaseStream.Position, image_data_size);
+ }
+ }
+ }
+}
diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs
index 36d059b6a..216b029b7 100644
--- a/AssetStudioGUI/AssetStudioGUIForm.cs
+++ b/AssetStudioGUI/AssetStudioGUIForm.cs
@@ -42,6 +42,7 @@ partial class AssetStudioGUIForm : Form
#region TexControl
private static char[] textureChannelNames = new[] { 'B', 'G', 'R', 'A' };
private bool[] textureChannels = new[] { true, true, true, true };
+ private static int textureArrayLayer = 0;
#endregion
#region GLControl
@@ -321,6 +322,14 @@ private void AssetStudioForm_KeyDown(object sender, KeyEventArgs e)
textureChannels[3] = !textureChannels[3];
need = true;
break;
+ case Keys.Left:
+ textureArrayLayer--;
+ need = true;
+ break;
+ case Keys.Right:
+ textureArrayLayer++;
+ need = true;
+ break;
}
if (need)
{
@@ -374,6 +383,7 @@ private void enablePreview_Check(object sender, EventArgs e)
switch (lastSelectedItem.Type)
{
case ClassIDType.Texture2D:
+ case ClassIDType.Texture2DArray:
case ClassIDType.Sprite:
{
if (enablePreview.Checked && imageTexture != null)
@@ -689,6 +699,9 @@ private void PreviewAsset(AssetItem assetItem)
case Texture2D m_Texture2D:
PreviewTexture2D(assetItem, m_Texture2D);
break;
+ case Texture2DArray m_Texture2DArray:
+ PreviewTexture2DArray(assetItem, m_Texture2DArray);
+ break;
case AudioClip m_AudioClip:
PreviewAudioClip(assetItem, m_AudioClip);
break;
@@ -736,64 +749,91 @@ private void PreviewAsset(AssetItem assetItem)
}
}
- private void PreviewTexture2D(AssetItem assetItem, Texture2D m_Texture2D)
+ private void PreviewTexture2D(AssetItem assetItem, Texture2D texture2D)
{
- var bitmap = m_Texture2D.ConvertToBitmap(true);
+ var bitmap = texture2D.ConvertToBitmap(true);
if (bitmap != null)
{
- assetItem.InfoText = $"Width: {m_Texture2D.m_Width}\nHeight: {m_Texture2D.m_Height}\nFormat: {m_Texture2D.m_TextureFormat}";
- switch (m_Texture2D.m_TextureSettings.m_FilterMode)
- {
- case 0: assetItem.InfoText += "\nFilter Mode: Point "; break;
- case 1: assetItem.InfoText += "\nFilter Mode: Bilinear "; break;
- case 2: assetItem.InfoText += "\nFilter Mode: Trilinear "; break;
- }
- assetItem.InfoText += $"\nAnisotropic level: {m_Texture2D.m_TextureSettings.m_Aniso}\nMip map bias: {m_Texture2D.m_TextureSettings.m_MipBias}";
- switch (m_Texture2D.m_TextureSettings.m_WrapMode)
+ assetItem.InfoText = $"Width: {texture2D.m_Width}\nHeight: {texture2D.m_Height}\nFormat: {texture2D.m_TextureFormat}";
+ PreviewTextureSettings(assetItem, texture2D.m_TextureSettings);
+ PreviewBitmapWithChannels(assetItem, bitmap);
+ StatusStripUpdate("'Ctrl'+'R'/'G'/'B'/'A' for Channel Toggle");
+ }
+ else
+ {
+ StatusStripUpdate("Unsupported image for preview");
+ }
+ }
+
+ private void PreviewTexture2DArray(AssetItem assetItem, Texture2DArray texture2DArray)
+ {
+ textureArrayLayer %= texture2DArray.m_Depth;
+ if (textureArrayLayer < 0) textureArrayLayer += texture2DArray.m_Depth;
+
+ var bitmap = texture2DArray.ConvertToBitmap(true, textureArrayLayer);
+ if (bitmap != null)
+ {
+ assetItem.InfoText = $"Width: {texture2DArray.m_Width}\nHeight: {texture2DArray.m_Height}\nDepth: {texture2DArray.m_Depth} (Currently showing: {textureArrayLayer})\nFormat: {texture2DArray.m_TextureFormat}";
+ PreviewTextureSettings(assetItem, texture2DArray.m_TextureSettings);
+ PreviewBitmapWithChannels(assetItem, bitmap);
+ StatusStripUpdate("'Ctrl'+'R'/'G'/'B'/'A' for Channel Toggle, 'Ctrl'+Left/Right Arrow to Switch Layer");
+ }
+ else
+ {
+ StatusStripUpdate("Unsupported image for preview");
+ }
+ }
+
+ private void PreviewTextureSettings(AssetItem assetItem, GLTextureSettings textureSettings)
+ {
+ switch (textureSettings.m_FilterMode)
+ {
+ case 0: assetItem.InfoText += "\nFilter Mode: Point "; break;
+ case 1: assetItem.InfoText += "\nFilter Mode: Bilinear "; break;
+ case 2: assetItem.InfoText += "\nFilter Mode: Trilinear "; break;
+ }
+ assetItem.InfoText += $"\nAnisotropic level: {textureSettings.m_Aniso}\nMip map bias: {textureSettings.m_MipBias}";
+ switch (textureSettings.m_WrapMode)
+ {
+ case 0: assetItem.InfoText += "\nWrap mode: Repeat"; break;
+ case 1: assetItem.InfoText += "\nWrap mode: Clamp"; break;
+ }
+ }
+
+ private void PreviewBitmapWithChannels(AssetItem assetItem, Bitmap bitmap) {
+ assetItem.InfoText += "\nChannels: ";
+ int validChannel = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ if (textureChannels[i])
{
- case 0: assetItem.InfoText += "\nWrap mode: Repeat"; break;
- case 1: assetItem.InfoText += "\nWrap mode: Clamp"; break;
+ assetItem.InfoText += textureChannelNames[i];
+ validChannel++;
}
- assetItem.InfoText += "\nChannels: ";
- int validChannel = 0;
- for (int i = 0; i < 4; i++)
+ }
+ if (validChannel == 0)
+ assetItem.InfoText += "None";
+ if (validChannel != 4)
+ {
+ var bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
+ var bytes = new byte[bitmap.Width * bitmap.Height * 4];
+ Marshal.Copy(bmpData.Scan0, bytes, 0, bytes.Length);
+ for (int i = 0; i < bmpData.Height; i++)
{
- if (textureChannels[i])
+ int offset = Math.Abs(bmpData.Stride) * i;
+ for (int j = 0; j < bmpData.Width; j++)
{
- assetItem.InfoText += textureChannelNames[i];
- validChannel++;
+ bytes[offset] = textureChannels[0] ? bytes[offset] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue;
+ bytes[offset + 1] = textureChannels[1] ? bytes[offset + 1] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue;
+ bytes[offset + 2] = textureChannels[2] ? bytes[offset + 2] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue;
+ bytes[offset + 3] = textureChannels[3] ? bytes[offset + 3] : byte.MaxValue;
+ offset += 4;
}
}
- if (validChannel == 0)
- assetItem.InfoText += "None";
- if (validChannel != 4)
- {
- var bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
- var bytes = new byte[bitmap.Width * bitmap.Height * 4];
- Marshal.Copy(bmpData.Scan0, bytes, 0, bytes.Length);
- for (int i = 0; i < bmpData.Height; i++)
- {
- int offset = Math.Abs(bmpData.Stride) * i;
- for (int j = 0; j < bmpData.Width; j++)
- {
- bytes[offset] = textureChannels[0] ? bytes[offset] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue;
- bytes[offset + 1] = textureChannels[1] ? bytes[offset + 1] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue;
- bytes[offset + 2] = textureChannels[2] ? bytes[offset + 2] : validChannel == 1 && textureChannels[3] ? byte.MaxValue : byte.MinValue;
- bytes[offset + 3] = textureChannels[3] ? bytes[offset + 3] : byte.MaxValue;
- offset += 4;
- }
- }
- Marshal.Copy(bytes, 0, bmpData.Scan0, bytes.Length);
- bitmap.UnlockBits(bmpData);
- }
- PreviewTexture(bitmap);
-
- StatusStripUpdate("'Ctrl'+'R'/'G'/'B'/'A' for Channel Toggle");
- }
- else
- {
- StatusStripUpdate("Unsupported image for preview");
+ Marshal.Copy(bytes, 0, bmpData.Scan0, bytes.Length);
+ bitmap.UnlockBits(bmpData);
}
+ PreviewBitmap(bitmap);
}
private void PreviewAudioClip(AssetItem assetItem, AudioClip m_AudioClip)
@@ -1147,7 +1187,7 @@ private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite)
{
assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n";
- PreviewTexture(bitmap);
+ PreviewBitmap(bitmap);
}
else
{
@@ -1155,7 +1195,7 @@ private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite)
}
}
- private void PreviewTexture(Bitmap bitmap)
+ private void PreviewBitmap(Bitmap bitmap)
{
imageTexture?.Dispose();
imageTexture = bitmap;
diff --git a/AssetStudioGUI/Exporter.cs b/AssetStudioGUI/Exporter.cs
index 66b58e43b..9a2c99bc2 100644
--- a/AssetStudioGUI/Exporter.cs
+++ b/AssetStudioGUI/Exporter.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
@@ -16,48 +17,83 @@ public static bool ExportTexture2D(AssetItem item, string exportPath)
var m_Texture2D = (Texture2D)item.Asset;
if (Properties.Settings.Default.convertTexture)
{
- var bitmap = m_Texture2D.ConvertToBitmap(true);
- if (bitmap == null)
- return false;
- ImageFormat format = null;
- var ext = Properties.Settings.Default.convertType;
- bool tga = false;
- switch (ext)
- {
- case "BMP":
- format = ImageFormat.Bmp;
- break;
- case "PNG":
- format = ImageFormat.Png;
- break;
- case "JPEG":
- format = ImageFormat.Jpeg;
- break;
- case "TGA":
- tga = true;
- break;
- }
- if (!TryExportFile(exportPath, item, "." + ext.ToLower(), out var exportFullPath))
+ return ExportBitmap(item, exportPath, m_Texture2D.ConvertToBitmap(true));
+ }
+ else
+ {
+ if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath))
return false;
- if (tga)
+ File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData());
+ return true;
+ }
+ }
+
+ public static bool ExportTexture2DArray(AssetItem item, string exportPath)
+ {
+ var m_Texture2DArray = (Texture2DArray)item.Asset;
+
+ if (Properties.Settings.Default.convertTexture)
+ {
+ for (var i = 0; i < m_Texture2DArray.m_Depth; i++)
{
- var file = new TGA(bitmap);
- file.Save(exportFullPath);
+ if (!ExportBitmap(item, exportPath + "_" + i, m_Texture2DArray.ConvertToBitmap(true, i)))
+ {
+ return false;
+ }
}
- else
+ }
+ else
+ {
+ // TODO: Should this export everything as a single file? If so, which file extension?
+ var data = m_Texture2DArray.image_data.GetData();
+ var singleLength = m_Texture2DArray.m_Width * m_Texture2DArray.m_Height;
+ var single = new byte[singleLength];
+ for (var i = 0; i < m_Texture2DArray.m_Depth; i++)
{
- bitmap.Save(exportFullPath, format);
+ if (!TryExportFile(exportPath + "_" + i, item, ".tex", out var exportFullPath))
+ return false;
+ Array.Copy(data, i * singleLength, single, 0, singleLength);
+ File.WriteAllBytes(exportFullPath, single);
}
- bitmap.Dispose();
- return true;
+ }
+ return true;
+ }
+
+ private static bool ExportBitmap(AssetItem item, string exportPath, System.Drawing.Bitmap bitmap)
+ {
+ if (bitmap == null)
+ return false;
+ ImageFormat format = null;
+ var ext = Properties.Settings.Default.convertType;
+ bool tga = false;
+ switch (ext)
+ {
+ case "BMP":
+ format = ImageFormat.Bmp;
+ break;
+ case "PNG":
+ format = ImageFormat.Png;
+ break;
+ case "JPEG":
+ format = ImageFormat.Jpeg;
+ break;
+ case "TGA":
+ tga = true;
+ break;
+ }
+ if (!TryExportFile(exportPath, item, "." + ext.ToLower(), out var exportFullPath))
+ return false;
+ if (tga)
+ {
+ var file = new TGA(bitmap);
+ file.Save(exportFullPath);
}
else
{
- if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath))
- return false;
- File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData());
- return true;
+ bitmap.Save(exportFullPath, format);
}
+ bitmap.Dispose();
+ return true;
}
public static bool ExportAudioClip(AssetItem item, string exportPath)
@@ -384,6 +420,8 @@ public static bool ExportConvertFile(AssetItem item, string exportPath)
{
case ClassIDType.Texture2D:
return ExportTexture2D(item, exportPath);
+ case ClassIDType.Texture2DArray:
+ return ExportTexture2DArray(item, exportPath);
case ClassIDType.AudioClip:
return ExportAudioClip(item, exportPath);
case ClassIDType.Shader:
diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs
index fdbc260d7..5b79b3dfa 100644
--- a/AssetStudioGUI/Studio.cs
+++ b/AssetStudioGUI/Studio.cs
@@ -148,6 +148,12 @@ public static (string, List) BuildAssetData()
assetItem.Text = m_Texture2D.m_Name;
exportable = true;
break;
+ case Texture2DArray m_Texture2DArray:
+ if (!string.IsNullOrEmpty(m_Texture2DArray.m_StreamData?.path))
+ assetItem.FullSize = asset.byteSize + m_Texture2DArray.m_StreamData.size;
+ assetItem.Text = m_Texture2DArray.m_Name;
+ exportable = true;
+ break;
case AudioClip m_AudioClip:
if (!string.IsNullOrEmpty(m_AudioClip.m_Source))
assetItem.FullSize = asset.byteSize + m_AudioClip.m_Size;
diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs
index b79a790cb..37bb07a69 100644
--- a/AssetStudioUtility/Texture2DConverter.cs
+++ b/AssetStudioUtility/Texture2DConverter.cs
@@ -28,6 +28,20 @@ public Texture2DConverter(Texture2D m_Texture2D)
platform = m_Texture2D.platform;
}
+ public Texture2DConverter(Texture2DArray m_Texture2DArray, int layer)
+ {
+ image_data_size = m_Texture2DArray.m_Width * m_Texture2DArray.m_Height;
+ image_data = new byte[image_data_size];
+
+ Array.Copy(m_Texture2DArray.image_data.GetData(), layer * image_data_size, image_data, 0, image_data_size);
+
+ m_Width = m_Texture2DArray.m_Width;
+ m_Height = m_Texture2DArray.m_Height;
+ m_TextureFormat = m_Texture2DArray.m_TextureFormat;
+ version = m_Texture2DArray.version;
+ platform = m_Texture2DArray.platform;
+ }
+
public Bitmap ConvertToBitmap(bool flip)
{
if (image_data == null || image_data.Length == 0)
diff --git a/AssetStudioUtility/Texture2DExtensions.cs b/AssetStudioUtility/Texture2DExtensions.cs
index 132ca9171..409396a36 100644
--- a/AssetStudioUtility/Texture2DExtensions.cs
+++ b/AssetStudioUtility/Texture2DExtensions.cs
@@ -9,5 +9,11 @@ public static Bitmap ConvertToBitmap(this Texture2D m_Texture2D, bool flip)
var converter = new Texture2DConverter(m_Texture2D);
return converter.ConvertToBitmap(flip);
}
+
+ public static Bitmap ConvertToBitmap(this Texture2DArray m_Texture2DArray, bool flip, int layer)
+ {
+ var converter = new Texture2DConverter(m_Texture2DArray, layer);
+ return converter.ConvertToBitmap(flip);
+ }
}
}
From 4db39e59c4b488bb285a1e6bdd477cd492da109c Mon Sep 17 00:00:00 2001
From: Benedikt Werner <1benediktwerner@gmail.com>
Date: Tue, 16 Feb 2021 23:55:59 +0100
Subject: [PATCH 2/3] Fix Texture2DArray export naming
---
AssetStudioGUI/Exporter.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/AssetStudioGUI/Exporter.cs b/AssetStudioGUI/Exporter.cs
index 9a2c99bc2..8d3e8e0b6 100644
--- a/AssetStudioGUI/Exporter.cs
+++ b/AssetStudioGUI/Exporter.cs
@@ -36,7 +36,7 @@ public static bool ExportTexture2DArray(AssetItem item, string exportPath)
{
for (var i = 0; i < m_Texture2DArray.m_Depth; i++)
{
- if (!ExportBitmap(item, exportPath + "_" + i, m_Texture2DArray.ConvertToBitmap(true, i)))
+ if (!ExportBitmap(item, exportPath, m_Texture2DArray.ConvertToBitmap(true, i), "_" + i))
{
return false;
}
@@ -50,7 +50,7 @@ public static bool ExportTexture2DArray(AssetItem item, string exportPath)
var single = new byte[singleLength];
for (var i = 0; i < m_Texture2DArray.m_Depth; i++)
{
- if (!TryExportFile(exportPath + "_" + i, item, ".tex", out var exportFullPath))
+ if (!TryExportFile(exportPath, item, "_" + i + ".tex", out var exportFullPath))
return false;
Array.Copy(data, i * singleLength, single, 0, singleLength);
File.WriteAllBytes(exportFullPath, single);
@@ -59,7 +59,7 @@ public static bool ExportTexture2DArray(AssetItem item, string exportPath)
return true;
}
- private static bool ExportBitmap(AssetItem item, string exportPath, System.Drawing.Bitmap bitmap)
+ private static bool ExportBitmap(AssetItem item, string exportPath, System.Drawing.Bitmap bitmap, string postfix = "")
{
if (bitmap == null)
return false;
@@ -81,7 +81,7 @@ private static bool ExportBitmap(AssetItem item, string exportPath, System.Drawi
tga = true;
break;
}
- if (!TryExportFile(exportPath, item, "." + ext.ToLower(), out var exportFullPath))
+ if (!TryExportFile(exportPath, item, postfix + "." + ext.ToLower(), out var exportFullPath))
return false;
if (tga)
{
From 32fb4539caaed5506364448c2d202d5230c4219a Mon Sep 17 00:00:00 2001
From: Benedikt Werner <1benediktwerner@gmail.com>
Date: Tue, 16 Feb 2021 23:56:22 +0100
Subject: [PATCH 3/3] Fix IndexOutOfRangeException when merge-exporting assets
unrelated to any objects
---
AssetStudioGUI/AssetStudioGUIForm.cs | 77 ++++++++++++++++------------
1 file changed, 43 insertions(+), 34 deletions(-)
diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs
index 216b029b7..b5d58110c 100644
--- a/AssetStudioGUI/AssetStudioGUIForm.cs
+++ b/AssetStudioGUI/AssetStudioGUIForm.cs
@@ -1356,27 +1356,26 @@ private void exportObjectswithAnimationClipMenuItem_Click(object sender, EventAr
private void ExportObjects(bool animation)
{
- if (sceneTreeView.Nodes.Count > 0)
+ if (sceneTreeView.Nodes.Count == 0)
{
- var saveFolderDialog = new OpenFolderDialog();
- if (saveFolderDialog.ShowDialog(this) == DialogResult.OK)
+ StatusStripUpdate("No Objects available for export");
+ return;
+ }
+
+ var saveFolderDialog = new OpenFolderDialog();
+ if (saveFolderDialog.ShowDialog(this) == DialogResult.OK)
+ {
+ var exportPath = saveFolderDialog.Folder + "\\GameObject\\";
+ List animationList = null;
+ if (animation)
{
- var exportPath = saveFolderDialog.Folder + "\\GameObject\\";
- List animationList = null;
- if (animation)
+ animationList = GetSelectedAssets().Where(x => x.Type == ClassIDType.AnimationClip).ToList();
+ if (animationList.Count == 0)
{
- animationList = GetSelectedAssets().Where(x => x.Type == ClassIDType.AnimationClip).ToList();
- if (animationList.Count == 0)
- {
- animationList = null;
- }
+ animationList = null;
}
- ExportObjectsWithAnimationClip(exportPath, sceneTreeView.Nodes, animationList);
}
- }
- else
- {
- StatusStripUpdate("No Objects available for export");
+ ExportObjectsWithAnimationClip(exportPath, sceneTreeView.Nodes, animationList);
}
}
@@ -1392,28 +1391,38 @@ private void exportSelectedObjectsmergeWithAnimationClipToolStripMenuItem_Click(
private void ExportMergeObjects(bool animation)
{
- if (sceneTreeView.Nodes.Count > 0)
+ if (sceneTreeView.Nodes.Count == 0)
+ {
+ StatusStripUpdate("No Objects available for export");
+ return;
+ }
+
+ var gameObjects = new List();
+ GetSelectedParentNode(sceneTreeView.Nodes, gameObjects);
+
+ if (gameObjects.Count == 0)
{
- var gameObjects = new List();
- GetSelectedParentNode(sceneTreeView.Nodes, gameObjects);
- var saveFileDialog = new SaveFileDialog();
- saveFileDialog.FileName = gameObjects[0].m_Name + " (merge).fbx";
- saveFileDialog.AddExtension = false;
- saveFileDialog.Filter = "Fbx file (*.fbx)|*.fbx";
- if (saveFileDialog.ShowDialog() == DialogResult.OK)
- {
- var exportPath = saveFileDialog.FileName;
- List animationList = null;
- if (animation)
+ StatusStripUpdate("No Object can be exported.");
+ return;
+ }
+
+ var saveFileDialog = new SaveFileDialog();
+ saveFileDialog.FileName = gameObjects[0].m_Name + " (merge).fbx";
+ saveFileDialog.AddExtension = false;
+ saveFileDialog.Filter = "Fbx file (*.fbx)|*.fbx";
+ if (saveFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ var exportPath = saveFileDialog.FileName;
+ List animationList = null;
+ if (animation)
+ {
+ animationList = GetSelectedAssets().Where(x => x.Type == ClassIDType.AnimationClip).ToList();
+ if (animationList.Count == 0)
{
- animationList = GetSelectedAssets().Where(x => x.Type == ClassIDType.AnimationClip).ToList();
- if (animationList.Count == 0)
- {
- animationList = null;
- }
+ animationList = null;
}
- ExportObjectsMergeWithAnimationClip(exportPath, gameObjects, animationList);
}
+ ExportObjectsMergeWithAnimationClip(exportPath, gameObjects, animationList);
}
}