Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0d90d1b1e | ||
|
|
dd727758a8 | ||
|
|
d44ed315e3 | ||
|
|
e30b9e9e89 | ||
|
|
9632e88115 | ||
|
|
bb19dd5019 | ||
|
|
b6c6ceba1c | ||
|
|
7c0a6375b1 | ||
|
|
5c489c5f83 | ||
|
|
e90af43459 | ||
|
|
381a7d89ae | ||
|
|
572e3bf0d6 | ||
|
|
3d7d51b54f | ||
|
|
abbd27fde7 |
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>1.1.0</Version>
|
||||
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>1.1.0</Version>
|
||||
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2023</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -37,12 +37,17 @@ namespace AssetStudio
|
||||
{
|
||||
filteredAssetTypesList.Add(ClassIDType.MonoScript);
|
||||
}
|
||||
if (classIDTypes.Contains(ClassIDType.Sprite))
|
||||
if (classIDTypes.Contains(ClassIDType.Sprite) || classIDTypes.Contains(ClassIDType.AkPortraitSprite))
|
||||
{
|
||||
filteredAssetTypesList.Add(ClassIDType.Texture2D);
|
||||
filteredAssetTypesList.Add(ClassIDType.SpriteAtlas);
|
||||
filteredAssetTypesList.UnionWith(new HashSet<ClassIDType>
|
||||
{
|
||||
ClassIDType.Texture2D,
|
||||
ClassIDType.SpriteAtlas,
|
||||
ClassIDType.MonoBehaviour,
|
||||
ClassIDType.MonoScript
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
filteredAssetTypesList.UnionWith(classIDTypes);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace AssetStudio
|
||||
{
|
||||
public enum ClassIDType
|
||||
{
|
||||
AkPortraitSprite = -2,
|
||||
UnknownType = -1,
|
||||
Object = 0,
|
||||
GameObject = 1,
|
||||
|
||||
@@ -182,6 +182,13 @@ namespace AssetStudio
|
||||
public float width;
|
||||
public float height;
|
||||
|
||||
public Rectf(float x, float y, float w, float h) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
width = w;
|
||||
height = h;
|
||||
}
|
||||
|
||||
public Rectf(BinaryReader reader)
|
||||
{
|
||||
x = reader.ReadSingle();
|
||||
@@ -205,6 +212,7 @@ namespace AssetStudio
|
||||
public PPtr<SpriteAtlas> m_SpriteAtlas;
|
||||
public SpriteRenderData m_RD;
|
||||
public Vector2[][] m_PhysicsShape;
|
||||
public bool akSplitAlpha;
|
||||
|
||||
public Sprite(ObjectReader reader) : base(reader)
|
||||
{
|
||||
@@ -254,6 +262,8 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
akSplitAlpha = false;
|
||||
|
||||
//vector m_Bones 2018 and up
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net472;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle>
|
||||
<AssemblyName>AssetStudioModCLI</AssemblyName>
|
||||
<Version>0.17.4.0</Version>
|
||||
<AssemblyTitle>ArknightsStudio by aelurum</AssemblyTitle>
|
||||
<AssemblyName>ArknightsStudioCLI</AssemblyName>
|
||||
<Version>1.1.0</Version>
|
||||
<Copyright>Copyright © Perfare; Copyright © aelurum 2023</Copyright>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>embedded</DebugType>
|
||||
|
||||
228
AssetStudioCLI/Components/Arknights/AkSpriteHelper.cs
Normal file
228
AssetStudioCLI/Components/Arknights/AkSpriteHelper.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using Arknights.PortraitSpriteMono;
|
||||
using AssetStudio;
|
||||
using AssetStudioCLI;
|
||||
using AssetStudioCLI.Options;
|
||||
using Newtonsoft.Json;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Arknights
|
||||
{
|
||||
internal static class AkSpriteHelper
|
||||
{
|
||||
public static Texture2D TryFindAlphaTex(AssetItem assetItem, AvgSprite avgSprite, bool isAvgSprite)
|
||||
{
|
||||
Sprite m_Sprite = (Sprite)assetItem.Asset;
|
||||
var imgType = "arts/characters";
|
||||
if (m_Sprite.m_RD.alphaTexture.m_PathID == 0)
|
||||
{
|
||||
if (isAvgSprite)
|
||||
{
|
||||
if (avgSprite?.FullAlphaTexture != null)
|
||||
return avgSprite.FullAlphaTexture;
|
||||
|
||||
imgType = "avg/characters"; //since the avg hub was not found for some reason, let's try to find alpha tex by name
|
||||
}
|
||||
var spriteFullName = Path.GetFileNameWithoutExtension(assetItem.Container);
|
||||
foreach (var item in Studio.loadedAssetsList)
|
||||
{
|
||||
if (item.Type == ClassIDType.Texture2D)
|
||||
{
|
||||
if (item.Container.Contains(imgType) && item.Container.Contains($"illust_{m_Sprite.m_Name}_material") && item.Text.Contains("[alpha]"))
|
||||
return (Texture2D)item.Asset;
|
||||
if (item.Container.Contains(imgType) && item.Container.Contains(spriteFullName) && item.Text == $"{m_Sprite.m_Name}[alpha]")
|
||||
return (Texture2D)item.Asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Image<Bgra32> AkGetImage(this Sprite m_Sprite, AvgSprite avgSprite = null, SpriteMaskMode spriteMaskMode = SpriteMaskMode.On)
|
||||
{
|
||||
if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D) && m_Sprite.m_RD.alphaTexture.TryGet(out var m_AlphaTexture2D) && spriteMaskMode != SpriteMaskMode.Off)
|
||||
{
|
||||
Image<Bgra32> tex;
|
||||
Image<Bgra32> alphaTex;
|
||||
|
||||
if (avgSprite != null && avgSprite.IsHubParsed)
|
||||
{
|
||||
alphaTex = m_AlphaTexture2D.ConvertToImage(true);
|
||||
if (avgSprite.IsFaceSprite)
|
||||
{
|
||||
var faceImage = m_Texture2D.ConvertToImage(true);
|
||||
var faceAlpha = avgSprite.FaceSpriteAlphaTexture.ConvertToImage(true);
|
||||
if (new Size(faceImage.Width, faceImage.Height) != avgSprite.FaceSize)
|
||||
{
|
||||
faceImage.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
|
||||
faceAlpha.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
|
||||
}
|
||||
tex = avgSprite.FullTexture.ConvertToImage(true);
|
||||
tex.Mutate(x => x.DrawImage(faceImage, avgSprite.FacePos, opacity: 1f));
|
||||
alphaTex.Mutate(x => x.DrawImage(faceAlpha, avgSprite.FacePos, opacity: 1f));
|
||||
}
|
||||
else
|
||||
{
|
||||
tex = m_Texture2D.ConvertToImage(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tex = CutImage(m_Texture2D.ConvertToImage(false), m_Sprite.m_RD.textureRect, m_Sprite.m_RD.downscaleMultiplier);
|
||||
alphaTex = CutImage(m_AlphaTexture2D.ConvertToImage(false), m_Sprite.m_RD.textureRect, m_Sprite.m_RD.downscaleMultiplier);
|
||||
}
|
||||
tex.ApplyRGBMask(alphaTex);
|
||||
return tex;
|
||||
}
|
||||
else if (m_Sprite.m_RD.texture.TryGet(out m_Texture2D) && avgSprite != null && avgSprite.IsHubParsed)
|
||||
{
|
||||
if (!avgSprite.IsFaceSprite)
|
||||
{
|
||||
return m_Texture2D.ConvertToImage(true);
|
||||
}
|
||||
|
||||
var faceImage = m_Texture2D.ConvertToImage(true);
|
||||
var tex = avgSprite.FullTexture.ConvertToImage(true);
|
||||
if (new Size(faceImage.Width, faceImage.Height) != avgSprite.FaceSize)
|
||||
{
|
||||
faceImage.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
|
||||
}
|
||||
tex.Mutate(x => x.DrawImage(faceImage, avgSprite.FacePos, opacity: 1f));
|
||||
|
||||
return tex;
|
||||
}
|
||||
else if (m_Sprite.m_RD.texture.TryGet(out m_Texture2D))
|
||||
{
|
||||
return CutImage(m_Texture2D.ConvertToImage(false), m_Sprite.m_RD.textureRect, m_Sprite.m_RD.downscaleMultiplier);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Image<Bgra32> AkGetImage(this PortraitSprite portraitSprite, SpriteMaskMode spriteMaskMode = SpriteMaskMode.On)
|
||||
{
|
||||
if (portraitSprite.Texture != null && portraitSprite.AlphaTexture != null)
|
||||
{
|
||||
var tex = CutImage(portraitSprite.Texture.ConvertToImage(false), portraitSprite.TextureRect, portraitSprite.DownscaleMultiplier, portraitSprite.Rotate);
|
||||
|
||||
if (spriteMaskMode == SpriteMaskMode.Off)
|
||||
{
|
||||
return tex;
|
||||
}
|
||||
else
|
||||
{
|
||||
var alphaTex = CutImage(portraitSprite.AlphaTexture.ConvertToImage(false), portraitSprite.TextureRect, portraitSprite.DownscaleMultiplier, portraitSprite.Rotate);
|
||||
tex.ApplyRGBMask(alphaTex);
|
||||
return tex;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<PortraitSprite> GeneratePortraits(AssetItem asset)
|
||||
{
|
||||
var portraits = new List<PortraitSprite>();
|
||||
|
||||
var portraitsDict = ((MonoBehaviour)asset.Asset).ToType();
|
||||
if (portraitsDict == null)
|
||||
{
|
||||
Logger.Warning("Portraits MonoBehaviour is not readable.");
|
||||
return portraits;
|
||||
}
|
||||
var portraitsJson = JsonConvert.SerializeObject(portraitsDict);
|
||||
var portraitsData = JsonConvert.DeserializeObject<PortraitSpriteConfig>(portraitsJson);
|
||||
|
||||
var atlasTex = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == portraitsData._atlas.Texture.m_PathID).Asset;
|
||||
var atlasAlpha = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == portraitsData._atlas.Alpha.m_PathID).Asset;
|
||||
|
||||
foreach (var portraitData in portraitsData._sprites)
|
||||
{
|
||||
var portraitSprite = new PortraitSprite()
|
||||
{
|
||||
Name = portraitData.Name,
|
||||
AssetsFile = atlasTex.assetsFile,
|
||||
Container = asset.Container,
|
||||
Texture = atlasTex,
|
||||
AlphaTexture = atlasAlpha,
|
||||
TextureRect = new Rectf(portraitData.Rect.X, portraitData.Rect.Y, portraitData.Rect.W, portraitData.Rect.H),
|
||||
Rotate = portraitData.Rotate,
|
||||
};
|
||||
portraits.Add(portraitSprite);
|
||||
}
|
||||
|
||||
return portraits;
|
||||
}
|
||||
|
||||
private static void ApplyRGBMask(this Image<Bgra32> tex, Image<Bgra32> texMask)
|
||||
{
|
||||
using (texMask)
|
||||
{
|
||||
bool resized = false;
|
||||
if (tex.Width != texMask.Width || tex.Height != texMask.Height)
|
||||
{
|
||||
texMask.Mutate(x => x.Resize(tex.Width, tex.Height, CLIOptions.o_akAlphaTexResampler.Value));
|
||||
resized = true;
|
||||
}
|
||||
|
||||
var invGamma = 1.0 / (1.0 + CLIOptions.o_akShadowGamma.Value / 10.0);
|
||||
if (CLIOptions.akResizedOnly && !resized)
|
||||
{
|
||||
invGamma = 1.0;
|
||||
}
|
||||
|
||||
tex.ProcessPixelRows(texMask, (sourceTex, targetTexMask) =>
|
||||
{
|
||||
for (int y = 0; y < texMask.Height; y++)
|
||||
{
|
||||
var texRow = sourceTex.GetRowSpan(y);
|
||||
var maskRow = targetTexMask.GetRowSpan(y);
|
||||
for (int x = 0; x < maskRow.Length; x++)
|
||||
{
|
||||
var grayscale = (maskRow[x].R + maskRow[x].G + maskRow[x].B) / 3.0;
|
||||
if (invGamma != 1.0)
|
||||
{
|
||||
grayscale = 255 - Math.Pow((255 - grayscale) / 255, invGamma) * 255;
|
||||
}
|
||||
texRow[x].A = (byte)grayscale;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static Image<Bgra32> CutImage(Image<Bgra32> originalImage, Rectf textureRect, float downscaleMultiplier, bool rotate = false)
|
||||
{
|
||||
if (originalImage != null)
|
||||
{
|
||||
if (downscaleMultiplier > 0f && downscaleMultiplier != 1f)
|
||||
{
|
||||
var newSize = (Size)(new Size(originalImage.Width, originalImage.Height) / downscaleMultiplier);
|
||||
originalImage.Mutate(x => x.Resize(newSize, KnownResamplers.Lanczos3, compand: true));
|
||||
}
|
||||
var rectX = (int)Math.Floor(textureRect.x);
|
||||
var rectY = (int)Math.Floor(textureRect.y);
|
||||
var rectRight = (int)Math.Ceiling(textureRect.x + textureRect.width);
|
||||
var rectBottom = (int)Math.Ceiling(textureRect.y + textureRect.height);
|
||||
rectRight = Math.Min(rectRight, originalImage.Width);
|
||||
rectBottom = Math.Min(rectBottom, originalImage.Height);
|
||||
var rect = new Rectangle(rectX, rectY, rectRight - rectX, rectBottom - rectY);
|
||||
var spriteImage = originalImage.Clone(x => x.Crop(rect));
|
||||
originalImage.Dispose();
|
||||
if (rotate)
|
||||
{
|
||||
spriteImage.Mutate(x => x.Rotate(RotateMode.Rotate270));
|
||||
}
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||
|
||||
return spriteImage;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
126
AssetStudioCLI/Components/Arknights/AvgSprite.cs
Normal file
126
AssetStudioCLI/Components/Arknights/AvgSprite.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using Arknights.AvgCharHubMono;
|
||||
using AssetStudio;
|
||||
using AssetStudioCLI;
|
||||
using SixLabors.ImageSharp;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Arknights
|
||||
{
|
||||
internal class AvgSprite
|
||||
{
|
||||
public Texture2D FaceSpriteAlphaTexture { get; }
|
||||
public Texture2D FullTexture { get; }
|
||||
public Texture2D FullAlphaTexture { get; }
|
||||
public Point FacePos { get; }
|
||||
public Size FaceSize { get; }
|
||||
public string Alias { get; }
|
||||
public bool IsWholeBodySprite { get; }
|
||||
public bool IsFaceSprite { get; }
|
||||
public bool IsHubParsed { get; }
|
||||
|
||||
private AvgSpriteConfig GetCurSpriteGroup(AvgSpriteConfigGroup spriteHubDataGrouped, long spriteItemID, string spriteName)
|
||||
{
|
||||
if (spriteHubDataGrouped.SpriteGroups.Length > 1)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(spriteName))
|
||||
{
|
||||
var groupFromName = int.TryParse(spriteName?.Substring(spriteName.IndexOf('$') + 1, 1), out int groupIndex);
|
||||
if (groupFromName)
|
||||
{
|
||||
return spriteHubDataGrouped.SpriteGroups[groupIndex - 1];
|
||||
}
|
||||
}
|
||||
return spriteHubDataGrouped.SpriteGroups.FirstOrDefault(x => x.Sprites.Any(y => y.Sprite.m_PathID == spriteItemID));
|
||||
}
|
||||
else
|
||||
{
|
||||
return spriteHubDataGrouped.SpriteGroups[0];
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetSpriteHub(AssetItem assetItem, out AvgSpriteConfig spriteHubData)
|
||||
{
|
||||
spriteHubData = null;
|
||||
var avgSpriteHubItem = Studio.loadedAssetsList.Find(x =>
|
||||
x.Type == ClassIDType.MonoBehaviour
|
||||
&& x.Container == assetItem.Container
|
||||
&& x.Text.IndexOf("AVGCharacterSpriteHub", StringComparison.OrdinalIgnoreCase) >= 0
|
||||
);
|
||||
if (avgSpriteHubItem == null)
|
||||
{
|
||||
Logger.Warning("AVGCharacterSpriteHub was not found.");
|
||||
return false;
|
||||
}
|
||||
var spriteHubDict = ((MonoBehaviour)avgSpriteHubItem.Asset).ToType();
|
||||
if (spriteHubDict == null)
|
||||
{
|
||||
Logger.Warning("AVGCharacterSpriteHub is not readable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var spriteHubJson = JsonConvert.SerializeObject(spriteHubDict);
|
||||
if (avgSpriteHubItem.Text.ToLower().Contains("hubgroup"))
|
||||
{
|
||||
var groupedSpriteHub = JsonConvert.DeserializeObject<AvgSpriteConfigGroup>(spriteHubJson);
|
||||
spriteHubData = GetCurSpriteGroup(groupedSpriteHub, assetItem.m_PathID, assetItem.Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteHubData = JsonConvert.DeserializeObject<AvgSpriteConfig>(spriteHubJson);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public AvgSprite(AssetItem assetItem)
|
||||
{
|
||||
if (TryGetSpriteHub(assetItem, out var spriteHubData))
|
||||
{
|
||||
IsHubParsed = spriteHubData?.Sprites.Length > 0;
|
||||
}
|
||||
if (IsHubParsed)
|
||||
{
|
||||
var curSpriteData = spriteHubData.Sprites.FirstOrDefault(x => x.Sprite.m_PathID == assetItem.m_PathID);
|
||||
|
||||
if (curSpriteData == null)
|
||||
{
|
||||
Logger.Warning($"Sprite \"{assetItem.Text}\" was not found in the avg sprite hub");
|
||||
return;
|
||||
}
|
||||
|
||||
Alias = curSpriteData.Alias;
|
||||
IsWholeBodySprite = curSpriteData.IsWholeBody;
|
||||
|
||||
if (spriteHubData.FaceSize.X > 0 && spriteHubData.FaceSize.Y > 0) //If face data exist
|
||||
{
|
||||
var fullTexSpriteData = spriteHubData.Sprites.Last(); //Last sprite item in the list usually contains PathID of Sprite with full texture
|
||||
|
||||
var curSprite = (Sprite)assetItem.Asset;
|
||||
IsFaceSprite = curSprite.m_Rect.width <= 256 && curSprite.m_Rect.height <= 256 && curSprite.m_PathID != fullTexSpriteData.Sprite.m_PathID;
|
||||
|
||||
var curSpriteAlphaID = curSpriteData.AlphaTex.m_PathID;
|
||||
var curSpriteAlphaTex = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == curSpriteAlphaID)?.Asset;
|
||||
if (curSpriteAlphaTex != null)
|
||||
{
|
||||
FaceSpriteAlphaTexture = IsFaceSprite ? curSpriteAlphaTex : null;
|
||||
fullTexSpriteData = IsFaceSprite ? fullTexSpriteData : curSpriteData;
|
||||
}
|
||||
var fullTexSpriteID = fullTexSpriteData.Sprite.m_PathID;
|
||||
var fullTexAlphaID = fullTexSpriteData.AlphaTex.m_PathID;
|
||||
var fullTexSprite = (Sprite)Studio.loadedAssetsList.Find(x => x.m_PathID == fullTexSpriteID).Asset;
|
||||
|
||||
FullTexture = fullTexSprite.m_RD.texture.TryGet(out var fullTex) ? fullTex : null;
|
||||
FullAlphaTexture = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == fullTexAlphaID)?.Asset;
|
||||
FacePos = new Point((int)Math.Round(spriteHubData.FacePos.X), (int)Math.Round(spriteHubData.FacePos.Y));
|
||||
FaceSize = new Size((int)Math.Round(spriteHubData.FaceSize.X), (int)Math.Round(spriteHubData.FaceSize.Y));
|
||||
}
|
||||
else
|
||||
{
|
||||
FullAlphaTexture = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == curSpriteData.AlphaTex.m_PathID).Asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
AssetStudioCLI/Components/Arknights/AvgSpriteConfig.cs
Normal file
30
AssetStudioCLI/Components/Arknights/AvgSpriteConfig.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using AssetStudio;
|
||||
|
||||
namespace Arknights.AvgCharHubMono
|
||||
{
|
||||
internal class AvgAssetIDs
|
||||
{
|
||||
public int m_FileID { get; set; }
|
||||
public long m_PathID { get; set; }
|
||||
}
|
||||
|
||||
internal class AvgSpriteData
|
||||
{
|
||||
public AvgAssetIDs Sprite { get; set; }
|
||||
public AvgAssetIDs AlphaTex { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public bool IsWholeBody { get; set; }
|
||||
}
|
||||
|
||||
internal class AvgSpriteConfig
|
||||
{
|
||||
public AvgSpriteData[] Sprites { get; set; }
|
||||
public Vector2 FaceSize { get; set; }
|
||||
public Vector3 FacePos { get; set; }
|
||||
}
|
||||
|
||||
internal class AvgSpriteConfigGroup
|
||||
{
|
||||
public AvgSpriteConfig[] SpriteGroups { get; set; }
|
||||
}
|
||||
}
|
||||
24
AssetStudioCLI/Components/Arknights/PortraitSprite.cs
Normal file
24
AssetStudioCLI/Components/Arknights/PortraitSprite.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using AssetStudio;
|
||||
|
||||
|
||||
namespace Arknights
|
||||
{
|
||||
internal class PortraitSprite
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public ClassIDType Type { get; }
|
||||
public SerializedFile AssetsFile { get; set; }
|
||||
public string Container { get; set; }
|
||||
public Texture2D Texture { get; set; }
|
||||
public Texture2D AlphaTexture { get; set; }
|
||||
public Rectf TextureRect { get; set; }
|
||||
public bool Rotate { get; set; }
|
||||
public float DownscaleMultiplier { get; }
|
||||
|
||||
public PortraitSprite()
|
||||
{
|
||||
Type = ClassIDType.AkPortraitSprite;
|
||||
DownscaleMultiplier = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
AssetStudioCLI/Components/Arknights/PortraitSpriteConfig.cs
Normal file
41
AssetStudioCLI/Components/Arknights/PortraitSpriteConfig.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
namespace Arknights.PortraitSpriteMono
|
||||
{
|
||||
internal class PortraitRect
|
||||
{
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
public float W { get; set; }
|
||||
public float H { get; set; }
|
||||
}
|
||||
|
||||
internal class AtlasSprite
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Guid { get; set; }
|
||||
public int Atlas { get; set; }
|
||||
public PortraitRect Rect { get; set; }
|
||||
public bool Rotate { get; set; }
|
||||
}
|
||||
|
||||
internal class TextureIDs
|
||||
{
|
||||
public int m_FileID { get; set; }
|
||||
public long m_PathID { get; set; }
|
||||
}
|
||||
|
||||
internal class AtlasInfo
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public TextureIDs Texture { get; set; }
|
||||
public TextureIDs Alpha { get; set; }
|
||||
public int Size { get; set; }
|
||||
}
|
||||
|
||||
internal class PortraitSpriteConfig
|
||||
{
|
||||
public string m_Name { get; set; }
|
||||
public AtlasSprite[] _sprites { get; set; }
|
||||
public AtlasInfo _atlas { get; set; }
|
||||
public int _index { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using AssetStudio;
|
||||
using Arknights;
|
||||
using AssetStudio;
|
||||
|
||||
namespace AssetStudioCLI
|
||||
{
|
||||
@@ -14,6 +15,7 @@ namespace AssetStudioCLI
|
||||
public string Text;
|
||||
public string UniqueID;
|
||||
public GameObjectNode Node;
|
||||
public PortraitSprite AkPortraitSprite;
|
||||
|
||||
public AssetItem(Object asset)
|
||||
{
|
||||
@@ -24,5 +26,17 @@ namespace AssetStudioCLI
|
||||
m_PathID = asset.m_PathID;
|
||||
FullSize = asset.byteSize;
|
||||
}
|
||||
|
||||
public AssetItem(PortraitSprite akPortraitSprite)
|
||||
{
|
||||
Asset = null;
|
||||
SourceFile = akPortraitSprite.AssetsFile;
|
||||
Container = akPortraitSprite.Container;
|
||||
Type = akPortraitSprite.Type;
|
||||
TypeString = Type.ToString();
|
||||
Text = akPortraitSprite.Name;
|
||||
m_PathID = -1;
|
||||
AkPortraitSprite = akPortraitSprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using AssetStudio;
|
||||
using Arknights;
|
||||
using AssetStudio;
|
||||
using AssetStudioCLI.Options;
|
||||
using Newtonsoft.Json;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AssetStudioCLI
|
||||
{
|
||||
@@ -235,11 +239,80 @@ namespace AssetStudioCLI
|
||||
|
||||
public static bool ExportSprite(AssetItem item, string exportPath)
|
||||
{
|
||||
Image<Bgra32> image;
|
||||
AvgSprite avgSprite = null;
|
||||
var alias = "";
|
||||
var m_Sprite = (Sprite)item.Asset;
|
||||
var type = CLIOptions.o_imageFormat.Value;
|
||||
var alphaMask = SpriteMaskMode.On;
|
||||
var spriteMaskMode = CLIOptions.o_akSpriteAlphaMode.Value != AkSpriteAlphaMode.None ? SpriteMaskMode.Export : SpriteMaskMode.Off;
|
||||
var isCharAvgSprite = item.Container.Contains("avg/characters");
|
||||
var isCharArt = item.Container.Contains("arts/characters");
|
||||
|
||||
if (isCharAvgSprite)
|
||||
{
|
||||
avgSprite = new AvgSprite(item);
|
||||
|
||||
if (CLIOptions.f_akAddAliases.Value && !string.IsNullOrEmpty(avgSprite.Alias))
|
||||
{
|
||||
alias = $"_{avgSprite.Alias}";
|
||||
}
|
||||
|
||||
if (!CLIOptions.f_akOriginalAvgNames.Value)
|
||||
{
|
||||
var groupedPattern = new Regex(@"^\d{1,2}\$\d{1,2}$"); // "spriteIndex$groupIndex"
|
||||
var notGroupedPattern = new Regex(@"^\d{1,2}$"); // "spriteIndex"
|
||||
if (groupedPattern.IsMatch(m_Sprite.m_Name) || notGroupedPattern.IsMatch(m_Sprite.m_Name))
|
||||
{
|
||||
var fullName = Path.GetFileNameWithoutExtension(item.Container);
|
||||
item.Text = $"{fullName}#{m_Sprite.m_Name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath, alias))
|
||||
return false;
|
||||
|
||||
if (CLIOptions.o_akSpriteAlphaMode.Value == AkSpriteAlphaMode.SearchExternal && (isCharAvgSprite || isCharArt))
|
||||
{
|
||||
if (m_Sprite.m_RD.alphaTexture.IsNull)
|
||||
{
|
||||
var charAlphaAtlas = AkSpriteHelper.TryFindAlphaTex(item, avgSprite, isCharAvgSprite);
|
||||
if (charAlphaAtlas != null)
|
||||
{
|
||||
m_Sprite.m_RD.alphaTexture.Set(charAlphaAtlas);
|
||||
m_Sprite.akSplitAlpha = true;
|
||||
}
|
||||
}
|
||||
image = m_Sprite.AkGetImage(avgSprite, spriteMaskMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
image = m_Sprite.GetImage(spriteMaskMode);
|
||||
}
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
using (image)
|
||||
{
|
||||
using (var file = File.OpenWrite(exportFullPath))
|
||||
{
|
||||
image.WriteToStream(file, type);
|
||||
}
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ExportPortraitSprite(AssetItem item, string exportPath)
|
||||
{
|
||||
var type = CLIOptions.o_imageFormat.Value;
|
||||
var spriteMaskMode = CLIOptions.o_akSpriteAlphaMode.Value != AkSpriteAlphaMode.None ? SpriteMaskMode.Export : SpriteMaskMode.Off;
|
||||
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
|
||||
return false;
|
||||
var image = ((Sprite)item.Asset).GetImage(alphaMask);
|
||||
|
||||
var image = item.AkPortraitSprite.AkGetImage(spriteMaskMode: spriteMaskMode);
|
||||
if (image != null)
|
||||
{
|
||||
using (image)
|
||||
@@ -257,6 +330,11 @@ namespace AssetStudioCLI
|
||||
|
||||
public static bool ExportRawFile(AssetItem item, string exportPath)
|
||||
{
|
||||
if (item.Asset == null)
|
||||
{
|
||||
Logger.Warning($"Raw export is not supported for \"{item.Text}\" ({item.TypeString}) file");
|
||||
return false;
|
||||
}
|
||||
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
|
||||
@@ -292,9 +370,13 @@ namespace AssetStudioCLI
|
||||
exportAllNodes, exportSkins, exportAnimations, exportBlendShape, castToBone, boneSize, exportAllUvsAsDiffuseMaps, scaleFactor, fbxVersion, fbxFormat == 1);
|
||||
}
|
||||
|
||||
|
||||
public static bool ExportDumpFile(AssetItem item, string exportPath)
|
||||
{
|
||||
if (item.Asset == null)
|
||||
{
|
||||
Logger.Warning($"Dump is not supported for \"{item.Text}\" ({item.TypeString}) file");
|
||||
return false;
|
||||
}
|
||||
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath))
|
||||
return false;
|
||||
var str = item.Asset.Dump();
|
||||
@@ -312,9 +394,9 @@ namespace AssetStudioCLI
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath)
|
||||
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath, string alias = "")
|
||||
{
|
||||
var fileName = FixFileName(item.Text);
|
||||
var fileName = FixFileName(item.Text) + alias;
|
||||
fullPath = Path.Combine(dir, fileName + extension);
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
@@ -450,6 +532,8 @@ namespace AssetStudioCLI
|
||||
return ExportFont(item, exportPath);
|
||||
case ClassIDType.Sprite:
|
||||
return ExportSprite(item, exportPath);
|
||||
case ClassIDType.AkPortraitSprite:
|
||||
return ExportPortraitSprite(item, exportPath);
|
||||
case ClassIDType.Mesh:
|
||||
return ExportMesh(item, exportPath);
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using AssetStudio;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SixLabors.ImageSharp.Processing.Processors.Transforms;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -15,6 +17,7 @@ namespace AssetStudioCLI.Options
|
||||
Live2D,
|
||||
FBX,
|
||||
Filter,
|
||||
Arknights,
|
||||
Advanced,
|
||||
}
|
||||
|
||||
@@ -59,6 +62,13 @@ namespace AssetStudioCLI.Options
|
||||
NameAndContainer,
|
||||
}
|
||||
|
||||
internal enum AkSpriteAlphaMode
|
||||
{
|
||||
None,
|
||||
InternalOnly,
|
||||
SearchExternal
|
||||
}
|
||||
|
||||
internal static class CLIOptions
|
||||
{
|
||||
public static bool isParsed;
|
||||
@@ -95,6 +105,14 @@ namespace AssetStudioCLI.Options
|
||||
public static Option<List<string>> o_filterByContainer;
|
||||
public static Option<List<string>> o_filterByPathID;
|
||||
public static Option<List<string>> o_filterByText;
|
||||
//arknights
|
||||
public static bool akResizedOnly;
|
||||
public static Option<AkSpriteAlphaMode> o_akSpriteAlphaMode;
|
||||
public static Option<IResampler> o_akAlphaTexResampler;
|
||||
private static string resamplerName;
|
||||
public static Option<int> o_akShadowGamma;
|
||||
public static Option<bool> f_akOriginalAvgNames;
|
||||
public static Option<bool> f_akAddAliases;
|
||||
//advanced
|
||||
public static Option<ExportListType> o_exportAssetList;
|
||||
public static Option<string> o_assemblyPath;
|
||||
@@ -149,6 +167,7 @@ namespace AssetStudioCLI.Options
|
||||
{
|
||||
ClassIDType.Texture2D,
|
||||
ClassIDType.Sprite,
|
||||
ClassIDType.AkPortraitSprite,
|
||||
ClassIDType.TextAsset,
|
||||
ClassIDType.MonoBehaviour,
|
||||
ClassIDType.Font,
|
||||
@@ -181,8 +200,8 @@ namespace AssetStudioCLI.Options
|
||||
optionDefaultValue: exportableAssetTypes,
|
||||
optionName: "-t, --asset-type <value(s)>",
|
||||
optionDescription: "Specify asset type(s) to export\n" +
|
||||
"<Value(s): tex2d, sprite, textAsset, monoBehaviour, font, shader, movieTexture,\n" +
|
||||
"audio, video, mesh | all(default)>\n" +
|
||||
"<Value(s): tex2d, sprite, akPortrait, textAsset, monoBehaviour, font, shader,\n" +
|
||||
"movieTexture, audio, video, mesh | all(default)>\n" +
|
||||
"All - export all asset types, which are listed in the values\n" +
|
||||
"*To specify multiple asset types, write them separated by ',' or ';' without spaces\n" +
|
||||
"Examples: \"-t sprite\" or \"-t tex2d,sprite,audio\" or \"-t tex2d;sprite;font\"\n",
|
||||
@@ -348,6 +367,65 @@ namespace AssetStudioCLI.Options
|
||||
optionHelpGroup: HelpGroups.Filter
|
||||
);
|
||||
#endregion
|
||||
|
||||
#region Init Arknights Options
|
||||
akResizedOnly = true;
|
||||
o_akSpriteAlphaMode = new GroupedOption<AkSpriteAlphaMode>
|
||||
(
|
||||
optionDefaultValue: AkSpriteAlphaMode.SearchExternal,
|
||||
optionName: "--spritealpha-mode <value>",
|
||||
optionDescription: "Specify the mode in which you want to export sprites with alpha texture\n" +
|
||||
"<Value: none | internalOnly | searchExternal(default)>\n" +
|
||||
"None - Export sprites without alpha texture applied\n" +
|
||||
"InternalOnly - Export sprites with internal alpha texture applied (if exist)\n" +
|
||||
"SearchExternal - Export sprites with internal alpha texture applied,\n" +
|
||||
"and in case it doesn't exist, Studio will try to find an external alpha texture\n" +
|
||||
"Example: \"--spritealpha-mode internalOnly\"\n",
|
||||
optionHelpGroup: HelpGroups.Arknights
|
||||
);
|
||||
o_akAlphaTexResampler = new GroupedOption<IResampler>
|
||||
(
|
||||
optionDefaultValue: KnownResamplers.MitchellNetravali,
|
||||
optionName: "--alphatex-resampler <value>",
|
||||
optionDescription: "Specify the alpha texture upscale algorithm for 2048x2048 sprites\n" +
|
||||
"<Value: nearest | bilinear | bicubic | mitchell(default) | spline | welch>\n" +
|
||||
"Mitchell - Mitchell Netravali algorithm. Yields good equilibrium between \n" +
|
||||
"sharpness and smoothness (produces less artifacts than bicubic in the current use case)\n" +
|
||||
"Spline - Similar to Mitchell Netravali but yielding smoother results\n" +
|
||||
"Welch - A high speed algorithm that delivers very sharpened results\n" +
|
||||
"Example: \"--alphatex-resampler bicubic\"\n",
|
||||
optionHelpGroup: HelpGroups.Arknights
|
||||
);
|
||||
resamplerName = "Mitchell";
|
||||
o_akShadowGamma = new GroupedOption<int>
|
||||
(
|
||||
optionDefaultValue: 2,
|
||||
optionName: "--shadow-gamma <value>",
|
||||
optionDescription: "Specify the gamma correction of semi-transparent shadow for 2048x2048 sprites\n" +
|
||||
"<Value: integer number from -5 to 5 (default=2)>\n" +
|
||||
"<0 - Make the shadow darker\n" +
|
||||
"0 - Do not change the brightness of the shadow\n" +
|
||||
">0 - Make the shadow lighter\n" +
|
||||
"Example: \"--shadow-gamma 0\"\n",
|
||||
optionHelpGroup: HelpGroups.Arknights
|
||||
);
|
||||
f_akOriginalAvgNames = new GroupedOption<bool>
|
||||
(
|
||||
optionDefaultValue: false,
|
||||
optionName: "--original-avg-names",
|
||||
optionDescription: "(Flag) If specified, names of avg character sprites will not be restored\n",
|
||||
optionHelpGroup: HelpGroups.Arknights,
|
||||
isFlag: true
|
||||
);
|
||||
f_akAddAliases = new GroupedOption<bool>
|
||||
(
|
||||
optionDefaultValue: false,
|
||||
optionName: "--add-aliases",
|
||||
optionDescription: "(Flag) If specified, aliases will be added to avg character sprite names (if exist)",
|
||||
optionHelpGroup: HelpGroups.Arknights,
|
||||
isFlag: true
|
||||
);
|
||||
#endregion
|
||||
|
||||
#region Init Advanced Options
|
||||
o_exportAssetList = new GroupedOption<ExportListType>
|
||||
@@ -378,7 +456,7 @@ namespace AssetStudioCLI.Options
|
||||
(
|
||||
optionDefaultValue: false,
|
||||
optionName: "--not-restore-extension",
|
||||
optionDescription: "(Flag) If specified, AssetStudio will not try to use/restore original TextAsset\nextension name, and will just export all TextAssets with the \".txt\" extension\n",
|
||||
optionDescription: "(Flag) If specified, Studio will not try to use/restore original TextAsset\nextension name, and will just export all TextAssets with the \".txt\" extension\n",
|
||||
optionHelpGroup: HelpGroups.Advanced,
|
||||
isFlag: true
|
||||
);
|
||||
@@ -386,7 +464,7 @@ namespace AssetStudioCLI.Options
|
||||
(
|
||||
optionDefaultValue: false,
|
||||
optionName: "--load-all",
|
||||
optionDescription: "(Flag) If specified, AssetStudio will load assets of all types\n(Only for Dump, Info and ExportRaw modes)",
|
||||
optionDescription: "(Flag) If specified, Studio will load assets of all types\n(Only for Dump, Info and ExportRaw modes)",
|
||||
optionHelpGroup: HelpGroups.Advanced,
|
||||
isFlag: true
|
||||
);
|
||||
@@ -526,6 +604,14 @@ namespace AssetStudioCLI.Options
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "--original-avg-names":
|
||||
f_akOriginalAvgNames.Value = true;
|
||||
resplittedArgs.RemoveAt(i);
|
||||
break;
|
||||
case "--add-aliases":
|
||||
f_akAddAliases.Value = true;
|
||||
resplittedArgs.RemoveAt(i);
|
||||
break;
|
||||
case "--l2d-force-bezier":
|
||||
if (o_workMode.Value != WorkMode.ExportLive2D)
|
||||
{
|
||||
@@ -565,6 +651,9 @@ namespace AssetStudioCLI.Options
|
||||
case "tex2d":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.Texture2D);
|
||||
break;
|
||||
case "akportrait":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.AkPortraitSprite);
|
||||
break;
|
||||
case "audio":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.AudioClip);
|
||||
break;
|
||||
@@ -757,46 +846,35 @@ namespace AssetStudioCLI.Options
|
||||
}
|
||||
break;
|
||||
case "--fbx-scale-factor":
|
||||
var isFloat = float.TryParse(value, out float floatValue);
|
||||
if (isFloat && floatValue >= 0 && floatValue <= 100)
|
||||
{
|
||||
o_fbxScaleFactor.Value = floatValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported scale factor value: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_fbxScaleFactor.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "--fbx-bone-size":
|
||||
var isInt = int.TryParse(value, out int intValue);
|
||||
if (isInt && intValue >= 0 && intValue <= 100)
|
||||
{
|
||||
o_fbxBoneSize.Value = intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported bone size value: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_fbxBoneSize.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "--export-asset-list":
|
||||
switch (value.ToLower())
|
||||
{
|
||||
case "xml":
|
||||
o_exportAssetList.Value = ExportListType.XML;
|
||||
break;
|
||||
case "none":
|
||||
o_exportAssetList.Value = ExportListType.None;
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported asset list export option: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_exportAssetList.Description);
|
||||
var isFloat = float.TryParse(value, out float floatValue);
|
||||
if (isFloat && floatValue >= 0 && floatValue <= 100)
|
||||
{
|
||||
o_fbxScaleFactor.Value = floatValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported scale factor value: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_fbxScaleFactor.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "--fbx-bone-size":
|
||||
{
|
||||
var isInt = int.TryParse(value, out int intValue);
|
||||
if (isInt && intValue >= 0 && intValue <= 100)
|
||||
{
|
||||
o_fbxBoneSize.Value = intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported bone size value: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_fbxBoneSize.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "--filter-by-name":
|
||||
o_filterByName.Value.AddRange(ValueSplitter(value));
|
||||
filterBy = filterBy == FilterBy.None ? FilterBy.Name : filterBy == FilterBy.Container ? FilterBy.NameAndContainer : filterBy;
|
||||
@@ -813,6 +891,82 @@ namespace AssetStudioCLI.Options
|
||||
o_filterByText.Value.AddRange(ValueSplitter(value));
|
||||
filterBy = FilterBy.NameOrContainer;
|
||||
break;
|
||||
case "--spritealpha-mode":
|
||||
switch (value.ToLower())
|
||||
{
|
||||
case "none":
|
||||
o_akSpriteAlphaMode.Value = AkSpriteAlphaMode.None;
|
||||
break;
|
||||
case "internalonly":
|
||||
o_akSpriteAlphaMode.Value = AkSpriteAlphaMode.InternalOnly;
|
||||
break;
|
||||
case "searchexternal":
|
||||
o_akSpriteAlphaMode.Value = AkSpriteAlphaMode.SearchExternal;
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported sprite alpha mode: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_akSpriteAlphaMode.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "--alphatex-resampler":
|
||||
switch (value.ToLower())
|
||||
{
|
||||
case "nearest":
|
||||
o_akAlphaTexResampler.Value = KnownResamplers.NearestNeighbor;
|
||||
break;
|
||||
case "bilinear":
|
||||
o_akAlphaTexResampler.Value = KnownResamplers.Triangle;
|
||||
break;
|
||||
case "bicubic":
|
||||
o_akAlphaTexResampler.Value = KnownResamplers.Bicubic;
|
||||
break;
|
||||
case "mitchell":
|
||||
o_akAlphaTexResampler.Value = KnownResamplers.MitchellNetravali;
|
||||
break;
|
||||
case "spline":
|
||||
o_akAlphaTexResampler.Value = KnownResamplers.Spline;
|
||||
break;
|
||||
case "welch":
|
||||
o_akAlphaTexResampler.Value = KnownResamplers.Welch;
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported alpha texture resampler: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_akAlphaTexResampler.Description);
|
||||
return;
|
||||
}
|
||||
resamplerName = value.ToLower();
|
||||
break;
|
||||
case "--shadow-gamma":
|
||||
{
|
||||
var isInt = int.TryParse(value, out int intValue);
|
||||
if (isInt && intValue >= -5 && intValue <= 5)
|
||||
{
|
||||
o_akShadowGamma.Value = intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported gamma correction value: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_akShadowGamma.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "--export-asset-list":
|
||||
switch (value.ToLower())
|
||||
{
|
||||
case "xml":
|
||||
o_exportAssetList.Value = ExportListType.XML;
|
||||
break;
|
||||
case "none":
|
||||
o_exportAssetList.Value = ExportListType.None;
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported asset list export option: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_exportAssetList.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "--assembly-folder":
|
||||
if (Directory.Exists(value))
|
||||
{
|
||||
@@ -943,7 +1097,7 @@ namespace AssetStudioCLI.Options
|
||||
else
|
||||
{
|
||||
var arch = Environment.Is64BitProcess ? "x64" : "x32";
|
||||
Console.WriteLine($"# {appAssembly.Name} [{arch}]\n# Based on AssetStudioMod v{appAssembly.Version}\n");
|
||||
Console.WriteLine($"# {appAssembly.Name} [{arch}]\n# v{appAssembly.Version}\n# Based on AssetStudioMod v0.17.4\n");
|
||||
Console.WriteLine($"{usage}\n\n{helpMessage}");
|
||||
}
|
||||
}
|
||||
@@ -1004,7 +1158,12 @@ namespace AssetStudioCLI.Options
|
||||
{
|
||||
sb.AppendLine($"# Export Image Format: {o_imageFormat}");
|
||||
sb.AppendLine($"# Export Audio Format: {o_audioFormat}");
|
||||
sb.AppendLine($"# [Arkingths] Sprite Alpha Mode: {o_akSpriteAlphaMode}");
|
||||
sb.AppendLine($"# [Arknights] Alpha Texture Resampler: {resamplerName}");
|
||||
sb.AppendLine($"# [Arknights] Shadow Gamma Correction: {o_akShadowGamma.Value * 10:+#;-#;0}%");
|
||||
}
|
||||
sb.AppendLine($"# [Arknights] Don't Fix Avg Names: {f_akOriginalAvgNames}");
|
||||
sb.AppendLine($"# [Arknights] Add Aliases: {f_akAddAliases}");
|
||||
sb.AppendLine($"# Log Level: {o_logLevel}");
|
||||
sb.AppendLine($"# Log Output: {o_logOutput}");
|
||||
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
## AssetStudioModCLI
|
||||
CLI version of AssetStudioMod.
|
||||
- Supported asset types for export: `Texture2D`, `Sprite`, `TextAsset`, `MonoBehaviour`, `Font`, `Shader`, `MovieTexture`, `AudioClip`, `VideoClip`, `Mesh`.
|
||||
## ArknightsStudioCLI
|
||||
CLI version of ArknightsStudio.
|
||||
- Supported asset types for export: `Texture2D`, `Sprite`, `AkPortraitSprite`, `TextAsset`, `MonoBehaviour`, `Font`, `Shader`, `MovieTexture`, `AudioClip`, `VideoClip`, `Mesh`.
|
||||
- *There are no plans to add support for `AnimationClip`, `Animator` for now.*
|
||||
|
||||
### Usage
|
||||
```
|
||||
AssetStudioModCLI <input path to asset file/folder> [-m, --mode <value>]
|
||||
ArknightsStudioCLI <input path to asset file/folder> [-m, --mode <value>]
|
||||
[-t, --asset-type <value(s)>] [-g, --group-option <value>]
|
||||
[-o, --output <path>] [-h, --help]
|
||||
[--log-level <value>] [--log-output <value>]
|
||||
@@ -14,9 +14,12 @@ AssetStudioModCLI <input path to asset file/folder> [-m, --mode <value>]
|
||||
[--fbx-scale-factor <value>] [--fbx-bone-size <value>]
|
||||
[--filter-by-name <text>] [--filter-by-container <text>]
|
||||
[--filter-by-pathid <text>] [--filter-by-text <text>]
|
||||
[--export-asset-list <value>] [--assembly-folder <path>]
|
||||
[--unity-version <text>] [--not-restore-extension]
|
||||
[--load-all]
|
||||
[--spritealpha-mode <value>] [--alphatex-resampler <value>]
|
||||
[--shadow-gamma <value>] [--original-avg-names]
|
||||
[--add-aliases] [--export-asset-list <value>]
|
||||
[--assembly-folder <path>] [--unity-version <text>]
|
||||
[--not-restore-extension] [--load-all]
|
||||
|
||||
|
||||
General Options:
|
||||
-m, --mode <value> Specify working mode
|
||||
@@ -30,8 +33,8 @@ General Options:
|
||||
Example: "-m info"
|
||||
|
||||
-t, --asset-type <value(s)> Specify asset type(s) to export
|
||||
<Value(s): tex2d, sprite, textAsset, monoBehaviour, font, shader, movieTexture,
|
||||
audio, video, mesh | all(default)>
|
||||
<Value(s): tex2d, sprite, akPortrait, textAsset, monoBehaviour, font, shader,
|
||||
movieTexture, audio, video, mesh | all(default)>
|
||||
All - export all asset types, which are listed in the values
|
||||
*To specify multiple asset types, write them separated by ',' or ';' without spaces
|
||||
Examples: "-t sprite" or "-t tex2d,sprite,audio" or "-t tex2d;sprite;font"
|
||||
@@ -109,6 +112,34 @@ Filter Options:
|
||||
Example: "--filter-by-text portrait" or "--filter-by-text portrait,art"
|
||||
|
||||
|
||||
Arknights Options:
|
||||
--spritealpha-mode <value> Specify the mode in which you want to export sprites with alpha texture
|
||||
<Value: none | internalOnly | searchExternal(default)>
|
||||
None - Export sprites without alpha texture applied
|
||||
InternalOnly - Export sprites with internal alpha texture applied (if exist)
|
||||
SearchExternal - Export sprites with internal alpha texture applied,
|
||||
and in case it doesn't exist, Studio will try to find an external alpha texture
|
||||
Example: "--spritealpha-mode internalOnly"
|
||||
|
||||
--alphatex-resampler <value> Specify the alpha texture upscale algorithm for 2048x2048 sprites
|
||||
<Value: nearest | bilinear | bicubic | mitchell(default) | spline | welch>
|
||||
Mitchell - Mitchell Netravali algorithm. Yields good equilibrium between
|
||||
sharpness and smoothness (produces less artifacts than bicubic in the current use case)
|
||||
Spline - Similar to Mitchell Netravali but yielding smoother results
|
||||
Welch - A high speed algorithm that delivers very sharpened results
|
||||
Example: "--alphatex-resampler bicubic"
|
||||
|
||||
--shadow-gamma <value> Specify the gamma correction of semi-transparent shadow for 2048x2048 sprites
|
||||
<Value: integer number from -5 to 5 (default=2)>
|
||||
<0 - Make the shadow darker
|
||||
0 - Do not change the brightness of the shadow
|
||||
>0 - Make the shadow lighter
|
||||
Example: "--shadow-gamma 0"
|
||||
|
||||
--original-avg-names (Flag) If specified, names of avg character sprites will not be restored
|
||||
|
||||
--add-aliases (Flag) If specified, aliases will be added to avg character sprite names (if exist)
|
||||
|
||||
Advanced Options:
|
||||
--export-asset-list <value> Specify the format in which you want to export asset list
|
||||
<Value: none(default) | xml>
|
||||
@@ -120,9 +151,9 @@ Advanced Options:
|
||||
--unity-version <text> Specify Unity version
|
||||
Example: "--unity-version 2017.4.39f1"
|
||||
|
||||
--not-restore-extension (Flag) If specified, AssetStudio will not try to use/restore original TextAsset
|
||||
--not-restore-extension (Flag) If specified, Studio will not try to use/restore original TextAsset
|
||||
extension name, and will just export all TextAssets with the ".txt" extension
|
||||
|
||||
--load-all (Flag) If specified, AssetStudio will load assets of all types
|
||||
--load-all (Flag) If specified, Studio will load assets of all types
|
||||
(Only for Dump, Info and ExportRaw modes)
|
||||
```
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace AssetStudioCLI
|
||||
internal static class Studio
|
||||
{
|
||||
public static AssetsManager assetsManager = new AssetsManager();
|
||||
public static List<AssetItem> parsedAssetsList = new List<AssetItem>();
|
||||
public static List<AssetItem> exportableAssetsList = new List<AssetItem>();
|
||||
public static List<AssetItem> loadedAssetsList = new List<AssetItem>();
|
||||
public static List<BaseNode> gameObjectTree = new List<BaseNode>();
|
||||
public static AssemblyLoader assemblyLoader = new AssemblyLoader();
|
||||
private static Dictionary<AssetStudio.Object, string> containers = new Dictionary<AssetStudio.Object, string>();
|
||||
@@ -54,7 +55,6 @@ namespace AssetStudioCLI
|
||||
{
|
||||
Logger.Info("Parse assets...");
|
||||
|
||||
var fileAssetsList = new List<AssetItem>();
|
||||
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
|
||||
var objectAssetItemDic = new Dictionary<AssetStudio.Object, AssetItem>(objectCount);
|
||||
|
||||
@@ -152,23 +152,31 @@ namespace AssetStudioCLI
|
||||
assetItem.Text = assetItem.TypeString + assetItem.UniqueID;
|
||||
}
|
||||
|
||||
loadedAssetsList.Add(assetItem);
|
||||
isExportable = CLIOptions.o_exportAssetTypes.Value.Contains(asset.type);
|
||||
if (isExportable || (CLIOptions.f_loadAllAssets.Value && CLIOptions.o_exportAssetTypes.Value == CLIOptions.o_exportAssetTypes.DefaultValue))
|
||||
{
|
||||
fileAssetsList.Add(assetItem);
|
||||
exportableAssetsList.Add(assetItem);
|
||||
}
|
||||
|
||||
Progress.Report(++i, objectCount);
|
||||
}
|
||||
foreach (var asset in fileAssetsList)
|
||||
foreach (var asset in loadedAssetsList)
|
||||
{
|
||||
if (containers.TryGetValue(asset.Asset, out var container))
|
||||
{
|
||||
asset.Container = container;
|
||||
|
||||
if (asset.Type == ClassIDType.MonoBehaviour && container.Contains("/arts/charportraits/portraits"))
|
||||
{
|
||||
var portraitsList = Arknights.AkSpriteHelper.GeneratePortraits(asset);
|
||||
foreach (var portrait in portraitsList)
|
||||
{
|
||||
exportableAssetsList.Add(new AssetItem(portrait));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parsedAssetsList.AddRange(fileAssetsList);
|
||||
fileAssetsList.Clear();
|
||||
if (CLIOptions.o_workMode.Value != WorkMode.ExportLive2D)
|
||||
{
|
||||
containers.Clear();
|
||||
@@ -179,8 +187,7 @@ namespace AssetStudioCLI
|
||||
{
|
||||
BuildTreeStructure(objectAssetItemDic);
|
||||
}
|
||||
|
||||
var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {parsedAssetsList.Count} exportable assets";
|
||||
var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {exportableAssetsList.Count} exportable assets";
|
||||
var unityVer = assetsManager.assetsFileList[0].version;
|
||||
long m_ObjectsCount;
|
||||
if (unityVer[0] > 2020)
|
||||
@@ -285,9 +292,9 @@ namespace AssetStudioCLI
|
||||
{
|
||||
var exportableAssetsCountDict = new Dictionary<ClassIDType, int>();
|
||||
string info = "";
|
||||
if (parsedAssetsList.Count > 0)
|
||||
if (exportableAssetsList.Count > 0)
|
||||
{
|
||||
foreach (var asset in parsedAssetsList)
|
||||
foreach (var asset in exportableAssetsList)
|
||||
{
|
||||
if (exportableAssetsCountDict.ContainsKey(asset.Type))
|
||||
{
|
||||
@@ -306,7 +313,7 @@ namespace AssetStudioCLI
|
||||
}
|
||||
if (exportableAssetsCountDict.Count > 1)
|
||||
{
|
||||
info += $"#\n# Total: {parsedAssetsList.Count} assets";
|
||||
info += $"#\n# Total: {exportableAssetsList.Count} assets";
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -339,34 +346,34 @@ namespace AssetStudioCLI
|
||||
|
||||
private static void FilterAssets()
|
||||
{
|
||||
var assetsCount = parsedAssetsList.Count;
|
||||
var assetsCount = exportableAssetsList.Count;
|
||||
var filteredAssets = new List<AssetItem>();
|
||||
|
||||
switch(CLIOptions.filterBy)
|
||||
{
|
||||
case FilterBy.Name:
|
||||
filteredAssets = parsedAssetsList.FindAll(x => CLIOptions.o_filterByName.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByName.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
Logger.Info(
|
||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
|
||||
);
|
||||
break;
|
||||
case FilterBy.Container:
|
||||
filteredAssets = parsedAssetsList.FindAll(x => CLIOptions.o_filterByContainer.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByContainer.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
Logger.Info(
|
||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers."
|
||||
);
|
||||
break;
|
||||
case FilterBy.PathID:
|
||||
filteredAssets = parsedAssetsList.FindAll(x => CLIOptions.o_filterByPathID.Value.Any(y => x.m_PathID.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByPathID.Value.Any(y => x.m_PathID.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
Logger.Info(
|
||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByPathID.Value)}\"".Color(Ansi.BrightYellow)} in their PathIDs."
|
||||
);
|
||||
break;
|
||||
case FilterBy.NameOrContainer:
|
||||
filteredAssets = parsedAssetsList.FindAll(x =>
|
||||
filteredAssets = exportableAssetsList.FindAll(x =>
|
||||
CLIOptions.o_filterByText.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
CLIOptions.o_filterByText.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
);
|
||||
@@ -376,7 +383,7 @@ namespace AssetStudioCLI
|
||||
);
|
||||
break;
|
||||
case FilterBy.NameAndContainer:
|
||||
filteredAssets = parsedAssetsList.FindAll(x =>
|
||||
filteredAssets = exportableAssetsList.FindAll(x =>
|
||||
CLIOptions.o_filterByName.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) &&
|
||||
CLIOptions.o_filterByContainer.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
);
|
||||
@@ -387,18 +394,18 @@ namespace AssetStudioCLI
|
||||
);
|
||||
break;
|
||||
}
|
||||
parsedAssetsList.Clear();
|
||||
parsedAssetsList = filteredAssets;
|
||||
exportableAssetsList.Clear();
|
||||
exportableAssetsList = filteredAssets;
|
||||
}
|
||||
|
||||
public static void ExportAssets()
|
||||
{
|
||||
var savePath = CLIOptions.o_outputFolder.Value;
|
||||
var toExportCount = parsedAssetsList.Count;
|
||||
var toExportCount = exportableAssetsList.Count;
|
||||
var exportedCount = 0;
|
||||
|
||||
var groupOption = CLIOptions.o_groupAssetsBy.Value;
|
||||
foreach (var asset in parsedAssetsList)
|
||||
foreach (var asset in exportableAssetsList)
|
||||
{
|
||||
string exportPath;
|
||||
switch (groupOption)
|
||||
@@ -503,7 +510,7 @@ namespace AssetStudioCLI
|
||||
new XElement("Assets",
|
||||
new XAttribute("filename", filename),
|
||||
new XAttribute("createdAt", DateTime.UtcNow.ToString("s")),
|
||||
parsedAssetsList.Select(
|
||||
exportableAssetsList.Select(
|
||||
asset => new XElement("Asset",
|
||||
new XElement("Name", asset.Text),
|
||||
new XElement("Container", asset.Container),
|
||||
@@ -519,7 +526,7 @@ namespace AssetStudioCLI
|
||||
|
||||
break;
|
||||
}
|
||||
Logger.Info($"Finished exporting asset list with {parsedAssetsList.Count} items.");
|
||||
Logger.Info($"Finished exporting asset list with {exportableAssetsList.Count} items.");
|
||||
}
|
||||
|
||||
public static void ExportSplitObjects()
|
||||
@@ -627,7 +634,7 @@ namespace AssetStudioCLI
|
||||
Progress.Reset();
|
||||
Logger.Info($"Searching for Live2D files...");
|
||||
|
||||
var cubismMocs = parsedAssetsList.Where(x =>
|
||||
var cubismMocs = exportableAssetsList.Where(x =>
|
||||
{
|
||||
if (x.Type == ClassIDType.MonoBehaviour)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>1.1.0</Version>
|
||||
<Copyright>Copyright © Perfare 2018-2022; Copyright © hozuki 2020</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
53
AssetStudioGUI/AboutForm.Designer.cs
generated
53
AssetStudioGUI/AboutForm.Designer.cs
generated
@@ -49,7 +49,7 @@
|
||||
this.label7 = new System.Windows.Forms.Label();
|
||||
this.modVersionLabel = new System.Windows.Forms.Label();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.label8 = new System.Windows.Forms.Label();
|
||||
this.basedOnLabel = new System.Windows.Forms.Label();
|
||||
this.checkUpdatesLinkLabel = new System.Windows.Forms.LinkLabel();
|
||||
this.tabPage2 = new System.Windows.Forms.TabPage();
|
||||
this.licenseRichTextBox = new System.Windows.Forms.RichTextBox();
|
||||
@@ -116,8 +116,7 @@
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(347, 46);
|
||||
this.label2.TabIndex = 0;
|
||||
this.label2.Text = "AssetStudio is a tool for exploring, extracting, and exporting assets and asset b" +
|
||||
"undles.";
|
||||
this.label2.Text = "ArknightsStudio is a modified version of AssetStudio designed for Arknights.";
|
||||
this.label2.UseCompatibleTextRendering = true;
|
||||
//
|
||||
// textBox2
|
||||
@@ -164,7 +163,7 @@
|
||||
this.tableLayoutPanel2.ColumnCount = 3;
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 41.37931F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 58.62069F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 111F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 113F));
|
||||
this.tableLayoutPanel2.Controls.Add(this.label16, 0, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.label17, 1, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.gitPerfareLinkLabel, 2, 0);
|
||||
@@ -194,7 +193,7 @@
|
||||
//
|
||||
this.label17.AutoSize = true;
|
||||
this.label17.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label17.Location = new System.Drawing.Point(102, 2);
|
||||
this.label17.Location = new System.Drawing.Point(101, 2);
|
||||
this.label17.Name = "label17";
|
||||
this.label17.Size = new System.Drawing.Size(110, 13);
|
||||
this.label17.TabIndex = 10;
|
||||
@@ -204,7 +203,7 @@
|
||||
//
|
||||
this.gitPerfareLinkLabel.AutoSize = true;
|
||||
this.gitPerfareLinkLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.gitPerfareLinkLabel.Location = new System.Drawing.Point(239, 2);
|
||||
this.gitPerfareLinkLabel.Location = new System.Drawing.Point(237, 2);
|
||||
this.gitPerfareLinkLabel.Name = "gitPerfareLinkLabel";
|
||||
this.gitPerfareLinkLabel.Size = new System.Drawing.Size(67, 13);
|
||||
this.gitPerfareLinkLabel.TabIndex = 11;
|
||||
@@ -226,7 +225,7 @@
|
||||
//
|
||||
this.label19.AutoSize = true;
|
||||
this.label19.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label19.Location = new System.Drawing.Point(102, 20);
|
||||
this.label19.Location = new System.Drawing.Point(101, 20);
|
||||
this.label19.Name = "label19";
|
||||
this.label19.Size = new System.Drawing.Size(113, 13);
|
||||
this.label19.TabIndex = 13;
|
||||
@@ -236,7 +235,7 @@
|
||||
//
|
||||
this.gitAelurumLinkLabel.AutoSize = true;
|
||||
this.gitAelurumLinkLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.gitAelurumLinkLabel.Location = new System.Drawing.Point(239, 20);
|
||||
this.gitAelurumLinkLabel.Location = new System.Drawing.Point(237, 20);
|
||||
this.gitAelurumLinkLabel.Name = "gitAelurumLinkLabel";
|
||||
this.gitAelurumLinkLabel.Size = new System.Drawing.Size(67, 13);
|
||||
this.gitAelurumLinkLabel.TabIndex = 14;
|
||||
@@ -250,13 +249,13 @@
|
||||
this.tableLayoutPanel1.ColumnCount = 3;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 41.37931F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 58.62069F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 111F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 113F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.label5, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.productNamelabel, 1, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label7, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.modVersionLabel, 1, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label4, 0, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label8, 1, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.basedOnLabel, 1, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.checkUpdatesLinkLabel, 2, 1);
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 80);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
@@ -285,11 +284,11 @@
|
||||
//
|
||||
this.productNamelabel.AutoSize = true;
|
||||
this.productNamelabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.productNamelabel.Location = new System.Drawing.Point(102, 2);
|
||||
this.productNamelabel.Location = new System.Drawing.Point(101, 2);
|
||||
this.productNamelabel.Name = "productNamelabel";
|
||||
this.productNamelabel.Size = new System.Drawing.Size(103, 13);
|
||||
this.productNamelabel.Size = new System.Drawing.Size(100, 13);
|
||||
this.productNamelabel.TabIndex = 1;
|
||||
this.productNamelabel.Text = "AssetStudioModGUI";
|
||||
this.productNamelabel.Text = "ArknightsStudioGUI";
|
||||
//
|
||||
// label7
|
||||
//
|
||||
@@ -297,16 +296,16 @@
|
||||
this.label7.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label7.Location = new System.Drawing.Point(5, 20);
|
||||
this.label7.Name = "label7";
|
||||
this.label7.Size = new System.Drawing.Size(68, 13);
|
||||
this.label7.Size = new System.Drawing.Size(45, 13);
|
||||
this.label7.TabIndex = 2;
|
||||
this.label7.Text = "Mod version:";
|
||||
this.label7.Text = "Version:";
|
||||
//
|
||||
// modVersionLabel
|
||||
//
|
||||
this.modVersionLabel.AutoSize = true;
|
||||
this.modVersionLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.modVersionLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.modVersionLabel.Location = new System.Drawing.Point(102, 20);
|
||||
this.modVersionLabel.Location = new System.Drawing.Point(101, 20);
|
||||
this.modVersionLabel.Name = "modVersionLabel";
|
||||
this.modVersionLabel.Size = new System.Drawing.Size(52, 13);
|
||||
this.modVersionLabel.TabIndex = 3;
|
||||
@@ -323,21 +322,21 @@
|
||||
this.label4.TabIndex = 4;
|
||||
this.label4.Text = "Based on:";
|
||||
//
|
||||
// label8
|
||||
// basedOnLabel
|
||||
//
|
||||
this.label8.AutoSize = true;
|
||||
this.label8.BackColor = System.Drawing.Color.Transparent;
|
||||
this.label8.Location = new System.Drawing.Point(102, 38);
|
||||
this.label8.Name = "label8";
|
||||
this.label8.Size = new System.Drawing.Size(108, 13);
|
||||
this.label8.TabIndex = 5;
|
||||
this.label8.Text = "AssetStudio v0.16.47";
|
||||
this.basedOnLabel.AutoSize = true;
|
||||
this.basedOnLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.basedOnLabel.Location = new System.Drawing.Point(101, 38);
|
||||
this.basedOnLabel.Name = "basedOnLabel";
|
||||
this.basedOnLabel.Size = new System.Drawing.Size(123, 13);
|
||||
this.basedOnLabel.TabIndex = 5;
|
||||
this.basedOnLabel.Text = "AssetStudioMod v0.17.0";
|
||||
//
|
||||
// checkUpdatesLinkLabel
|
||||
//
|
||||
this.checkUpdatesLinkLabel.AutoSize = true;
|
||||
this.checkUpdatesLinkLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.checkUpdatesLinkLabel.Location = new System.Drawing.Point(239, 20);
|
||||
this.checkUpdatesLinkLabel.Location = new System.Drawing.Point(237, 20);
|
||||
this.checkUpdatesLinkLabel.Name = "checkUpdatesLinkLabel";
|
||||
this.checkUpdatesLinkLabel.Size = new System.Drawing.Size(96, 13);
|
||||
this.checkUpdatesLinkLabel.TabIndex = 6;
|
||||
@@ -391,7 +390,7 @@
|
||||
this.productTitleLabel.Name = "productTitleLabel";
|
||||
this.productTitleLabel.Size = new System.Drawing.Size(384, 30);
|
||||
this.productTitleLabel.TabIndex = 1;
|
||||
this.productTitleLabel.Text = "AssetStudioModGUI";
|
||||
this.productTitleLabel.Text = "ArknightsStudioGUI";
|
||||
this.productTitleLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
//
|
||||
// CloseButton
|
||||
@@ -492,7 +491,7 @@
|
||||
private System.Windows.Forms.Label label7;
|
||||
private System.Windows.Forms.Label modVersionLabel;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.Label label8;
|
||||
private System.Windows.Forms.Label basedOnLabel;
|
||||
private System.Windows.Forms.LinkLabel checkUpdatesLinkLabel;
|
||||
private System.Windows.Forms.RichTextBox licenseRichTextBox;
|
||||
private System.Windows.Forms.TextBox textBox2;
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace AssetStudioGUI
|
||||
productVersionLabel.Text = $"v{productVer} [{arch}]";
|
||||
productNamelabel.Text = productName;
|
||||
modVersionLabel.Text = productVer;
|
||||
basedOnLabel.Text = "AssetStudioMod v0.17.4";
|
||||
|
||||
licenseRichTextBox.Text = GetLicenseText();
|
||||
}
|
||||
@@ -43,7 +44,7 @@ namespace AssetStudioGUI
|
||||
|
||||
private void checkUpdatesLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
||||
{
|
||||
var ps = new ProcessStartInfo("https://github.com/aelurum/AssetStudio/releases")
|
||||
var ps = new ProcessStartInfo("https://github.com/aelurum/AssetStudio/tags")
|
||||
{
|
||||
UseShellExecute = true
|
||||
};
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<TargetFrameworks>net472;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ApplicationIcon>Resources\as.ico</ApplicationIcon>
|
||||
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle>
|
||||
<AssemblyName>AssetStudioModGUI</AssemblyName>
|
||||
<Version>0.17.4.0</Version>
|
||||
<AssemblyTitle>ArknightsStudio by aelurum</AssemblyTitle>
|
||||
<AssemblyName>ArknightsStudioGUI</AssemblyName>
|
||||
<Version>1.1.0</Version>
|
||||
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2021-2023</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
69
AssetStudioGUI/AssetStudioGUIForm.Designer.cs
generated
69
AssetStudioGUI/AssetStudioGUIForm.Designer.cs
generated
@@ -41,6 +41,11 @@
|
||||
this.displayAll = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.enablePreview = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.displayInfo = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.akSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.akTitleMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.akFixFaceSpriteNamesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.akUseExternalAlphaToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.akSeparator2 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.buildTreeStructureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem14 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.specifyUnityVersion = new System.Windows.Forms.ToolStripTextBox();
|
||||
@@ -234,6 +239,11 @@
|
||||
this.displayAll,
|
||||
this.enablePreview,
|
||||
this.displayInfo,
|
||||
this.akSeparator1,
|
||||
this.akTitleMenuItem,
|
||||
this.akFixFaceSpriteNamesToolStripMenuItem,
|
||||
this.akUseExternalAlphaToolStripMenuItem,
|
||||
this.akSeparator2,
|
||||
this.buildTreeStructureToolStripMenuItem,
|
||||
this.toolStripMenuItem14,
|
||||
this.showExpOpt});
|
||||
@@ -245,7 +255,7 @@
|
||||
//
|
||||
this.displayAll.CheckOnClick = true;
|
||||
this.displayAll.Name = "displayAll";
|
||||
this.displayAll.Size = new System.Drawing.Size(207, 22);
|
||||
this.displayAll.Size = new System.Drawing.Size(276, 22);
|
||||
this.displayAll.Text = "Display all assets";
|
||||
this.displayAll.ToolTipText = "Check this option will display all types assets. Not extractable assets can expor" +
|
||||
"t the RAW file.";
|
||||
@@ -257,7 +267,7 @@
|
||||
this.enablePreview.CheckOnClick = true;
|
||||
this.enablePreview.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.enablePreview.Name = "enablePreview";
|
||||
this.enablePreview.Size = new System.Drawing.Size(207, 22);
|
||||
this.enablePreview.Size = new System.Drawing.Size(276, 22);
|
||||
this.enablePreview.Text = "Enable preview";
|
||||
this.enablePreview.ToolTipText = "Toggle the loading and preview of readable assets, such as images, sounds, text, " +
|
||||
"etc.\r\nDisable preview if you have performance or compatibility issues.";
|
||||
@@ -269,19 +279,61 @@
|
||||
this.displayInfo.CheckOnClick = true;
|
||||
this.displayInfo.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.displayInfo.Name = "displayInfo";
|
||||
this.displayInfo.Size = new System.Drawing.Size(207, 22);
|
||||
this.displayInfo.Size = new System.Drawing.Size(276, 22);
|
||||
this.displayInfo.Text = "Display asset information";
|
||||
this.displayInfo.ToolTipText = "Toggle the overlay that shows information about each asset, eg. image size, forma" +
|
||||
"t, audio bitrate, etc.";
|
||||
this.displayInfo.CheckedChanged += new System.EventHandler(this.displayAssetInfo_Check);
|
||||
//
|
||||
// akSeparator1
|
||||
//
|
||||
this.akSeparator1.Name = "akSeparator1";
|
||||
this.akSeparator1.Size = new System.Drawing.Size(273, 6);
|
||||
//
|
||||
// akTitleMenuItem
|
||||
//
|
||||
this.akTitleMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
||||
this.akTitleMenuItem.Enabled = false;
|
||||
this.akTitleMenuItem.Name = "akTitleMenuItem";
|
||||
this.akTitleMenuItem.ShowShortcutKeys = false;
|
||||
this.akTitleMenuItem.Size = new System.Drawing.Size(276, 22);
|
||||
this.akTitleMenuItem.Text = "Arknights";
|
||||
//
|
||||
// akFixFaceSpriteNamesToolStripMenuItem
|
||||
//
|
||||
this.akFixFaceSpriteNamesToolStripMenuItem.Checked = true;
|
||||
this.akFixFaceSpriteNamesToolStripMenuItem.CheckOnClick = true;
|
||||
this.akFixFaceSpriteNamesToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.akFixFaceSpriteNamesToolStripMenuItem.Name = "akFixFaceSpriteNamesToolStripMenuItem";
|
||||
this.akFixFaceSpriteNamesToolStripMenuItem.Size = new System.Drawing.Size(276, 22);
|
||||
this.akFixFaceSpriteNamesToolStripMenuItem.Text = "Restore names of avg character sprites";
|
||||
this.akFixFaceSpriteNamesToolStripMenuItem.ToolTipText = "Rename face sprites with numeric names to correct ones";
|
||||
this.akFixFaceSpriteNamesToolStripMenuItem.CheckedChanged += new System.EventHandler(this.akFixFaceSpriteNamesToolStripMenuItem_Check);
|
||||
//
|
||||
// akUseExternalAlphaToolStripMenuItem
|
||||
//
|
||||
this.akUseExternalAlphaToolStripMenuItem.Checked = true;
|
||||
this.akUseExternalAlphaToolStripMenuItem.CheckOnClick = true;
|
||||
this.akUseExternalAlphaToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.akUseExternalAlphaToolStripMenuItem.Name = "akUseExternalAlphaToolStripMenuItem";
|
||||
this.akUseExternalAlphaToolStripMenuItem.Size = new System.Drawing.Size(276, 22);
|
||||
this.akUseExternalAlphaToolStripMenuItem.Text = "Use external alpha texture for sprites";
|
||||
this.akUseExternalAlphaToolStripMenuItem.ToolTipText = "Trying to find an external alpha texture for preview/export sprite assets (Skins," +
|
||||
" Char arts, Avg char arts, etc.)";
|
||||
this.akUseExternalAlphaToolStripMenuItem.CheckedChanged += new System.EventHandler(this.akUseExternalAlphaToolStripMenuItem_Check);
|
||||
//
|
||||
// akSeparator2
|
||||
//
|
||||
this.akSeparator2.Name = "akSeparator2";
|
||||
this.akSeparator2.Size = new System.Drawing.Size(273, 6);
|
||||
//
|
||||
// buildTreeStructureToolStripMenuItem
|
||||
//
|
||||
this.buildTreeStructureToolStripMenuItem.Checked = true;
|
||||
this.buildTreeStructureToolStripMenuItem.CheckOnClick = true;
|
||||
this.buildTreeStructureToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.buildTreeStructureToolStripMenuItem.Name = "buildTreeStructureToolStripMenuItem";
|
||||
this.buildTreeStructureToolStripMenuItem.Size = new System.Drawing.Size(207, 22);
|
||||
this.buildTreeStructureToolStripMenuItem.Size = new System.Drawing.Size(276, 22);
|
||||
this.buildTreeStructureToolStripMenuItem.Text = "Build tree structure";
|
||||
this.buildTreeStructureToolStripMenuItem.ToolTipText = "You can disable tree structure building if you don\'t use the Scene Hierarchy tab";
|
||||
this.buildTreeStructureToolStripMenuItem.CheckedChanged += new System.EventHandler(this.buildTreeStructureToolStripMenuItem_CheckedChanged);
|
||||
@@ -291,7 +343,7 @@
|
||||
this.toolStripMenuItem14.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.specifyUnityVersion});
|
||||
this.toolStripMenuItem14.Name = "toolStripMenuItem14";
|
||||
this.toolStripMenuItem14.Size = new System.Drawing.Size(207, 22);
|
||||
this.toolStripMenuItem14.Size = new System.Drawing.Size(276, 22);
|
||||
this.toolStripMenuItem14.Text = "Specify Unity version";
|
||||
//
|
||||
// specifyUnityVersion
|
||||
@@ -305,7 +357,7 @@
|
||||
// showExpOpt
|
||||
//
|
||||
this.showExpOpt.Name = "showExpOpt";
|
||||
this.showExpOpt.Size = new System.Drawing.Size(207, 22);
|
||||
this.showExpOpt.Size = new System.Drawing.Size(276, 22);
|
||||
this.showExpOpt.Text = "Export options";
|
||||
this.showExpOpt.Click += new System.EventHandler(this.showExpOpt_Click);
|
||||
//
|
||||
@@ -1441,6 +1493,11 @@
|
||||
private System.Windows.Forms.ToolStripMenuItem showRelatedAssetsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator7;
|
||||
private System.Windows.Forms.ListView assetListView;
|
||||
private System.Windows.Forms.ToolStripMenuItem akFixFaceSpriteNamesToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem akUseExternalAlphaToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripSeparator akSeparator1;
|
||||
private System.Windows.Forms.ToolStripMenuItem akTitleMenuItem;
|
||||
private System.Windows.Forms.ToolStripSeparator akSeparator2;
|
||||
private System.Windows.Forms.ToolStripMenuItem showConsoleToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem writeLogToFileToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem buildTreeStructureToolStripMenuItem;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AssetStudio;
|
||||
using Arknights;
|
||||
using AssetStudio;
|
||||
using Newtonsoft.Json;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using System;
|
||||
@@ -18,6 +19,8 @@ using System.Timers;
|
||||
using System.Windows.Forms;
|
||||
using static AssetStudioGUI.Studio;
|
||||
using Font = AssetStudio.Font;
|
||||
using SharpImage = SixLabors.ImageSharp;
|
||||
using SharpImageFormat = SixLabors.ImageSharp.PixelFormats;
|
||||
using Microsoft.WindowsAPICodePack.Taskbar;
|
||||
#if NET472
|
||||
using OpenTK;
|
||||
@@ -47,6 +50,7 @@ namespace AssetStudioGUI
|
||||
|
||||
#region SpriteControl
|
||||
private SpriteMaskMode spriteMaskVisibleMode = SpriteMaskMode.On;
|
||||
private bool showDebugInfo = false;
|
||||
#endregion
|
||||
|
||||
#region TexControl
|
||||
@@ -129,6 +133,8 @@ namespace AssetStudioGUI
|
||||
displayAll.Checked = Properties.Settings.Default.displayAll;
|
||||
displayInfo.Checked = Properties.Settings.Default.displayInfo;
|
||||
enablePreview.Checked = Properties.Settings.Default.enablePreview;
|
||||
akFixFaceSpriteNamesToolStripMenuItem.Checked = Properties.Settings.Default.fixFaceSpriteNames;
|
||||
akUseExternalAlphaToolStripMenuItem.Checked = Properties.Settings.Default.useExternalAlpha;
|
||||
showConsoleToolStripMenuItem.Checked = Properties.Settings.Default.showConsole;
|
||||
buildTreeStructureToolStripMenuItem.Checked = Properties.Settings.Default.buildTreeStructure;
|
||||
FMODinit();
|
||||
@@ -268,6 +274,9 @@ namespace AssetStudioGUI
|
||||
typeMap.Clear();
|
||||
classesListView.EndUpdate();
|
||||
|
||||
if (akFixFaceSpriteNamesToolStripMenuItem.Checked)
|
||||
FixFaceSpriteNames();
|
||||
|
||||
var types = exportableAssets.Select(x => x.Type).Distinct().OrderBy(x => x.ToString()).ToArray();
|
||||
foreach (var type in types)
|
||||
{
|
||||
@@ -367,7 +376,7 @@ namespace AssetStudioGUI
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (lastSelectedItem?.Type == ClassIDType.Sprite && !((Sprite)lastSelectedItem.Asset).m_RD.alphaTexture.IsNull)
|
||||
else if ((lastSelectedItem?.Type == ClassIDType.Sprite && !((Sprite)lastSelectedItem.Asset).m_RD.alphaTexture.IsNull) || lastSelectedItem?.Type == ClassIDType.AkPortraitSprite)
|
||||
{
|
||||
switch (e.KeyCode)
|
||||
{
|
||||
@@ -379,6 +388,10 @@ namespace AssetStudioGUI
|
||||
spriteMaskVisibleMode = spriteMaskVisibleMode == SpriteMaskMode.MaskOnly ? SpriteMaskMode.On : SpriteMaskMode.MaskOnly;
|
||||
need = true;
|
||||
break;
|
||||
case Keys.D:
|
||||
showDebugInfo = !showDebugInfo;
|
||||
need = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (need)
|
||||
@@ -433,6 +446,7 @@ namespace AssetStudioGUI
|
||||
switch (lastSelectedItem.Type)
|
||||
{
|
||||
case ClassIDType.Texture2D:
|
||||
case ClassIDType.AkPortraitSprite:
|
||||
case ClassIDType.Sprite:
|
||||
{
|
||||
if (enablePreview.Checked && imageTexture != null)
|
||||
@@ -833,6 +847,9 @@ namespace AssetStudioGUI
|
||||
case ClassIDType.Sprite:
|
||||
PreviewSprite(assetItem, assetItem.Asset as Sprite);
|
||||
break;
|
||||
case ClassIDType.AkPortraitSprite:
|
||||
PreviewAkPortraitSprite(assetItem);
|
||||
break;
|
||||
case ClassIDType.Animator:
|
||||
StatusStripUpdate("Can be exported to FBX file.");
|
||||
break;
|
||||
@@ -1278,7 +1295,31 @@ namespace AssetStudioGUI
|
||||
|
||||
private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite)
|
||||
{
|
||||
var image = m_Sprite.GetImage(spriteMaskMode: spriteMaskVisibleMode);
|
||||
SharpImage.Image<SharpImageFormat.Bgra32> image;
|
||||
AvgSprite avgSprite = null;
|
||||
|
||||
bool isCharAvgSprite = assetItem.Container.Contains("avg/characters");
|
||||
bool isCharArt = assetItem.Container.Contains("arts/characters");
|
||||
if (akUseExternalAlphaToolStripMenuItem.Checked && (isCharAvgSprite || isCharArt))
|
||||
{
|
||||
avgSprite = isCharAvgSprite ? new AvgSprite(assetItem) : null;
|
||||
|
||||
if (m_Sprite.m_RD.alphaTexture.IsNull)
|
||||
{
|
||||
var charAlphaTex = AkSpriteHelper.TryFindAlphaTex(assetItem, avgSprite, isCharAvgSprite);
|
||||
if (charAlphaTex != null)
|
||||
{
|
||||
m_Sprite.m_RD.alphaTexture.Set(charAlphaTex);
|
||||
m_Sprite.akSplitAlpha = true;
|
||||
}
|
||||
}
|
||||
image = m_Sprite.AkGetImage(avgSprite, spriteMaskMode: spriteMaskVisibleMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
image = m_Sprite.GetImage(spriteMaskMode: spriteMaskVisibleMode);
|
||||
}
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
var bitmap = new DirectBitmap(image);
|
||||
@@ -1286,10 +1327,33 @@ namespace AssetStudioGUI
|
||||
assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n";
|
||||
PreviewTexture(bitmap);
|
||||
|
||||
if (!m_Sprite.m_RD.alphaTexture.IsNull)
|
||||
if (!m_Sprite.m_RD.alphaTexture.IsNull && (akUseExternalAlphaToolStripMenuItem.Checked || !m_Sprite.akSplitAlpha))
|
||||
{
|
||||
assetItem.InfoText += $"Alpha Mask: {spriteMaskVisibleMode}\n";
|
||||
StatusStripUpdate("'Ctrl'+'A' - Enable/Disable alpha mask usage. 'Ctrl'+'M' - Show alpha mask only.");
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine($"Alpha mask: {spriteMaskVisibleMode}");
|
||||
sb.Append(spriteMaskVisibleMode != SpriteMaskMode.Off ? $"Is external mask: {m_Sprite.akSplitAlpha}\n" : "");
|
||||
if (avgSprite != null)
|
||||
{
|
||||
sb.AppendLine($"Alias: \"{avgSprite.Alias}\"");
|
||||
if (showDebugInfo)
|
||||
{
|
||||
sb.AppendLine($"[Debug]");
|
||||
sb.AppendLine($"Is avg hub parsed: {avgSprite.IsHubParsed}");
|
||||
if (avgSprite.IsHubParsed)
|
||||
{
|
||||
sb.AppendLine($"Is face data exist: {avgSprite.FaceSize.Width > 0}");
|
||||
sb.AppendLine($"Is face sprite: {avgSprite.IsFaceSprite}");
|
||||
sb.AppendLine($"Is whole body sprite: {avgSprite.IsWholeBodySprite}");
|
||||
}
|
||||
}
|
||||
StatusStripUpdate("'Ctrl'+'A' - Enable/Disable alpha mask usage. 'Ctrl'+'M' - Show alpha mask only. 'Ctrl'+'D' - Show debug info.");
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusStripUpdate("'Ctrl'+'A' - Enable/Disable alpha mask usage. 'Ctrl'+'M' - Show alpha mask only.");
|
||||
}
|
||||
assetItem.InfoText += sb.ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1298,6 +1362,25 @@ namespace AssetStudioGUI
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviewAkPortraitSprite(AssetItem assetItem)
|
||||
{
|
||||
var image = assetItem.AkPortraitSprite.AkGetImage(spriteMaskMode: spriteMaskVisibleMode);
|
||||
if (image != null)
|
||||
{
|
||||
var bitmap = new DirectBitmap(image);
|
||||
image.Dispose();
|
||||
assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n";
|
||||
assetItem.InfoText += $"Alpha mask: {spriteMaskVisibleMode}";
|
||||
PreviewTexture(bitmap);
|
||||
|
||||
StatusStripUpdate("'Ctrl'+'A' - Enable/Disable alpha mask usage. 'Ctrl'+'M' - Show alpha mask only.");
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusStripUpdate("Unsupported sprite for preview.");
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviewTexture(DirectBitmap bitmap)
|
||||
{
|
||||
imageTexture?.Dispose();
|
||||
@@ -1395,6 +1478,34 @@ namespace AssetStudioGUI
|
||||
FMODreset();
|
||||
}
|
||||
|
||||
private void FixFaceSpriteNames()
|
||||
{
|
||||
assetListView.BeginUpdate();
|
||||
for (int i = 0; i < assetListView.Items.Count; i++)
|
||||
{
|
||||
var assetItem = (AssetItem)assetListView.Items[i];
|
||||
if (assetItem.Type == ClassIDType.Sprite)
|
||||
{
|
||||
var m_Sprite = (Sprite)assetItem.Asset;
|
||||
if (akFixFaceSpriteNamesToolStripMenuItem.Checked)
|
||||
{
|
||||
var groupedPattern = new Regex(@"^\d{1,2}\$\d{1,2}$"); // "spriteIndex$groupIndex"
|
||||
var notGroupedPattern = new Regex(@"^\d{1,2}$"); // "spriteIndex"
|
||||
if (groupedPattern.IsMatch(m_Sprite.m_Name) || notGroupedPattern.IsMatch(m_Sprite.m_Name))
|
||||
{
|
||||
var fullName = Path.GetFileNameWithoutExtension(assetItem.Container);
|
||||
assetItem.Text = $"{fullName}#{m_Sprite.m_Name}";
|
||||
}
|
||||
}
|
||||
else if (assetItem.Text != m_Sprite.m_Name)
|
||||
{
|
||||
assetItem.Text = m_Sprite.m_Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
assetListView.EndUpdate();
|
||||
}
|
||||
|
||||
private void tabControl2_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (tabControl2.SelectedIndex == 1 && lastSelectedItem != null)
|
||||
@@ -1883,6 +1994,26 @@ namespace AssetStudioGUI
|
||||
sceneTreeView.EndUpdate();
|
||||
}
|
||||
|
||||
private void akFixFaceSpriteNamesToolStripMenuItem_Check(object sender, EventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.fixFaceSpriteNames = akFixFaceSpriteNamesToolStripMenuItem.Checked;
|
||||
Properties.Settings.Default.Save();
|
||||
FixFaceSpriteNames();
|
||||
}
|
||||
|
||||
private void akUseExternalAlphaToolStripMenuItem_Check(object sender, EventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.useExternalAlpha = akUseExternalAlphaToolStripMenuItem.Checked;
|
||||
Properties.Settings.Default.Save();
|
||||
|
||||
if (lastSelectedItem?.Type == ClassIDType.Sprite)
|
||||
{
|
||||
StatusStripUpdate("");
|
||||
PreviewAsset(lastSelectedItem);
|
||||
assetInfoLabel.Text = lastSelectedItem.InfoText;
|
||||
}
|
||||
}
|
||||
|
||||
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var aboutForm = new AboutForm();
|
||||
|
||||
291
AssetStudioGUI/Components/Arknights/AkSpriteHelper.cs
Normal file
291
AssetStudioGUI/Components/Arknights/AkSpriteHelper.cs
Normal file
@@ -0,0 +1,291 @@
|
||||
using Arknights.PortraitSpriteMono;
|
||||
using AssetStudio;
|
||||
using AssetStudioGUI;
|
||||
using AssetStudioGUI.Properties;
|
||||
using Newtonsoft.Json;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SixLabors.ImageSharp.Processing.Processors.Transforms;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Arknights
|
||||
{
|
||||
internal static class AkSpriteHelper
|
||||
{
|
||||
public static Texture2D TryFindAlphaTex(AssetItem assetItem, AvgSprite avgSprite, bool isAvgSprite)
|
||||
{
|
||||
Sprite m_Sprite = (Sprite)assetItem.Asset;
|
||||
var imgType = "arts/characters";
|
||||
if (m_Sprite.m_RD.alphaTexture.m_PathID == 0)
|
||||
{
|
||||
if (isAvgSprite)
|
||||
{
|
||||
if (avgSprite?.FullAlphaTexture != null)
|
||||
return avgSprite.FullAlphaTexture;
|
||||
|
||||
imgType = "avg/characters"; //since the avg hub was not found for some reason, let's try to find alpha tex by name
|
||||
}
|
||||
var spriteFullName = Path.GetFileNameWithoutExtension(assetItem.Container);
|
||||
foreach (var item in Studio.exportableAssets)
|
||||
{
|
||||
if (item.Type == ClassIDType.Texture2D)
|
||||
{
|
||||
if (item.Container.Contains(imgType) && item.Container.Contains($"illust_{m_Sprite.m_Name}_material") && item.Text.Contains("[alpha]"))
|
||||
return (Texture2D)item.Asset;
|
||||
if (item.Container.Contains(imgType) && item.Container.Contains(spriteFullName) && item.Text == $"{m_Sprite.m_Name}[alpha]")
|
||||
return (Texture2D)item.Asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Image<Bgra32> AkGetImage(this Sprite m_Sprite, AvgSprite avgSprite = null, SpriteMaskMode spriteMaskMode = SpriteMaskMode.On)
|
||||
{
|
||||
if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D) && m_Sprite.m_RD.alphaTexture.TryGet(out var m_AlphaTexture2D) && spriteMaskMode != SpriteMaskMode.Off)
|
||||
{
|
||||
Image<Bgra32> tex = null;
|
||||
Image<Bgra32> alphaTex = null;
|
||||
|
||||
if (avgSprite != null && avgSprite.IsHubParsed)
|
||||
{
|
||||
alphaTex = m_AlphaTexture2D.ConvertToImage(true);
|
||||
if (avgSprite.IsFaceSprite)
|
||||
{
|
||||
var faceImage = m_Texture2D.ConvertToImage(true);
|
||||
var faceAlpha = avgSprite.FaceSpriteAlphaTexture.ConvertToImage(true);
|
||||
if (new Size(faceImage.Width, faceImage.Height) != avgSprite.FaceSize)
|
||||
{
|
||||
faceImage.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
|
||||
faceAlpha.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
|
||||
}
|
||||
tex = avgSprite.FullTexture.ConvertToImage(true);
|
||||
tex.Mutate(x => x.DrawImage(faceImage, avgSprite.FacePos, opacity: 1f));
|
||||
alphaTex.Mutate(x => x.DrawImage(faceAlpha, avgSprite.FacePos, opacity: 1f));
|
||||
}
|
||||
else
|
||||
{
|
||||
tex = m_Texture2D.ConvertToImage(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spriteMaskMode != SpriteMaskMode.MaskOnly)
|
||||
{
|
||||
tex = CutImage(m_Texture2D.ConvertToImage(false), m_Sprite.m_RD.textureRect, m_Sprite.m_RD.downscaleMultiplier);
|
||||
}
|
||||
alphaTex = CutImage(m_AlphaTexture2D.ConvertToImage(false), m_Sprite.m_RD.textureRect, m_Sprite.m_RD.downscaleMultiplier);
|
||||
}
|
||||
|
||||
return ImageRender(tex, alphaTex, spriteMaskMode);
|
||||
}
|
||||
else if (m_Sprite.m_RD.texture.TryGet(out m_Texture2D) && avgSprite != null && avgSprite.IsHubParsed)
|
||||
{
|
||||
if (!avgSprite.IsFaceSprite)
|
||||
{
|
||||
return m_Texture2D.ConvertToImage(true);
|
||||
}
|
||||
|
||||
var faceImage = m_Texture2D.ConvertToImage(true);
|
||||
var tex = avgSprite.FullTexture.ConvertToImage(true);
|
||||
if (new Size(faceImage.Width, faceImage.Height) != avgSprite.FaceSize)
|
||||
{
|
||||
faceImage.Mutate(x => x.Resize(new ResizeOptions {Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch}));
|
||||
}
|
||||
tex.Mutate(x => x.DrawImage(faceImage, avgSprite.FacePos, opacity: 1f));
|
||||
|
||||
return tex;
|
||||
}
|
||||
else if (m_Sprite.m_RD.texture.TryGet(out m_Texture2D))
|
||||
{
|
||||
return CutImage(m_Texture2D.ConvertToImage(false), m_Sprite.m_RD.textureRect, m_Sprite.m_RD.downscaleMultiplier);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Image<Bgra32> AkGetImage(this PortraitSprite portraitSprite, SpriteMaskMode spriteMaskMode = SpriteMaskMode.On)
|
||||
{
|
||||
if (portraitSprite.Texture != null && portraitSprite.AlphaTexture != null)
|
||||
{
|
||||
Image<Bgra32> tex = null;
|
||||
Image<Bgra32> alphaTex = null;
|
||||
|
||||
if (spriteMaskMode != SpriteMaskMode.MaskOnly)
|
||||
{
|
||||
tex = CutImage(portraitSprite.Texture.ConvertToImage(false), portraitSprite.TextureRect, portraitSprite.DownscaleMultiplier, portraitSprite.Rotate);
|
||||
}
|
||||
if (spriteMaskMode != SpriteMaskMode.Off)
|
||||
{
|
||||
alphaTex = CutImage(portraitSprite.AlphaTexture.ConvertToImage(false), portraitSprite.TextureRect, portraitSprite.DownscaleMultiplier, portraitSprite.Rotate);
|
||||
}
|
||||
|
||||
return ImageRender(tex, alphaTex, spriteMaskMode);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<PortraitSprite> GeneratePortraits(AssetItem asset)
|
||||
{
|
||||
var portraits = new List<PortraitSprite>();
|
||||
|
||||
var portraitsDict = ((MonoBehaviour)asset.Asset).ToType();
|
||||
if (portraitsDict == null)
|
||||
{
|
||||
Logger.Warning("Portraits MonoBehaviour is not readable.");
|
||||
return portraits;
|
||||
}
|
||||
var portraitsJson = JsonConvert.SerializeObject(portraitsDict);
|
||||
var portraitsData = JsonConvert.DeserializeObject<PortraitSpriteConfig>(portraitsJson);
|
||||
|
||||
var atlasTex = (Texture2D)Studio.exportableAssets.Find(x => x.m_PathID == portraitsData._atlas.Texture.m_PathID).Asset;
|
||||
var atlasAlpha = (Texture2D)Studio.exportableAssets.Find(x => x.m_PathID == portraitsData._atlas.Alpha.m_PathID).Asset;
|
||||
|
||||
foreach (var portraitData in portraitsData._sprites)
|
||||
{
|
||||
var portraitSprite = new PortraitSprite()
|
||||
{
|
||||
Name = portraitData.Name,
|
||||
AssetsFile = atlasTex.assetsFile,
|
||||
Container = asset.Container,
|
||||
Texture = atlasTex,
|
||||
AlphaTexture = atlasAlpha,
|
||||
TextureRect = new Rectf(portraitData.Rect.X, portraitData.Rect.Y, portraitData.Rect.W, portraitData.Rect.H),
|
||||
Rotate = portraitData.Rotate,
|
||||
};
|
||||
portraits.Add(portraitSprite);
|
||||
}
|
||||
|
||||
return portraits;
|
||||
}
|
||||
|
||||
private static Image<Bgra32> ImageRender(Image<Bgra32> tex, Image<Bgra32> alpha, SpriteMaskMode maskMode)
|
||||
{
|
||||
switch (maskMode)
|
||||
{
|
||||
case SpriteMaskMode.On:
|
||||
tex.ApplyRGBMask(alpha, isPreview: true);
|
||||
return tex;
|
||||
case SpriteMaskMode.Off:
|
||||
alpha?.Dispose();
|
||||
return tex;
|
||||
case SpriteMaskMode.MaskOnly:
|
||||
tex?.Dispose();
|
||||
return alpha;
|
||||
case SpriteMaskMode.Export:
|
||||
tex.ApplyRGBMask(alpha);
|
||||
return tex;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IResampler GetResampler(bool isPreview)
|
||||
{
|
||||
IResampler resampler;
|
||||
if (isPreview)
|
||||
{
|
||||
resampler = KnownResamplers.NearestNeighbor;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (Settings.Default.resamplerIndex)
|
||||
{
|
||||
case 0:
|
||||
resampler = KnownResamplers.NearestNeighbor;
|
||||
break;
|
||||
case 1: //Bilinear
|
||||
resampler = KnownResamplers.Triangle;
|
||||
break;
|
||||
case 2:
|
||||
resampler = KnownResamplers.Bicubic;
|
||||
break;
|
||||
case 3:
|
||||
resampler = KnownResamplers.MitchellNetravali;
|
||||
break;
|
||||
case 4:
|
||||
resampler = KnownResamplers.Spline;
|
||||
break;
|
||||
case 5:
|
||||
resampler = KnownResamplers.Welch;
|
||||
break;
|
||||
default:
|
||||
resampler = KnownResamplers.MitchellNetravali;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return resampler;
|
||||
}
|
||||
|
||||
private static void ApplyRGBMask(this Image<Bgra32> tex, Image<Bgra32> texMask, bool isPreview = false)
|
||||
{
|
||||
using (texMask)
|
||||
{
|
||||
bool resized = false;
|
||||
if (tex.Width != texMask.Width || tex.Height != texMask.Height)
|
||||
{
|
||||
texMask.Mutate(x => x.Resize(tex.Width, tex.Height, GetResampler(isPreview)));
|
||||
resized = true;
|
||||
}
|
||||
|
||||
var invGamma = 1.0 / (1.0 + Settings.Default.alphaMaskGamma / 10.0);
|
||||
if (Settings.Default.resizedOnly && !resized)
|
||||
{
|
||||
invGamma = 1.0;
|
||||
}
|
||||
|
||||
tex.ProcessPixelRows(texMask, (sourceTex, targetTexMask) =>
|
||||
{
|
||||
for (int y = 0; y < texMask.Height; y++)
|
||||
{
|
||||
var texRow = sourceTex.GetRowSpan(y);
|
||||
var maskRow = targetTexMask.GetRowSpan(y);
|
||||
for (int x = 0; x < maskRow.Length; x++)
|
||||
{
|
||||
var grayscale = (maskRow[x].R + maskRow[x].G + maskRow[x].B) / 3.0;
|
||||
if (invGamma != 1)
|
||||
{
|
||||
grayscale = 255 - Math.Pow((255 - grayscale) / 255, invGamma) * 255;
|
||||
}
|
||||
texRow[x].A = (byte)grayscale;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static Image<Bgra32> CutImage(Image<Bgra32> originalImage, Rectf textureRect, float downscaleMultiplier, bool rotate = false)
|
||||
{
|
||||
if (originalImage != null)
|
||||
{
|
||||
if (downscaleMultiplier > 0f && downscaleMultiplier != 1f)
|
||||
{
|
||||
var newSize = (Size)(new Size(originalImage.Width, originalImage.Height) / downscaleMultiplier);
|
||||
originalImage.Mutate(x => x.Resize(newSize, KnownResamplers.Lanczos3, compand: true));
|
||||
}
|
||||
var rectX = (int)Math.Floor(textureRect.x);
|
||||
var rectY = (int)Math.Floor(textureRect.y);
|
||||
var rectRight = (int)Math.Ceiling(textureRect.x + textureRect.width);
|
||||
var rectBottom = (int)Math.Ceiling(textureRect.y + textureRect.height);
|
||||
rectRight = Math.Min(rectRight, originalImage.Width);
|
||||
rectBottom = Math.Min(rectBottom, originalImage.Height);
|
||||
var rect = new Rectangle(rectX, rectY, rectRight - rectX, rectBottom - rectY);
|
||||
var spriteImage = originalImage.Clone(x => x.Crop(rect));
|
||||
originalImage.Dispose();
|
||||
if (rotate)
|
||||
{
|
||||
spriteImage.Mutate(x => x.Rotate(RotateMode.Rotate270));
|
||||
}
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||
|
||||
return spriteImage;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
126
AssetStudioGUI/Components/Arknights/AvgSprite.cs
Normal file
126
AssetStudioGUI/Components/Arknights/AvgSprite.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using Arknights.AvgCharHubMono;
|
||||
using AssetStudio;
|
||||
using AssetStudioGUI;
|
||||
using SixLabors.ImageSharp;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Arknights
|
||||
{
|
||||
internal class AvgSprite
|
||||
{
|
||||
public Texture2D FaceSpriteAlphaTexture { get; }
|
||||
public Texture2D FullTexture { get; }
|
||||
public Texture2D FullAlphaTexture { get; }
|
||||
public Point FacePos { get; }
|
||||
public Size FaceSize { get; }
|
||||
public string Alias { get; }
|
||||
public bool IsWholeBodySprite { get; }
|
||||
public bool IsFaceSprite { get; }
|
||||
public bool IsHubParsed { get; }
|
||||
|
||||
private AvgSpriteConfig GetCurSpriteGroup(AvgSpriteConfigGroup spriteHubDataGrouped, long spriteItemID, string spriteName)
|
||||
{
|
||||
if (spriteHubDataGrouped.SpriteGroups.Length > 1)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(spriteName))
|
||||
{
|
||||
var groupFromName = int.TryParse(spriteName?.Substring(spriteName.IndexOf('$') + 1, 1), out int groupIndex);
|
||||
if (groupFromName)
|
||||
{
|
||||
return spriteHubDataGrouped.SpriteGroups[groupIndex - 1];
|
||||
}
|
||||
}
|
||||
return spriteHubDataGrouped.SpriteGroups.FirstOrDefault(x => x.Sprites.Any(y => y.Sprite.m_PathID == spriteItemID));
|
||||
}
|
||||
else
|
||||
{
|
||||
return spriteHubDataGrouped.SpriteGroups[0];
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetSpriteHub(AssetItem assetItem, out AvgSpriteConfig spriteHubData)
|
||||
{
|
||||
spriteHubData = null;
|
||||
var avgSpriteHubItem = Studio.exportableAssets.Find(x =>
|
||||
x.Type == ClassIDType.MonoBehaviour
|
||||
&& x.Container == assetItem.Container
|
||||
&& x.Text.IndexOf("AVGCharacterSpriteHub", StringComparison.OrdinalIgnoreCase) >= 0
|
||||
);
|
||||
if (avgSpriteHubItem == null)
|
||||
{
|
||||
Logger.Warning("AVGCharacterSpriteHub was not found.");
|
||||
return false;
|
||||
}
|
||||
var spriteHubDict = ((MonoBehaviour)avgSpriteHubItem.Asset).ToType();
|
||||
if (spriteHubDict == null)
|
||||
{
|
||||
Logger.Warning("AVGCharacterSpriteHub is not readable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var spriteHubJson = JsonConvert.SerializeObject(spriteHubDict);
|
||||
if (avgSpriteHubItem.Text.ToLower().Contains("hubgroup"))
|
||||
{
|
||||
var groupedSpriteHub = JsonConvert.DeserializeObject<AvgSpriteConfigGroup>(spriteHubJson);
|
||||
spriteHubData = GetCurSpriteGroup(groupedSpriteHub, assetItem.m_PathID, assetItem.Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteHubData = JsonConvert.DeserializeObject<AvgSpriteConfig>(spriteHubJson);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public AvgSprite(AssetItem assetItem)
|
||||
{
|
||||
if (TryGetSpriteHub(assetItem, out var spriteHubData))
|
||||
{
|
||||
IsHubParsed = spriteHubData?.Sprites.Length > 0;
|
||||
}
|
||||
if (IsHubParsed)
|
||||
{
|
||||
var curSpriteData = spriteHubData.Sprites.FirstOrDefault(x => x.Sprite.m_PathID == assetItem.m_PathID);
|
||||
|
||||
if (curSpriteData == null)
|
||||
{
|
||||
Studio.StatusStripUpdate($"Sprite \"{assetItem.Text}\" was not found in the avg sprite hub.");
|
||||
return;
|
||||
}
|
||||
|
||||
Alias = curSpriteData.Alias;
|
||||
IsWholeBodySprite = curSpriteData.IsWholeBody;
|
||||
|
||||
if (spriteHubData.FaceSize.X > 0 && spriteHubData.FaceSize.Y > 0) //If face data exist
|
||||
{
|
||||
var fullTexSpriteData = spriteHubData.Sprites.Last(); //Last sprite item in the list usually contains PathID of Sprite with full texture
|
||||
|
||||
var curSprite = (Sprite)assetItem.Asset;
|
||||
IsFaceSprite = curSprite.m_Rect.width <= 256 && curSprite.m_Rect.height <= 256 && curSprite.m_PathID != fullTexSpriteData.Sprite.m_PathID;
|
||||
|
||||
var curSpriteAlphaID = curSpriteData.AlphaTex.m_PathID;
|
||||
var curSpriteAlphaTex = (Texture2D)Studio.exportableAssets.Find(x => x.m_PathID == curSpriteAlphaID)?.Asset;
|
||||
if (curSpriteAlphaTex != null)
|
||||
{
|
||||
FaceSpriteAlphaTexture = IsFaceSprite ? curSpriteAlphaTex : null;
|
||||
fullTexSpriteData = IsFaceSprite ? fullTexSpriteData : curSpriteData;
|
||||
}
|
||||
var fullTexSpriteID = fullTexSpriteData.Sprite.m_PathID;
|
||||
var fullTexAlphaID = fullTexSpriteData.AlphaTex.m_PathID;
|
||||
var fullTexSprite = (Sprite)Studio.exportableAssets.Find(x => x.m_PathID == fullTexSpriteID).Asset;
|
||||
|
||||
FullTexture = fullTexSprite.m_RD.texture.TryGet(out var fullTex) ? fullTex : null;
|
||||
FullAlphaTexture = (Texture2D)Studio.exportableAssets.Find(x => x.m_PathID == fullTexAlphaID)?.Asset;
|
||||
FacePos = new Point((int)Math.Round(spriteHubData.FacePos.X), (int)Math.Round(spriteHubData.FacePos.Y));
|
||||
FaceSize = new Size((int)Math.Round(spriteHubData.FaceSize.X), (int)Math.Round(spriteHubData.FaceSize.Y));
|
||||
}
|
||||
else
|
||||
{
|
||||
FullAlphaTexture = (Texture2D)Studio.exportableAssets.Find(x => x.m_PathID == curSpriteData.AlphaTex.m_PathID).Asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
AssetStudioGUI/Components/Arknights/AvgSpriteConfig.cs
Normal file
30
AssetStudioGUI/Components/Arknights/AvgSpriteConfig.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using AssetStudio;
|
||||
|
||||
namespace Arknights.AvgCharHubMono
|
||||
{
|
||||
internal class AvgAssetIDs
|
||||
{
|
||||
public int m_FileID { get; set; }
|
||||
public long m_PathID { get; set; }
|
||||
}
|
||||
|
||||
internal class AvgSpriteData
|
||||
{
|
||||
public AvgAssetIDs Sprite { get; set; }
|
||||
public AvgAssetIDs AlphaTex { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public bool IsWholeBody { get; set; }
|
||||
}
|
||||
|
||||
internal class AvgSpriteConfig
|
||||
{
|
||||
public AvgSpriteData[] Sprites { get; set; }
|
||||
public Vector2 FaceSize { get; set; }
|
||||
public Vector3 FacePos { get; set; }
|
||||
}
|
||||
|
||||
internal class AvgSpriteConfigGroup
|
||||
{
|
||||
public AvgSpriteConfig[] SpriteGroups { get; set; }
|
||||
}
|
||||
}
|
||||
24
AssetStudioGUI/Components/Arknights/PortraitSprite.cs
Normal file
24
AssetStudioGUI/Components/Arknights/PortraitSprite.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using AssetStudio;
|
||||
|
||||
|
||||
namespace Arknights
|
||||
{
|
||||
internal class PortraitSprite
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public ClassIDType Type { get; }
|
||||
public SerializedFile AssetsFile { get; set; }
|
||||
public string Container { get; set; }
|
||||
public Texture2D Texture { get; set; }
|
||||
public Texture2D AlphaTexture { get; set; }
|
||||
public Rectf TextureRect { get; set; }
|
||||
public bool Rotate { get; set; }
|
||||
public float DownscaleMultiplier { get; }
|
||||
|
||||
public PortraitSprite()
|
||||
{
|
||||
Type = ClassIDType.AkPortraitSprite;
|
||||
DownscaleMultiplier = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
AssetStudioGUI/Components/Arknights/PortraitSpriteConfig.cs
Normal file
41
AssetStudioGUI/Components/Arknights/PortraitSpriteConfig.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
namespace Arknights.PortraitSpriteMono
|
||||
{
|
||||
internal class PortraitRect
|
||||
{
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
public float W { get; set; }
|
||||
public float H { get; set; }
|
||||
}
|
||||
|
||||
internal class AtlasSprite
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Guid { get; set; }
|
||||
public int Atlas { get; set; }
|
||||
public PortraitRect Rect { get; set; }
|
||||
public bool Rotate { get; set; }
|
||||
}
|
||||
|
||||
internal class TextureIDs
|
||||
{
|
||||
public int m_FileID { get; set; }
|
||||
public long m_PathID { get; set; }
|
||||
}
|
||||
|
||||
internal class AtlasInfo
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public TextureIDs Texture { get; set; }
|
||||
public TextureIDs Alpha { get; set; }
|
||||
public int Size { get; set; }
|
||||
}
|
||||
|
||||
internal class PortraitSpriteConfig
|
||||
{
|
||||
public string m_Name { get; set; }
|
||||
public AtlasSprite[] _sprites { get; set; }
|
||||
public AtlasInfo _atlas { get; set; }
|
||||
public int _index { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Windows.Forms;
|
||||
using AssetStudio;
|
||||
using Arknights;
|
||||
|
||||
namespace AssetStudioGUI
|
||||
{
|
||||
@@ -15,6 +16,7 @@ namespace AssetStudioGUI
|
||||
public string InfoText;
|
||||
public string UniqueID;
|
||||
public GameObjectTreeNode TreeNode;
|
||||
public PortraitSprite AkPortraitSprite;
|
||||
|
||||
public AssetItem(Object asset)
|
||||
{
|
||||
@@ -26,6 +28,19 @@ namespace AssetStudioGUI
|
||||
FullSize = asset.byteSize;
|
||||
}
|
||||
|
||||
public AssetItem(PortraitSprite akPortraitSprite)
|
||||
{
|
||||
Asset = null;
|
||||
SourceFile = akPortraitSprite.AssetsFile;
|
||||
Container = akPortraitSprite.Container;
|
||||
Type = akPortraitSprite.Type;
|
||||
TypeString = Type.ToString();
|
||||
Text = akPortraitSprite.Name;
|
||||
m_PathID = -1;
|
||||
FullSize = (long)(akPortraitSprite.TextureRect.width * akPortraitSprite.TextureRect.height * 4);
|
||||
AkPortraitSprite = akPortraitSprite;
|
||||
}
|
||||
|
||||
public void SetSubItems()
|
||||
{
|
||||
SubItems.AddRange(new[]
|
||||
|
||||
167
AssetStudioGUI/ExportOptions.Designer.cs
generated
167
AssetStudioGUI/ExportOptions.Designer.cs
generated
@@ -69,6 +69,17 @@
|
||||
this.castToBone = new System.Windows.Forms.CheckBox();
|
||||
this.exportAllNodes = new System.Windows.Forms.CheckBox();
|
||||
this.eulerFilter = new System.Windows.Forms.CheckBox();
|
||||
this.akResamplerLabel = new System.Windows.Forms.Label();
|
||||
this.akResamplerComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.akSpritesAlphaGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.akGammaNoteLabel = new System.Windows.Forms.Label();
|
||||
this.akResamplerDescLabel = new System.Windows.Forms.Label();
|
||||
this.akResizedOnlyCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.akGammaValueLabel = new System.Windows.Forms.Label();
|
||||
this.akGammaLabel = new System.Windows.Forms.Label();
|
||||
this.akAlphaMaskGammaTrackBar = new System.Windows.Forms.TrackBar();
|
||||
this.akSpritesExportGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.akAddAliasesCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.optionTooltip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.panel1.SuspendLayout();
|
||||
@@ -78,14 +89,17 @@
|
||||
((System.ComponentModel.ISupportInitialize)(this.scaleFactor)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.boneSize)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.filterPrecision)).BeginInit();
|
||||
this.akSpritesAlphaGroupBox.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.akAlphaMaskGammaTrackBar)).BeginInit();
|
||||
this.akSpritesExportGroupBox.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// OKbutton
|
||||
//
|
||||
this.OKbutton.Location = new System.Drawing.Point(381, 380);
|
||||
this.OKbutton.Location = new System.Drawing.Point(681, 381);
|
||||
this.OKbutton.Name = "OKbutton";
|
||||
this.OKbutton.Size = new System.Drawing.Size(75, 23);
|
||||
this.OKbutton.TabIndex = 4;
|
||||
this.OKbutton.TabIndex = 6;
|
||||
this.OKbutton.Text = "OK";
|
||||
this.OKbutton.UseVisualStyleBackColor = true;
|
||||
this.OKbutton.Click += new System.EventHandler(this.OKbutton_Click);
|
||||
@@ -93,10 +107,10 @@
|
||||
// Cancel
|
||||
//
|
||||
this.Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.Cancel.Location = new System.Drawing.Point(462, 380);
|
||||
this.Cancel.Location = new System.Drawing.Point(762, 381);
|
||||
this.Cancel.Name = "Cancel";
|
||||
this.Cancel.Size = new System.Drawing.Size(75, 23);
|
||||
this.Cancel.TabIndex = 5;
|
||||
this.Cancel.TabIndex = 7;
|
||||
this.Cancel.Text = "Cancel";
|
||||
this.Cancel.UseVisualStyleBackColor = true;
|
||||
this.Cancel.Click += new System.EventHandler(this.Cancel_Click);
|
||||
@@ -566,14 +580,141 @@
|
||||
this.eulerFilter.Text = "EulerFilter";
|
||||
this.eulerFilter.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// akResamplerLabel
|
||||
//
|
||||
this.akResamplerLabel.AutoSize = true;
|
||||
this.akResamplerLabel.Location = new System.Drawing.Point(6, 21);
|
||||
this.akResamplerLabel.Name = "akResamplerLabel";
|
||||
this.akResamplerLabel.Size = new System.Drawing.Size(120, 13);
|
||||
this.akResamplerLabel.TabIndex = 1;
|
||||
this.akResamplerLabel.Text = "Alpha texture resampler:";
|
||||
this.optionTooltip.SetToolTip(this.akResamplerLabel, "Only affects exported images");
|
||||
//
|
||||
// akResamplerComboBox
|
||||
//
|
||||
this.akResamplerComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.akResamplerComboBox.FormattingEnabled = true;
|
||||
this.akResamplerComboBox.Items.AddRange(new object[] {
|
||||
"Nearest Neighbor",
|
||||
"Bilinear",
|
||||
"Bicubic",
|
||||
"Mitchell-Netravali",
|
||||
"Spline",
|
||||
"Welch"});
|
||||
this.akResamplerComboBox.Location = new System.Drawing.Point(132, 18);
|
||||
this.akResamplerComboBox.Name = "akResamplerComboBox";
|
||||
this.akResamplerComboBox.Size = new System.Drawing.Size(162, 21);
|
||||
this.akResamplerComboBox.TabIndex = 2;
|
||||
this.optionTooltip.SetToolTip(this.akResamplerComboBox, "Only affects exported images");
|
||||
//
|
||||
// akSpritesAlphaGroupBox
|
||||
//
|
||||
this.akSpritesAlphaGroupBox.Controls.Add(this.akGammaNoteLabel);
|
||||
this.akSpritesAlphaGroupBox.Controls.Add(this.akResamplerDescLabel);
|
||||
this.akSpritesAlphaGroupBox.Controls.Add(this.akResamplerLabel);
|
||||
this.akSpritesAlphaGroupBox.Controls.Add(this.akResamplerComboBox);
|
||||
this.akSpritesAlphaGroupBox.Controls.Add(this.akResizedOnlyCheckBox);
|
||||
this.akSpritesAlphaGroupBox.Controls.Add(this.akGammaValueLabel);
|
||||
this.akSpritesAlphaGroupBox.Controls.Add(this.akGammaLabel);
|
||||
this.akSpritesAlphaGroupBox.Controls.Add(this.akAlphaMaskGammaTrackBar);
|
||||
this.akSpritesAlphaGroupBox.Location = new System.Drawing.Point(537, 13);
|
||||
this.akSpritesAlphaGroupBox.Name = "akSpritesAlphaGroupBox";
|
||||
this.akSpritesAlphaGroupBox.Size = new System.Drawing.Size(300, 178);
|
||||
this.akSpritesAlphaGroupBox.TabIndex = 4;
|
||||
this.akSpritesAlphaGroupBox.TabStop = false;
|
||||
this.akSpritesAlphaGroupBox.Text = "Sprites: Alpha Texture [Arknights]";
|
||||
//
|
||||
// akGammaNoteLabel
|
||||
//
|
||||
this.akGammaNoteLabel.AutoSize = true;
|
||||
this.akGammaNoteLabel.ForeColor = System.Drawing.SystemColors.GrayText;
|
||||
this.akGammaNoteLabel.Location = new System.Drawing.Point(6, 138);
|
||||
this.akGammaNoteLabel.Name = "akGammaNoteLabel";
|
||||
this.akGammaNoteLabel.Size = new System.Drawing.Size(230, 13);
|
||||
this.akGammaNoteLabel.TabIndex = 8;
|
||||
this.akGammaNoteLabel.Text = "* Gamma settings also affect the preview image";
|
||||
//
|
||||
// akResamplerDescLabel
|
||||
//
|
||||
this.akResamplerDescLabel.AutoSize = true;
|
||||
this.akResamplerDescLabel.ForeColor = System.Drawing.SystemColors.GrayText;
|
||||
this.akResamplerDescLabel.Location = new System.Drawing.Point(6, 43);
|
||||
this.akResamplerDescLabel.Name = "akResamplerDescLabel";
|
||||
this.akResamplerDescLabel.Size = new System.Drawing.Size(251, 13);
|
||||
this.akResamplerDescLabel.TabIndex = 3;
|
||||
this.akResamplerDescLabel.Text = "Alpha texture upscale method for 2048x2048 sprites";
|
||||
//
|
||||
// akResizedOnlyCheckBox
|
||||
//
|
||||
this.akResizedOnlyCheckBox.AutoSize = true;
|
||||
this.akResizedOnlyCheckBox.Checked = true;
|
||||
this.akResizedOnlyCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.akResizedOnlyCheckBox.Location = new System.Drawing.Point(172, 85);
|
||||
this.akResizedOnlyCheckBox.Name = "akResizedOnlyCheckBox";
|
||||
this.akResizedOnlyCheckBox.Size = new System.Drawing.Size(122, 17);
|
||||
this.akResizedOnlyCheckBox.TabIndex = 6;
|
||||
this.akResizedOnlyCheckBox.Text = "Apply to resized only";
|
||||
this.akResizedOnlyCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// akGammaValueLabel
|
||||
//
|
||||
this.akGammaValueLabel.AutoSize = true;
|
||||
this.akGammaValueLabel.Location = new System.Drawing.Point(111, 86);
|
||||
this.akGammaValueLabel.Name = "akGammaValueLabel";
|
||||
this.akGammaValueLabel.Size = new System.Drawing.Size(41, 13);
|
||||
this.akGammaValueLabel.TabIndex = 5;
|
||||
this.akGammaValueLabel.Text = "Default";
|
||||
//
|
||||
// akGammaLabel
|
||||
//
|
||||
this.akGammaLabel.AutoSize = true;
|
||||
this.akGammaLabel.Location = new System.Drawing.Point(6, 86);
|
||||
this.akGammaLabel.Name = "akGammaLabel";
|
||||
this.akGammaLabel.Size = new System.Drawing.Size(86, 13);
|
||||
this.akGammaLabel.TabIndex = 4;
|
||||
this.akGammaLabel.Text = "Shadow gamma:";
|
||||
//
|
||||
// akAlphaMaskGammaTrackBar
|
||||
//
|
||||
this.akAlphaMaskGammaTrackBar.LargeChange = 2;
|
||||
this.akAlphaMaskGammaTrackBar.Location = new System.Drawing.Point(6, 102);
|
||||
this.akAlphaMaskGammaTrackBar.Maximum = 5;
|
||||
this.akAlphaMaskGammaTrackBar.Minimum = -5;
|
||||
this.akAlphaMaskGammaTrackBar.Name = "akAlphaMaskGammaTrackBar";
|
||||
this.akAlphaMaskGammaTrackBar.Size = new System.Drawing.Size(288, 45);
|
||||
this.akAlphaMaskGammaTrackBar.TabIndex = 7;
|
||||
this.akAlphaMaskGammaTrackBar.Scroll += new System.EventHandler(this.akAlphaMaskGammaTrackBar_Scroll);
|
||||
//
|
||||
// akSpritesExportGroupBox
|
||||
//
|
||||
this.akSpritesExportGroupBox.Controls.Add(this.akAddAliasesCheckBox);
|
||||
this.akSpritesExportGroupBox.Location = new System.Drawing.Point(537, 197);
|
||||
this.akSpritesExportGroupBox.Name = "akSpritesExportGroupBox";
|
||||
this.akSpritesExportGroupBox.Size = new System.Drawing.Size(300, 178);
|
||||
this.akSpritesExportGroupBox.TabIndex = 5;
|
||||
this.akSpritesExportGroupBox.TabStop = false;
|
||||
this.akSpritesExportGroupBox.Text = "Sprites: Export [Arknights]";
|
||||
//
|
||||
// akAddAliasesCheckBox
|
||||
//
|
||||
this.akAddAliasesCheckBox.AutoSize = true;
|
||||
this.akAddAliasesCheckBox.Location = new System.Drawing.Point(6, 28);
|
||||
this.akAddAliasesCheckBox.Name = "akAddAliasesCheckBox";
|
||||
this.akAddAliasesCheckBox.Size = new System.Drawing.Size(261, 17);
|
||||
this.akAddAliasesCheckBox.TabIndex = 1;
|
||||
this.akAddAliasesCheckBox.Text = "Add aliases to avg character sprite names (if exist)";
|
||||
this.akAddAliasesCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// ExportOptions
|
||||
//
|
||||
this.AcceptButton = this.OKbutton;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.Cancel;
|
||||
this.ClientSize = new System.Drawing.Size(549, 416);
|
||||
this.ClientSize = new System.Drawing.Size(849, 416);
|
||||
this.Controls.Add(this.l2dGroupBox);
|
||||
this.Controls.Add(this.akSpritesExportGroupBox);
|
||||
this.Controls.Add(this.akSpritesAlphaGroupBox);
|
||||
this.Controls.Add(this.groupBox2);
|
||||
this.Controls.Add(this.groupBox1);
|
||||
this.Controls.Add(this.Cancel);
|
||||
@@ -599,6 +740,11 @@
|
||||
((System.ComponentModel.ISupportInitialize)(this.scaleFactor)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.boneSize)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.filterPrecision)).EndInit();
|
||||
this.akSpritesAlphaGroupBox.ResumeLayout(false);
|
||||
this.akSpritesAlphaGroupBox.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.akAlphaMaskGammaTrackBar)).EndInit();
|
||||
this.akSpritesExportGroupBox.ResumeLayout(false);
|
||||
this.akSpritesExportGroupBox.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@@ -640,6 +786,17 @@
|
||||
private System.Windows.Forms.ToolTip optionTooltip;
|
||||
private System.Windows.Forms.CheckBox exportSpriteWithAlphaMask;
|
||||
private System.Windows.Forms.RadioButton towebp;
|
||||
private System.Windows.Forms.GroupBox akSpritesAlphaGroupBox;
|
||||
private System.Windows.Forms.TrackBar akAlphaMaskGammaTrackBar;
|
||||
private System.Windows.Forms.Label akResamplerDescLabel;
|
||||
private System.Windows.Forms.Label akResamplerLabel;
|
||||
private System.Windows.Forms.ComboBox akResamplerComboBox;
|
||||
private System.Windows.Forms.CheckBox akResizedOnlyCheckBox;
|
||||
private System.Windows.Forms.Label akGammaValueLabel;
|
||||
private System.Windows.Forms.Label akGammaLabel;
|
||||
private System.Windows.Forms.GroupBox akSpritesExportGroupBox;
|
||||
private System.Windows.Forms.CheckBox akAddAliasesCheckBox;
|
||||
private System.Windows.Forms.Label akGammaNoteLabel;
|
||||
private System.Windows.Forms.GroupBox l2dGroupBox;
|
||||
private System.Windows.Forms.CheckBox l2dForceBezierCheckBox;
|
||||
private System.Windows.Forms.Label l2dMotionExportMethodLabel;
|
||||
|
||||
@@ -30,6 +30,14 @@ namespace AssetStudioGUI
|
||||
scaleFactor.Value = Properties.Settings.Default.scaleFactor;
|
||||
fbxVersion.SelectedIndex = Properties.Settings.Default.fbxVersion;
|
||||
fbxFormat.SelectedIndex = Properties.Settings.Default.fbxFormat;
|
||||
|
||||
//Arknights
|
||||
akResamplerComboBox.SelectedIndex = Properties.Settings.Default.resamplerIndex;
|
||||
akAlphaMaskGammaTrackBar.Value = Properties.Settings.Default.alphaMaskGamma;
|
||||
akGammaValueLabel.Text = akAlphaMaskGammaTrackBar.Value == 0 ? "Default" : $"{akAlphaMaskGammaTrackBar.Value * 10:+#;-#;0}%";
|
||||
akResizedOnlyCheckBox.Checked = Properties.Settings.Default.resizedOnly;
|
||||
akAddAliasesCheckBox.Checked = Properties.Settings.Default.addAliases;
|
||||
|
||||
var defaultMotionMode = Properties.Settings.Default.l2dMotionMode.ToString();
|
||||
((RadioButton)l2dMotionExportMethodPanel.Controls.Cast<Control>().First(x => x.AccessibleName == defaultMotionMode)).Checked = true;
|
||||
l2dForceBezierCheckBox.Checked = Properties.Settings.Default.l2dForceBezier;
|
||||
@@ -57,6 +65,13 @@ namespace AssetStudioGUI
|
||||
Properties.Settings.Default.scaleFactor = scaleFactor.Value;
|
||||
Properties.Settings.Default.fbxVersion = fbxVersion.SelectedIndex;
|
||||
Properties.Settings.Default.fbxFormat = fbxFormat.SelectedIndex;
|
||||
|
||||
//Arknights
|
||||
Properties.Settings.Default.resamplerIndex = akResamplerComboBox.SelectedIndex;
|
||||
Properties.Settings.Default.alphaMaskGamma = akAlphaMaskGammaTrackBar.Value;
|
||||
Properties.Settings.Default.resizedOnly = akResizedOnlyCheckBox.Checked;
|
||||
Properties.Settings.Default.addAliases = akAddAliasesCheckBox.Checked;
|
||||
|
||||
var checkedMotionMode = (RadioButton)l2dMotionExportMethodPanel.Controls.Cast<Control>().First(x => ((RadioButton)x).Checked);
|
||||
Properties.Settings.Default.l2dMotionMode = (CubismLive2DExtractor.Live2DMotionMode)Enum.Parse(typeof(CubismLive2DExtractor.Live2DMotionMode), checkedMotionMode.AccessibleName);
|
||||
Properties.Settings.Default.l2dForceBezier = l2dForceBezierCheckBox.Checked;
|
||||
@@ -65,6 +80,12 @@ namespace AssetStudioGUI
|
||||
Close();
|
||||
}
|
||||
|
||||
//Arknights
|
||||
private void akAlphaMaskGammaTrackBar_Scroll(object sender, EventArgs e)
|
||||
{
|
||||
akGammaValueLabel.Text = akAlphaMaskGammaTrackBar.Value == 0 ? "Default" : $"{akAlphaMaskGammaTrackBar.Value * 10:+#;-#;0}%";
|
||||
}
|
||||
|
||||
private void Cancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using AssetStudio;
|
||||
using Arknights;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using AssetStudio;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -238,12 +241,69 @@ namespace AssetStudioGUI
|
||||
}
|
||||
|
||||
public static bool ExportSprite(AssetItem item, string exportPath)
|
||||
{
|
||||
Image<Bgra32> image;
|
||||
AvgSprite avgSprite = null;
|
||||
var alias = "";
|
||||
var m_Sprite = (Sprite)item.Asset;
|
||||
var spriteMaskMode = Properties.Settings.Default.exportSpriteWithMask ? SpriteMaskMode.Export : SpriteMaskMode.Off;
|
||||
var type = Properties.Settings.Default.convertType;
|
||||
var isCharAvgSprite = item.Container.Contains("avg/characters");
|
||||
var isCharArt = item.Container.Contains("arts/characters");
|
||||
|
||||
if (isCharAvgSprite)
|
||||
{
|
||||
avgSprite = new AvgSprite(item);
|
||||
|
||||
if (Properties.Settings.Default.addAliases && !string.IsNullOrEmpty(avgSprite.Alias))
|
||||
{
|
||||
alias = $"_{avgSprite.Alias}";
|
||||
}
|
||||
}
|
||||
|
||||
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath, alias))
|
||||
return false;
|
||||
|
||||
if (Properties.Settings.Default.useExternalAlpha && (isCharAvgSprite || isCharArt))
|
||||
{
|
||||
if (m_Sprite.m_RD.alphaTexture.IsNull)
|
||||
{
|
||||
var charAlphaAtlas = AkSpriteHelper.TryFindAlphaTex(item, avgSprite, isCharAvgSprite);
|
||||
if (charAlphaAtlas != null)
|
||||
{
|
||||
m_Sprite.m_RD.alphaTexture.Set(charAlphaAtlas);
|
||||
m_Sprite.akSplitAlpha = true;
|
||||
}
|
||||
}
|
||||
image = m_Sprite.AkGetImage(avgSprite, spriteMaskMode: spriteMaskMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
image = m_Sprite.GetImage(spriteMaskMode: spriteMaskMode);
|
||||
}
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
using (image)
|
||||
{
|
||||
using (var file = File.OpenWrite(exportFullPath))
|
||||
{
|
||||
image.WriteToStream(file, type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ExportPortraitSprite(AssetItem item, string exportPath)
|
||||
{
|
||||
var type = Properties.Settings.Default.convertType;
|
||||
var spriteMaskMode = Properties.Settings.Default.exportSpriteWithMask ? SpriteMaskMode.Export : SpriteMaskMode.Off;
|
||||
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
|
||||
return false;
|
||||
var image = ((Sprite)item.Asset).GetImage(spriteMaskMode: spriteMaskMode);
|
||||
|
||||
var image = item.AkPortraitSprite.AkGetImage(spriteMaskMode: spriteMaskMode);
|
||||
if (image != null)
|
||||
{
|
||||
using (image)
|
||||
@@ -260,15 +320,17 @@ namespace AssetStudioGUI
|
||||
|
||||
public static bool ExportRawFile(AssetItem item, string exportPath)
|
||||
{
|
||||
if (item.Asset == null)
|
||||
return false;
|
||||
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath)
|
||||
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath, string alias = "")
|
||||
{
|
||||
var fileName = FixFileName(item.Text);
|
||||
var fileName = FixFileName(item.Text) + alias;
|
||||
fullPath = Path.Combine(dir, fileName + extension);
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
@@ -337,6 +399,8 @@ namespace AssetStudioGUI
|
||||
|
||||
public static bool ExportDumpFile(AssetItem item, string exportPath)
|
||||
{
|
||||
if (item.Asset == null)
|
||||
return false;
|
||||
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath))
|
||||
return false;
|
||||
var str = item.Asset.Dump();
|
||||
@@ -377,6 +441,8 @@ namespace AssetStudioGUI
|
||||
return ExportMovieTexture(item, exportPath);
|
||||
case ClassIDType.Sprite:
|
||||
return ExportSprite(item, exportPath);
|
||||
case ClassIDType.AkPortraitSprite:
|
||||
return ExportPortraitSprite(item, exportPath);
|
||||
case ClassIDType.Animator:
|
||||
return ExportAnimator(item, exportPath);
|
||||
case ClassIDType.AnimationClip:
|
||||
|
||||
74
AssetStudioGUI/Properties/Settings.Designer.cs
generated
74
AssetStudioGUI/Properties/Settings.Designer.cs
generated
@@ -287,6 +287,78 @@ namespace AssetStudioGUI.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool fixFaceSpriteNames {
|
||||
get {
|
||||
return ((bool)(this["fixFaceSpriteNames"]));
|
||||
}
|
||||
set {
|
||||
this["fixFaceSpriteNames"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool useExternalAlpha {
|
||||
get {
|
||||
return ((bool)(this["useExternalAlpha"]));
|
||||
}
|
||||
set {
|
||||
this["useExternalAlpha"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool addAliases {
|
||||
get {
|
||||
return ((bool)(this["addAliases"]));
|
||||
}
|
||||
set {
|
||||
this["addAliases"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("3")]
|
||||
public int resamplerIndex {
|
||||
get {
|
||||
return ((int)(this["resamplerIndex"]));
|
||||
}
|
||||
set {
|
||||
this["resamplerIndex"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool resizedOnly {
|
||||
get {
|
||||
return ((bool)(this["resizedOnly"]));
|
||||
}
|
||||
set {
|
||||
this["resizedOnly"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("2")]
|
||||
public int alphaMaskGamma {
|
||||
get {
|
||||
return ((int)(this["alphaMaskGamma"]));
|
||||
}
|
||||
set {
|
||||
this["alphaMaskGamma"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("MonoBehaviour")]
|
||||
@@ -322,7 +394,7 @@ namespace AssetStudioGUI.Properties {
|
||||
this["showConsole"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
|
||||
@@ -68,6 +68,24 @@
|
||||
<Setting Name="exportSpriteWithMask" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="fixFaceSpriteNames" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="useExternalAlpha" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="addAliases" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="resamplerIndex" Type="System.Int32" Scope="User">
|
||||
<Value Profile="(Default)">3</Value>
|
||||
</Setting>
|
||||
<Setting Name="resizedOnly" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="alphaMaskGamma" Type="System.Int32" Scope="User">
|
||||
<Value Profile="(Default)">2</Value>
|
||||
</Setting>
|
||||
<Setting Name="l2dMotionMode" Type="CubismLive2DExtractor.Live2DMotionMode" Scope="User">
|
||||
<Value Profile="(Default)">MonoBehaviour</Value>
|
||||
</Setting>
|
||||
|
||||
@@ -275,8 +275,18 @@ namespace AssetStudioGUI
|
||||
{
|
||||
if (pptr.TryGet(out var obj))
|
||||
{
|
||||
objectAssetItemDic[obj].Container = container;
|
||||
var asset = objectAssetItemDic[obj];
|
||||
asset.Container = container;
|
||||
allContainers[obj] = container;
|
||||
|
||||
if (asset.Type == ClassIDType.MonoBehaviour && container.Contains("/arts/charportraits/portraits"))
|
||||
{
|
||||
var portraitsList = Arknights.AkSpriteHelper.GeneratePortraits(asset);
|
||||
foreach (var portrait in portraitsList)
|
||||
{
|
||||
exportableAssets.Add(new AssetItem(portrait));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var tmp in exportableAssets)
|
||||
@@ -743,7 +753,7 @@ namespace AssetStudioGUI
|
||||
|
||||
public static string DumpAsset(Object obj)
|
||||
{
|
||||
var str = obj.Dump();
|
||||
var str = obj?.Dump();
|
||||
if (str == null && obj is MonoBehaviour m_MonoBehaviour)
|
||||
{
|
||||
var type = MonoBehaviourToTypeTree(m_MonoBehaviour);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>1.1.0</Version>
|
||||
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2023</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace AssetStudio
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D) && m_Sprite.m_RD.alphaTexture.TryGet(out var m_AlphaTexture2D) && spriteMaskMode != SpriteMaskMode.Off)
|
||||
if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D) && m_Sprite.m_RD.alphaTexture.TryGet(out var m_AlphaTexture2D) && spriteMaskMode != SpriteMaskMode.Off && !m_Sprite.akSplitAlpha)
|
||||
{
|
||||
Image<Bgra32> tex = null;
|
||||
if (spriteMaskMode != SpriteMaskMode.MaskOnly)
|
||||
|
||||
124
CHANGELOG.md
124
CHANGELOG.md
@@ -1,92 +1,42 @@
|
||||
# Changelog
|
||||
|
||||
## v0.17.4.0 [16-12-2023]
|
||||
- Added support for Live2D Fade motions
|
||||
- [GUI] Added related settings to the Export Options window
|
||||
- [CLI] Added options section with related settings
|
||||
- Added support for separate PreloadData (https://github.com/Perfare/AssetStudio/issues/690)
|
||||
- [GUI] Added support for .lnk files via Drag&Drop (https://github.com/aelurum/AssetStudio/issues/13)
|
||||
- [GUI] Added support for Drag&Drop files from a browser
|
||||
- [GUI] Added console logger
|
||||
- Added option to save log to a file
|
||||
- [GUI] Added option to not build a tree structure
|
||||
- Made some other minor fixes and improvements
|
||||
## v1.1.0 (based on v0.17.4) [16-12-2023]
|
||||
- Added support for avg sprite without separate alpha tex
|
||||
- Changed avg face sprite detection method, since we can't rely on the "IsWholeBody" flag
|
||||
|
||||
## v0.17.3.0 [13-09-2023]
|
||||
- [CLI] Added support for exporting split objects (fbx) (https://github.com/aelurum/AssetStudio/pull/10)
|
||||
- [CLI] Fixed display of asset names in the exported asset list in some working modes
|
||||
- [CLI] Fixed a bug where the default output folder might not exist
|
||||
- Added support of Texture2D assets from Unity 2022.2+
|
||||
- Fixed AssemblyLoader (https://github.com/aelurum/AssetStudio/issues/6)
|
||||
- [CLI] Added --load-all flag to load assets of all types
|
||||
- [CLI] Improved option grouping on the help screen
|
||||
|
||||
## v0.17.2.0 [27-08-2023]
|
||||
- [GUI] Improved Scene Hierarchy tab
|
||||
- Added "Related assets" item to the context menu (https://github.com/aelurum/AssetStudio/issues/7)
|
||||
- [GUI] Added app.manifest for net472 build
|
||||
- Added long paths support (win10 v1607+)
|
||||
- Fixed blurring at high DPI with scaling
|
||||
- [CLI] Fixed sprite export in sprite only mode
|
||||
- Made some changes to motion list for live2d models
|
||||
- Motion list is now sorted
|
||||
- Motions divided into groups (each motion is a separate group)
|
||||
- Motion names are used as group names
|
||||
- Updated dependencies
|
||||
- Made some other minor fixes and improvements
|
||||
|
||||
## v0.17.1.0 [12-07-2023]
|
||||
#### Breaking Changes
|
||||
- With the drag&drop fix (https://github.com/aelurum/AssetStudio/commit/2f8f57c1a63893c0b0d2a55349d6cb6d8f8a5a3b), functions `LoadFiles` and `LoadFolder` in AssetsManager have been replaced with one universal function `LoadFilesAndFolders`
|
||||
|
||||
#### Changes
|
||||
- Fixed Texture2DDecoderNative compatibility issue with Linux/macOS (CLI preparation #1)
|
||||
- Changed image library to ImageSharp (CLI preparation #2)
|
||||
- Added support for sprites with alpha mask
|
||||
- Sprites with alpha mask can now be viewed and exported with transparency
|
||||
- Added hotkeys to control display of an alpha mask on the preview tab
|
||||
- Added an option to the export settings to enable/disable export with alpha mask as well
|
||||
- Prevented texture2D preview options from being changed with hotkeys outside of texture preview (e.g. when some other asset is selected)
|
||||
- Added image export in WebP format
|
||||
- Updated FMOD to 0.2.0.22 (CLI preparation #3)
|
||||
- Added progress info about zip(apk) file loading process
|
||||
## v1.0.0 (based on v0.17.3) [24-09-2023]
|
||||
- Base version changed to AssetStudioMod v0.17.x
|
||||
- Replaced NetVips lib with ImageSharp lib
|
||||
- Removed arknights related grouping options
|
||||
- Added option to export avg character sprites with aliases in their names (if exist)
|
||||
- Added support for portrait sprites
|
||||
- Added CLI version
|
||||
- [GUI] Added context menu with "Select all", "Clear selection", "Expand all" and "Collapse all" options to the "Scene Hierarchy" tab
|
||||
- Selected objects count is now displayed in the status bar
|
||||
- [GUI] Improved error handling
|
||||
- [CLI] Added support for partial assets reading
|
||||
- [GUI] Added some videoClip info to preview tab
|
||||
- [GUI] Improved memory usage of image previews
|
||||
- Disabled Shader support for Unity > 2020
|
||||
- Added error message for bundles with UnityCN encryption
|
||||
- Added error message on incorrect format of specified Unity version
|
||||
- Block alignment fix for Unity 2019.4.X (source: https://github.com/K0lb3/UnityPy/commit/10346b4f02f2dbe0fa707799130c9f83c24f8e24)
|
||||
- [GUI] Added "About" window
|
||||
- Fixed cutout glitch in some packed sprites (https://github.com/Perfare/AssetStudio/issues/1015)
|
||||
- Optimized drawing performance of packed sprites
|
||||
- [GUI] Improved asset list filtering
|
||||
- Added filter history
|
||||
- Added more filtering modes: Include, Exclude, Regex (Name/Container)
|
||||
- Added grouping option with full container path (https://github.com/Perfare/AssetStudio/issues/815)
|
||||
- [GUI] - "container path full (with name)"
|
||||
- [CLI] - "containerFull"
|
||||
- Improved "Restore TextAsset extension name" option
|
||||
- If checked, AssetStudio will first try to find an extension in an asset's name and only then in its container. If no extension is found, ".txt" will be used
|
||||
- [GUI] Fixed audio player position in maximized window
|
||||
- [GUI] Improved file and folder loading (drag&drop)
|
||||
- Added support for drag&drop of multiple folders
|
||||
- Open/Export dialog can now also use a path taken from drag&drop items
|
||||
- [GUI] Added showing of progress bar in the taskbar button
|
||||
- Added option to export Live2D Cubism 3 models
|
||||
- Fixed some bugs
|
||||
|
||||
## v0.16.8.1 [25-11-2021]
|
||||
- Uses System.Drawing lib instead of ImageSharp for process textures
|
||||
- Added alphanumeric sorting to the column with asset names for more natural presentation of asset list
|
||||
- Improved "Copy text" option in right click menu, to display what exactly to copy
|
||||
- Added "Dump selected assets" option to right click menu
|
||||
- Added 'selected assets count' info to status strip when you select assets
|
||||
- Added 'exported count / total export count' info to status strip during export
|
||||
- "Show error message" option on the "Debug" tab has been renamed to "Show all error messages" and is now disabled by default
|
||||
- "Fixed" an issue with getting stuck during the "Building tree structure" step
|
||||
- Fixed a bug with listSearch that could make it not work in some conditions
|
||||
- Fixed a rare bug for resource files with the same name, that caused their data to be overwritten and become incorrect
|
||||
## v0.15.47.4 [25-01-2022]
|
||||
- Fixed bug with wrong "isWholeBody" flag for the last sprite in a sprite list
|
||||
|
||||
## v0.15.47.3 [17-09-2021]
|
||||
- Added support for avg Sprite Groups
|
||||
|
||||
## v0.15.47.2 [04-08-2021]
|
||||
- Added support for avg character sprites with "isWholeBody" flag
|
||||
|
||||
## v0.15.47.1 [06-02-2021]
|
||||
- Added support for avg character face sprites
|
||||
- Added option to fix avg character sprite names
|
||||
- Added NetVips lib for sprite processing
|
||||
|
||||
## v0.15.20.3 [24-09-2020]
|
||||
- Added support for sprites with an external alpha texture
|
||||
- Added support of alpha texture resizing for 2048x2048 sprites
|
||||
|
||||
## v0.15.20.2 [09-09-2020]
|
||||
- Added support for sprites with alpha textures
|
||||
- Added some arknights related grouping options to the "Export options" window
|
||||
|
||||
## v0.15.20.1 [01-09-2020]
|
||||
- Base version updated to v0.15.x
|
||||
|
||||
## v0.14.38.1 [2020]
|
||||
- Initial version
|
||||
|
||||
145
README.md
145
README.md
@@ -1,110 +1,83 @@
|
||||
# AssetStudioMod
|
||||
# ArknightsStudio
|
||||
|
||||
[](https://ci.appveyor.com/project/aelurum/assetstudiomod/branch/AssetStudioMod)
|
||||
[](https://ci.appveyor.com/project/aelurum/arknightsstudio)
|
||||
|
||||
**AssetStudioMod** - modified version of Perfare's [AssetStudio](https://github.com/Perfare/AssetStudio), mainly focused on UI optimization and some functionality enhancements.
|
||||
**ArknightsStudio** is a modified version of AssetStudio designed for Arknights. Based on [AssetStudioMod](https://github.com/aelurum/AssetStudio).
|
||||
|
||||
**Neither the repository, nor the tool, nor the author of the tool, nor the author of the modification is affiliated with, sponsored, or authorized by Unity Technologies or its affiliates.**
|
||||
|
||||
Since the original repo has been archived, it's worth saying that you shouldn't expect support for newer versions of Unity from this fork.
|
||||
Unfortunately, I can't continue Perfare's work and keep AssetStudio up to date.
|
||||
|
||||
## Game specific modifications
|
||||
|
||||
- [ArknightsStudio](https://github.com/aelurum/AssetStudio/tree/ArknightsStudio)
|
||||
|
||||
## AssetStudio Features
|
||||
|
||||
- Support version:
|
||||
- 3.4 - 2022.3
|
||||
- Support asset types:
|
||||
- **Texture2D** : convert to png, tga, jpeg, bmp, webp
|
||||
- **Sprite** : crop Texture2D to png, tga, jpeg, bmp, webp
|
||||
- **AudioClip** : mp3, ogg, wav, m4a, fsb. Support converting FSB file to WAV(PCM)
|
||||
- **Font** : ttf, otf
|
||||
- **Mesh** : obj
|
||||
- **TextAsset**
|
||||
- **Shader** (for Unity < 2021)
|
||||
- **MovieTexture**
|
||||
- **VideoClip**
|
||||
- **MonoBehaviour** : json
|
||||
- **Animator** : export to FBX file with bound AnimationClip
|
||||
|
||||
## AssetStudioMod Features
|
||||
## ArknightsStudio Features
|
||||
|
||||
- CLI version (for Windows, Linux, Mac)
|
||||
- `Animator` and `AnimationClip` assets are not supported in the CLI version
|
||||
- Support of sprites with alpha mask
|
||||
- Support of image export in WebP format
|
||||
- Support of Live2D Cubism model export
|
||||
- Ported from my fork of Perfare's [UnityLive2DExtractor](https://github.com/aelurum/UnityLive2DExtractor)
|
||||
- Using the Live2D export in AssetStudio allows you to specify a Unity version and assembly folder if needed
|
||||
- Detecting bundles with UnityCN encryption
|
||||
- Detection only. If you want to open them, please use Razmoth's [Studio](https://github.com/RazTools/Studio)
|
||||
- Some UI optimizations and bug fixes (See [CHANGELOG](https://github.com/aelurum/AssetStudio/blob/AssetStudioMod/CHANGELOG.md) for details)
|
||||
|
||||
- Support of sprites with alpha texture
|
||||
- Support of portrait sprites
|
||||
- Correct support of avg character sprites
|
||||
- Correct support of character art sprites
|
||||
|
||||
## Requirements
|
||||
|
||||
- AssetStudioMod.net472
|
||||
- ArknightsStudio-net472
|
||||
- GUI/CLI - [.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472)
|
||||
- AssetStudioMod.net6
|
||||
- ArknightsStudio-net6
|
||||
- GUI/CLI (Windows) - [.NET Desktop Runtime 6.0](https://dotnet.microsoft.com/download/dotnet/6.0)
|
||||
- CLI (Linux/Mac) - [.NET Runtime 6.0](https://dotnet.microsoft.com/download/dotnet/6.0)
|
||||
- AssetStudioMod.net7
|
||||
- ArknightsStudio-net7
|
||||
- GUI/CLI (Windows) - [.NET Desktop Runtime 7.0](https://dotnet.microsoft.com/download/dotnet/7.0)
|
||||
- CLI (Linux/Mac) - [.NET Runtime 7.0](https://dotnet.microsoft.com/download/dotnet/7.0)
|
||||
- AssetStudioMod.net8
|
||||
- GUI/CLI (Windows) - [.NET Desktop Runtime 8.0](https://dotnet.microsoft.com/download/dotnet/8.0)
|
||||
- CLI (Linux/Mac) - [.NET Runtime 8.0](https://dotnet.microsoft.com/download/dotnet/8.0)
|
||||
- ArknightsStudio-net8
|
||||
- GUI/CLI (Windows) - [.NET Desktop Runtime 7.0](https://dotnet.microsoft.com/download/dotnet/8.0)
|
||||
- CLI (Linux/Mac) - [.NET Runtime 7.0](https://dotnet.microsoft.com/download/dotnet/8.0)
|
||||
|
||||
## CLI Usage
|
||||
|
||||
You can read CLI readme [here](https://github.com/aelurum/AssetStudio/blob/AssetStudioMod/AssetStudioCLI/ReadMe.md).
|
||||
You can read CLI readme [here](https://github.com/aelurum/AssetStudio/blob/ArknightsStudio/AssetStudioCLI/ReadMe.md).
|
||||
|
||||
### Run
|
||||
|
||||
- Command-line: `AssetStudioModCLI <asset folder path>`
|
||||
- Command-line for Portable versions (.NET 6+): `dotnet AssetStudioModCLI.dll <asset folder path>`
|
||||
- Command-line: `ArknightsStudioCLI <asset folder path>`
|
||||
- Command-line for Portable versions (.NET 6+): `dotnet ArknightsStudioCLI.dll <asset folder path>`
|
||||
|
||||
### Basic Samples
|
||||
|
||||
- Show a list with a number of assets of each type available for export
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m info
|
||||
ArknightsStudioCLI <asset folder path> -m info
|
||||
```
|
||||
- Export assets of all supported for export types
|
||||
```
|
||||
AssetStudioModCLI <asset folder path>
|
||||
ArknightsStudioCLI <asset folder path>
|
||||
```
|
||||
- Export assets of specific types
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t tex2d
|
||||
ArknightsStudioCLI <asset folder path> -t sprite
|
||||
```
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t tex2d,sprite,audio
|
||||
ArknightsStudioCLI <asset folder path> -t tex2d,sprite,audio
|
||||
```
|
||||
- Export portrait sprites
|
||||
```
|
||||
ArknightsStudioCLI <asset folder path> -t akPortrait
|
||||
```
|
||||
- Export assets grouped by type
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -g type
|
||||
ArknightsStudioCLI <asset folder path> -g type
|
||||
```
|
||||
- Export assets to a specified output folder
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -o <output folder path>
|
||||
ArknightsStudioCLI <asset folder path> -o <output folder path>
|
||||
```
|
||||
- Dump assets to a specified output folder
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m dump -o <output folder path>
|
||||
ArknightsStudioCLI <asset folder path> -m dump -o <output folder path>
|
||||
```
|
||||
- Export Live2D Cubism models
|
||||
- Export assets and create a log file
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m live2d
|
||||
ArknightsStudioCLI <asset folder path> --log-output both
|
||||
```
|
||||
> When running in live2d mode you can only specify `-o`, `--log-level`, `--log-output`, `--l2d-motion-mode`, `--l2d-force-bezier`, `--export-asset-list`, `--unity-version` and `--assembly-folder` options.
|
||||
Any other options will be ignored.
|
||||
- Export all FBX objects (similar to "Export all objects (split)" option in the GUI)
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m splitObjects
|
||||
ArknightsStudioCLI <asset folder path> -m splitObjects
|
||||
```
|
||||
> When running in splitObjects mode you can only specify `-o`, `--log-level`, `--log-output`, `--export-asset-list`, `--image-format`, `--filter-by-name` and `--unity-version` options.
|
||||
Any other options will be ignored.
|
||||
@@ -112,50 +85,50 @@ Any other options will be ignored.
|
||||
### Advanced Samples
|
||||
- Export image assets converted to webp format to a specified output folder
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -o <output folder path> -t sprite,tex2d --image-format webp
|
||||
ArknightsStudioCLI <asset folder path> -o <output folder path> -t sprite,akPortrait,tex2d --image-format webp
|
||||
```
|
||||
- Show the number of audio assets that have "voice" in their names
|
||||
- Export avg character sprites with aliases in their names
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m info -t audio --filter-by-name voice
|
||||
ArknightsStudioCLI <asset folder path> -t sprite --add-aliases
|
||||
```
|
||||
- Export audio assets that have "voice" in their names
|
||||
- Export character art sprites without brightness change of semi-transparent shadow for 2048x2048 images
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t audio --filter-by-name voice
|
||||
ArknightsStudioCLI <asset folder path> -t sprite --shadow-gamma 0
|
||||
```
|
||||
- Export audio assets that have "music" or "voice" in their names
|
||||
- Show the number of audio assets that have "voice" in their containers
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t audio --filter-by-name music,voice
|
||||
ArknightsStudioCLI <asset folder path> -m info -t audio --filter-by-container voice
|
||||
```
|
||||
- Export audio assets that have "voice" in their containers
|
||||
```
|
||||
ArknightsStudioCLI <asset folder path> -t audio --filter-by-container voice
|
||||
```
|
||||
- Export audio assets that have "music" or "voice" in their containers
|
||||
```
|
||||
ArknightsStudioCLI <asset folder path> -t audio --filter-by-container music,voice
|
||||
```
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t audio --filter-by-name music --filter-by-name voice
|
||||
ArknightsStudioCLI <asset folder path> -t audio --filter-by-container music --filter-by-container voice
|
||||
```
|
||||
- Export audio assets that have "char" in their names **or** containers
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t audio --filter-by-text char
|
||||
ArknightsStudioCLI <asset folder path> -t audio --filter-by-text char
|
||||
```
|
||||
- Export audio assets that have "voice" in their names **and** "char" in their containers
|
||||
- Export audio assets that have "loop" in their names **and** "music" in their containers
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t audio --filter-by-name voice --filter-by-container char
|
||||
ArknightsStudioCLI <asset folder path> -t audio --filter-by-name loop --filter-by-container music
|
||||
```
|
||||
- Export FBX objects that have "model" or "scene" in their names and set the scale factor to 10
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m splitObjects --filter-by-name model,scene --fbx-scale-factor 10
|
||||
```
|
||||
- Export MonoBehaviour assets that require an assembly folder to read and create a log file
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t monobehaviour --assembly-folder <assembly folder path> --log-output both
|
||||
```
|
||||
- Export assets that require to specify a Unity version
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> --unity-version 2017.4.39f1
|
||||
ArknightsStudioCLI <asset folder path> -m splitObjects --filter-by-name model,scene --fbx-scale-factor 10
|
||||
```
|
||||
- Load assets of all types and show them (similar to "Display all assets" option in the GUI)
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m info --load-all
|
||||
ArknightsStudioCLI <asset folder path> -m info --load-all
|
||||
```
|
||||
- Load assets of all types and dump Material assets
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m dump -t material --load-all
|
||||
ArknightsStudioCLI <asset folder path> -m dump -t material --load-all
|
||||
```
|
||||
|
||||
## GUI Usage
|
||||
@@ -164,13 +137,13 @@ AssetStudioModCLI <asset folder path> -m dump -t material --load-all
|
||||
|
||||
Use **File->Load file** or **File->Load folder**.
|
||||
|
||||
When AssetStudio loads AssetBundles, it decompresses and reads it directly in memory, which may cause a large amount of memory to be used. You can use **File->Extract file** or **File->Extract folder** to extract AssetBundles to another folder, and then read.
|
||||
When ArknightsStudio loads AssetBundles, it decompresses and reads it directly in memory, which may cause a large amount of memory to be used. You can use **File->Extract file** or **File->Extract folder** to extract AssetBundles to another folder, and then read.
|
||||
|
||||
### Extract/Decompress AssetBundles
|
||||
|
||||
Use **File->Extract file** or **File->Extract folder**.
|
||||
|
||||
### Export Assets, Live2D models
|
||||
### Export Assets
|
||||
|
||||
Use **Export** menu.
|
||||
|
||||
@@ -186,14 +159,6 @@ Select model from "Scene Hierarchy" then select the AnimationClip from "Asset Li
|
||||
|
||||
Export Animator will export bound AnimationClip or use **Ctrl** to select Animator and AnimationClip from "Asset List", using **Export->Export Animator with selected AnimationClip** to export.
|
||||
|
||||
### Export MonoBehaviour
|
||||
|
||||
When you select an asset of the MonoBehaviour type for the first time, AssetStudio will ask you the directory where the assembly is located, please select the directory where the assembly is located, such as the `Managed` folder.
|
||||
|
||||
#### For Il2Cpp
|
||||
|
||||
First, use [Il2CppDumper](https://github.com/Perfare/Il2CppDumper) to generate dummy dll, then when using AssetStudio to select the assembly directory, select the dummy dll folder.
|
||||
|
||||
## Build
|
||||
|
||||
* Visual Studio 2022 or newer
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Version>1.1.0</Version>
|
||||
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
Reference in New Issue
Block a user