mirror of
https://github.com/aelurum/AssetStudio.git
synced 2025-05-27 22:00:23 -04:00
[AK][CLI] Add support for sprites with an external alpha texture
- Added support for Arknights chararts sprites - Added support for Arknights avg sprites
This commit is contained in:
parent
abbd27fde7
commit
3d7d51b54f
@ -39,7 +39,12 @@ namespace AssetStudio
|
|||||||
}
|
}
|
||||||
if (classIDTypes.Contains(ClassIDType.Sprite))
|
if (classIDTypes.Contains(ClassIDType.Sprite))
|
||||||
{
|
{
|
||||||
filteredAssetTypesList.Add(ClassIDType.Texture2D);
|
filteredAssetTypesList.UnionWith(new HashSet<ClassIDType>
|
||||||
|
{
|
||||||
|
ClassIDType.Texture2D,
|
||||||
|
ClassIDType.MonoBehaviour,
|
||||||
|
ClassIDType.MonoScript
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredAssetTypesList.UnionWith(classIDTypes);
|
filteredAssetTypesList.UnionWith(classIDTypes);
|
||||||
|
151
AssetStudioCLI/Components/Arknights/AkSpriteHelper.cs
Normal file
151
AssetStudioCLI/Components/Arknights/AkSpriteHelper.cs
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
using AssetStudio;
|
||||||
|
using AssetStudioCLI;
|
||||||
|
using AssetStudioCLI.Options;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
using System;
|
||||||
|
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;
|
||||||
|
else 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 (faceImage.Size() != 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, location: avgSprite.FacePos, opacity: 1f));
|
||||||
|
alphaTex.Mutate(x => x.DrawImage(faceAlpha, location: 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))
|
||||||
|
{
|
||||||
|
return CutImage(m_Texture2D.ConvertToImage(false), m_Sprite.m_RD.textureRect, m_Sprite.m_RD.downscaleMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_akAlphaMaskResampler.Value));
|
||||||
|
resized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var invGamma = 1.0 / (1.0 + CLIOptions.o_akAlphaMaskGamma.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)
|
||||||
|
{
|
||||||
|
if (originalImage != null)
|
||||||
|
{
|
||||||
|
using (originalImage)
|
||||||
|
{
|
||||||
|
if (downscaleMultiplier > 0f && downscaleMultiplier != 1f)
|
||||||
|
{
|
||||||
|
var newSize = (Size)(originalImage.Size() / 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));
|
||||||
|
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||||
|
|
||||||
|
return spriteImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
125
AssetStudioCLI/Components/Arknights/AvgSprite.cs
Normal file
125
AssetStudioCLI/Components/Arknights/AvgSprite.cs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
using Arknights.AvgCharHub;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var fullTexSpriteData = spriteHubData.Sprites.Last(); //Last sprite item in the list usually contains PathID of Sprite with full texture
|
||||||
|
if (IsWholeBodySprite || curSpriteData.Equals(fullTexSpriteData))
|
||||||
|
{
|
||||||
|
fullTexSpriteData = curSpriteData;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var faceAlphaID = curSpriteData.AlphaTex.m_PathID;
|
||||||
|
FaceSpriteAlphaTexture = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == faceAlphaID).Asset;
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
IsFaceSprite = assetItem.m_PathID != fullTexSpriteID;
|
||||||
|
}
|
||||||
|
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.AvgCharHub
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
using AssetStudio;
|
using Arknights;
|
||||||
|
using AssetStudio;
|
||||||
using AssetStudioCLI.Options;
|
using AssetStudioCLI.Options;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -204,11 +207,56 @@ namespace AssetStudioCLI
|
|||||||
|
|
||||||
public static bool ExportSprite(AssetItem item, string exportPath)
|
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 type = CLIOptions.o_imageFormat.Value;
|
||||||
var alphaMask = SpriteMaskMode.On;
|
var spriteMaskMode = CLIOptions.o_akSpriteMaskMode.Value != AkSpriteMaskMode.None ? SpriteMaskMode.Export : SpriteMaskMode.Off;
|
||||||
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
|
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)
|
||||||
|
{
|
||||||
|
if ((m_Sprite.m_Name.Length < 3 && m_Sprite.m_Name.All(char.IsDigit)) //not grouped ("spriteIndex")
|
||||||
|
|| (m_Sprite.m_Name.Length < 5 && m_Sprite.m_Name.Contains('$') && m_Sprite.m_Name.Split('$')[0].All(char.IsDigit))) //grouped ("spriteIndex$groupIndex")
|
||||||
|
{
|
||||||
|
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;
|
return false;
|
||||||
var image = ((Sprite)item.Asset).GetImage(alphaMask);
|
|
||||||
|
if (CLIOptions.o_akSpriteMaskMode.Value == AkSpriteMaskMode.External && (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)
|
if (image != null)
|
||||||
{
|
{
|
||||||
using (image)
|
using (image)
|
||||||
@ -253,9 +301,9 @@ namespace AssetStudioCLI
|
|||||||
return false;
|
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);
|
fullPath = Path.Combine(dir, fileName + extension);
|
||||||
if (!File.Exists(fullPath))
|
if (!File.Exists(fullPath))
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using AssetStudio;
|
using AssetStudio;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
using SixLabors.ImageSharp.Processing.Processors.Transforms;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -12,6 +14,7 @@ namespace AssetStudioCLI.Options
|
|||||||
General,
|
General,
|
||||||
Convert,
|
Convert,
|
||||||
Logger,
|
Logger,
|
||||||
|
Arknights,
|
||||||
Advanced,
|
Advanced,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +58,13 @@ namespace AssetStudioCLI.Options
|
|||||||
NameAndContainer,
|
NameAndContainer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal enum AkSpriteMaskMode
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Internal,
|
||||||
|
External
|
||||||
|
}
|
||||||
|
|
||||||
internal static class CLIOptions
|
internal static class CLIOptions
|
||||||
{
|
{
|
||||||
public static bool isParsed;
|
public static bool isParsed;
|
||||||
@ -79,6 +89,14 @@ namespace AssetStudioCLI.Options
|
|||||||
public static bool convertTexture;
|
public static bool convertTexture;
|
||||||
public static Option<ImageFormat> o_imageFormat;
|
public static Option<ImageFormat> o_imageFormat;
|
||||||
public static Option<AudioFormat> o_audioFormat;
|
public static Option<AudioFormat> o_audioFormat;
|
||||||
|
//arknights
|
||||||
|
public static bool akResizedOnly;
|
||||||
|
public static Option<AkSpriteMaskMode> o_akSpriteMaskMode;
|
||||||
|
public static Option<IResampler> o_akAlphaMaskResampler;
|
||||||
|
private static string resamplerName;
|
||||||
|
public static Option<int> o_akAlphaMaskGamma;
|
||||||
|
public static Option<bool> f_akOriginalAvgNames;
|
||||||
|
public static Option<bool> f_akAddAliases;
|
||||||
//advanced
|
//advanced
|
||||||
public static Option<ExportListType> o_exportAssetList;
|
public static Option<ExportListType> o_exportAssetList;
|
||||||
public static Option<List<string>> o_filterByName;
|
public static Option<List<string>> o_filterByName;
|
||||||
@ -249,6 +267,65 @@ namespace AssetStudioCLI.Options
|
|||||||
);
|
);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Arknights Options
|
||||||
|
akResizedOnly = true;
|
||||||
|
o_akSpriteMaskMode = new GroupedOption<AkSpriteMaskMode>
|
||||||
|
(
|
||||||
|
optionDefaultValue: AkSpriteMaskMode.External,
|
||||||
|
optionName: "--spritemask-mode <value>",
|
||||||
|
optionDescription: "Specify the mode in which you want to export sprites with alpha mask\n" +
|
||||||
|
"<Value: none | internalOnly | searchExternal(default)>\n" +
|
||||||
|
"None - Export sprites without alpha mask applied\n" +
|
||||||
|
"Internal - Export sprites with internal alpha mask applied (if exist)\n" +
|
||||||
|
"SearchExternal - Export sprites with internal alpha mask applied,\n" +
|
||||||
|
"and in case it doesn't exist, Studio will try to find an external alpha mask\n" +
|
||||||
|
"Example: \"--spritemask-mode internalOnly\"\n",
|
||||||
|
optionHelpGroup: HelpGroups.Arknights
|
||||||
|
);
|
||||||
|
o_akAlphaMaskResampler = new GroupedOption<IResampler>
|
||||||
|
(
|
||||||
|
optionDefaultValue: KnownResamplers.MitchellNetravali,
|
||||||
|
optionName: "--alphamask-resampler <value>",
|
||||||
|
optionDescription: "Specify the alpha mask 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 case of alpha masks)\n" +
|
||||||
|
"Spline - Similar to Mitchell Netravali but yielding smoother results\n" +
|
||||||
|
"Welch - A high speed algorithm that delivers very sharpened results\n" +
|
||||||
|
"Example: \"--alphamask-resampler bicubic\"\n",
|
||||||
|
optionHelpGroup: HelpGroups.Arknights
|
||||||
|
);
|
||||||
|
resamplerName = "Mitchell";
|
||||||
|
o_akAlphaMaskGamma = new GroupedOption<int>
|
||||||
|
(
|
||||||
|
optionDefaultValue: 2,
|
||||||
|
optionName: "--alphamask-gamma <value>",
|
||||||
|
optionDescription: "Specify the alpha mask gamma correction for 2048x2048 sprites\n" +
|
||||||
|
"<Value: integer number from -5 to 5 (default=2)>\n" +
|
||||||
|
"<0 - Make the alpha mask darker\n" +
|
||||||
|
"0 - Do not change the gamma of alpha mask\n" +
|
||||||
|
">0 - Make the alpha mask lighter\n" +
|
||||||
|
"Example: \"--alphamask-gamma 0\"\n",
|
||||||
|
optionHelpGroup: HelpGroups.Arknights
|
||||||
|
);
|
||||||
|
f_akOriginalAvgNames = new GroupedOption<bool>
|
||||||
|
(
|
||||||
|
optionDefaultValue: false,
|
||||||
|
optionName: "--original-avg-names",
|
||||||
|
optionDescription: "(Flag) If specified, names of avg sprites with faces will not be fixed\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 sprite names (if exist)",
|
||||||
|
optionHelpGroup: HelpGroups.Arknights,
|
||||||
|
isFlag: true
|
||||||
|
);
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Init Advanced Options
|
#region Init Advanced Options
|
||||||
o_exportAssetList = new GroupedOption<ExportListType>
|
o_exportAssetList = new GroupedOption<ExportListType>
|
||||||
(
|
(
|
||||||
@ -315,7 +392,7 @@ namespace AssetStudioCLI.Options
|
|||||||
(
|
(
|
||||||
optionDefaultValue: false,
|
optionDefaultValue: false,
|
||||||
optionName: "--not-restore-extension",
|
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",
|
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",
|
||||||
optionHelpGroup: HelpGroups.Advanced,
|
optionHelpGroup: HelpGroups.Advanced,
|
||||||
isFlag: true
|
isFlag: true
|
||||||
);
|
);
|
||||||
@ -380,6 +457,14 @@ namespace AssetStudioCLI.Options
|
|||||||
f_notRestoreExtensionName.Value = true;
|
f_notRestoreExtensionName.Value = true;
|
||||||
resplittedArgs.RemoveAt(i);
|
resplittedArgs.RemoveAt(i);
|
||||||
break;
|
break;
|
||||||
|
case "--original-avg-names":
|
||||||
|
f_akOriginalAvgNames.Value = true;
|
||||||
|
resplittedArgs.RemoveAt(i);
|
||||||
|
break;
|
||||||
|
case "--add-aliases":
|
||||||
|
f_akAddAliases.Value = true;
|
||||||
|
resplittedArgs.RemoveAt(i);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@ -617,6 +702,65 @@ namespace AssetStudioCLI.Options
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "--spritemask-mode":
|
||||||
|
switch (value.ToLower())
|
||||||
|
{
|
||||||
|
case "none":
|
||||||
|
o_akSpriteMaskMode.Value = AkSpriteMaskMode.None;
|
||||||
|
break;
|
||||||
|
case "internal":
|
||||||
|
o_akSpriteMaskMode.Value = AkSpriteMaskMode.Internal;
|
||||||
|
break;
|
||||||
|
case "searchexternal":
|
||||||
|
o_akSpriteMaskMode.Value = AkSpriteMaskMode.External;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported sprite mask mode: [{value.Color(brightRed)}].\n");
|
||||||
|
Console.WriteLine(o_akSpriteMaskMode.Description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "--alphamask-resampler":
|
||||||
|
switch (value.ToLower())
|
||||||
|
{
|
||||||
|
case "nearest":
|
||||||
|
o_akAlphaMaskResampler.Value = KnownResamplers.NearestNeighbor;
|
||||||
|
break;
|
||||||
|
case "bilinear":
|
||||||
|
o_akAlphaMaskResampler.Value = KnownResamplers.Triangle;
|
||||||
|
break;
|
||||||
|
case "bicubic":
|
||||||
|
o_akAlphaMaskResampler.Value = KnownResamplers.Bicubic;
|
||||||
|
break;
|
||||||
|
case "mitchell":
|
||||||
|
o_akAlphaMaskResampler.Value = KnownResamplers.MitchellNetravali;
|
||||||
|
break;
|
||||||
|
case "spline":
|
||||||
|
o_akAlphaMaskResampler.Value = KnownResamplers.Spline;
|
||||||
|
break;
|
||||||
|
case "welch":
|
||||||
|
o_akAlphaMaskResampler.Value = KnownResamplers.Welch;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported alpha mask resampler: [{value.Color(brightRed)}].\n");
|
||||||
|
Console.WriteLine(o_akAlphaMaskResampler.Description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resamplerName = value.ToLower();
|
||||||
|
break;
|
||||||
|
case "--alphamask-gamma":
|
||||||
|
var isInt = int.TryParse(value, out int intValue);
|
||||||
|
if (isInt && intValue >= -5 && intValue <= 5)
|
||||||
|
{
|
||||||
|
o_akAlphaMaskGamma.Value = intValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported gamma correction value: [{value.Color(brightRed)}].\n");
|
||||||
|
Console.WriteLine(o_akAlphaMaskGamma.Description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "--export-asset-list":
|
case "--export-asset-list":
|
||||||
switch (value.ToLower())
|
switch (value.ToLower())
|
||||||
{
|
{
|
||||||
@ -815,6 +959,11 @@ namespace AssetStudioCLI.Options
|
|||||||
sb.AppendLine($"# Asset Group Option: {o_groupAssetsBy}");
|
sb.AppendLine($"# Asset Group Option: {o_groupAssetsBy}");
|
||||||
sb.AppendLine($"# Export Image Format: {o_imageFormat}");
|
sb.AppendLine($"# Export Image Format: {o_imageFormat}");
|
||||||
sb.AppendLine($"# Export Audio Format: {o_audioFormat}");
|
sb.AppendLine($"# Export Audio Format: {o_audioFormat}");
|
||||||
|
sb.AppendLine($"# [Arkingths] Sprite Mode: {o_akSpriteMaskMode}");
|
||||||
|
sb.AppendLine($"# [Arknights] Mask Resampler: {resamplerName}");
|
||||||
|
sb.AppendLine($"# [Arknights] Mask Gamma Correction: {o_akAlphaMaskGamma.Value * 10:+#;-#;0}%");
|
||||||
|
sb.AppendLine($"# [Arknights] Original Avg Names: {f_akOriginalAvgNames}");
|
||||||
|
sb.AppendLine($"# [Arknights] Add Aliases: {f_akAddAliases}");
|
||||||
sb.AppendLine($"# Log Level: {o_logLevel}");
|
sb.AppendLine($"# Log Level: {o_logLevel}");
|
||||||
sb.AppendLine($"# Log Output: {o_logOutput}");
|
sb.AppendLine($"# Log Output: {o_logOutput}");
|
||||||
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
|
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
|
||||||
|
@ -14,7 +14,8 @@ namespace AssetStudioCLI
|
|||||||
internal static class Studio
|
internal static class Studio
|
||||||
{
|
{
|
||||||
public static AssetsManager assetsManager = new AssetsManager();
|
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 AssemblyLoader assemblyLoader = new AssemblyLoader();
|
public static AssemblyLoader assemblyLoader = new AssemblyLoader();
|
||||||
private static Dictionary<AssetStudio.Object, string> containers = new Dictionary<AssetStudio.Object, string>();
|
private static Dictionary<AssetStudio.Object, string> containers = new Dictionary<AssetStudio.Object, string>();
|
||||||
|
|
||||||
@ -51,7 +52,6 @@ namespace AssetStudioCLI
|
|||||||
{
|
{
|
||||||
Logger.Info("Parse assets...");
|
Logger.Info("Parse assets...");
|
||||||
|
|
||||||
var fileAssetsList = new List<AssetItem>();
|
|
||||||
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
|
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
|
||||||
|
|
||||||
Progress.Reset();
|
Progress.Reset();
|
||||||
@ -131,29 +131,28 @@ namespace AssetStudioCLI
|
|||||||
assetItem.Text = assetItem.TypeString + assetItem.UniqueID;
|
assetItem.Text = assetItem.TypeString + assetItem.UniqueID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadedAssetsList.Add(assetItem);
|
||||||
isExportable = CLIOptions.o_exportAssetTypes.Value.Contains(asset.type);
|
isExportable = CLIOptions.o_exportAssetTypes.Value.Contains(asset.type);
|
||||||
if (isExportable)
|
if (isExportable)
|
||||||
{
|
{
|
||||||
fileAssetsList.Add(assetItem);
|
exportableAssetsList.Add(assetItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
Progress.Report(++i, objectCount);
|
Progress.Report(++i, objectCount);
|
||||||
}
|
}
|
||||||
foreach (var asset in fileAssetsList)
|
foreach (var asset in loadedAssetsList)
|
||||||
{
|
{
|
||||||
if (containers.ContainsKey(asset.Asset))
|
if (containers.ContainsKey(asset.Asset))
|
||||||
{
|
{
|
||||||
asset.Container = containers[asset.Asset];
|
asset.Container = containers[asset.Asset];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parsedAssetsList.AddRange(fileAssetsList);
|
|
||||||
fileAssetsList.Clear();
|
|
||||||
if (CLIOptions.o_workMode.Value != WorkMode.ExportLive2D)
|
if (CLIOptions.o_workMode.Value != WorkMode.ExportLive2D)
|
||||||
{
|
{
|
||||||
containers.Clear();
|
containers.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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;
|
var unityVer = assetsManager.assetsFileList[0].version;
|
||||||
long m_ObjectsCount;
|
long m_ObjectsCount;
|
||||||
if (unityVer[0] > 2020)
|
if (unityVer[0] > 2020)
|
||||||
@ -179,9 +178,9 @@ namespace AssetStudioCLI
|
|||||||
{
|
{
|
||||||
var exportableAssetsCountDict = new Dictionary<ClassIDType, int>();
|
var exportableAssetsCountDict = new Dictionary<ClassIDType, int>();
|
||||||
string info = "";
|
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))
|
if (exportableAssetsCountDict.ContainsKey(asset.Type))
|
||||||
{
|
{
|
||||||
@ -200,7 +199,7 @@ namespace AssetStudioCLI
|
|||||||
}
|
}
|
||||||
if (exportableAssetsCountDict.Count > 1)
|
if (exportableAssetsCountDict.Count > 1)
|
||||||
{
|
{
|
||||||
info += $"#\n# Total: {parsedAssetsList.Count} assets";
|
info += $"#\n# Total: {exportableAssetsList.Count} assets";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -220,34 +219,34 @@ namespace AssetStudioCLI
|
|||||||
|
|
||||||
public static void FilterAssets()
|
public static void FilterAssets()
|
||||||
{
|
{
|
||||||
var assetsCount = parsedAssetsList.Count;
|
var assetsCount = exportableAssetsList.Count;
|
||||||
var filteredAssets = new List<AssetItem>();
|
var filteredAssets = new List<AssetItem>();
|
||||||
|
|
||||||
switch(CLIOptions.filterBy)
|
switch(CLIOptions.filterBy)
|
||||||
{
|
{
|
||||||
case FilterBy.Name:
|
case FilterBy.Name:
|
||||||
filteredAssets = parsedAssetsList.FindAll(x => CLIOptions.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||||
Logger.Info(
|
Logger.Info(
|
||||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
|
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case FilterBy.Container:
|
case FilterBy.Container:
|
||||||
filteredAssets = parsedAssetsList.FindAll(x => CLIOptions.o_filterByContainer.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByContainer.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||||
Logger.Info(
|
Logger.Info(
|
||||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers."
|
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers."
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case FilterBy.PathID:
|
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(
|
Logger.Info(
|
||||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByPathID.Value)}\"".Color(Ansi.BrightYellow)} in their PathIDs."
|
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByPathID.Value)}\"".Color(Ansi.BrightYellow)} in their PathIDs."
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case FilterBy.NameOrContainer:
|
case FilterBy.NameOrContainer:
|
||||||
filteredAssets = parsedAssetsList.FindAll(x =>
|
filteredAssets = exportableAssetsList.FindAll(x =>
|
||||||
CLIOptions.o_filterByText.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) ||
|
CLIOptions.o_filterByText.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||||
CLIOptions.o_filterByText.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
|
CLIOptions.o_filterByText.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
);
|
);
|
||||||
@ -257,7 +256,7 @@ namespace AssetStudioCLI
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case FilterBy.NameAndContainer:
|
case FilterBy.NameAndContainer:
|
||||||
filteredAssets = parsedAssetsList.FindAll(x =>
|
filteredAssets = exportableAssetsList.FindAll(x =>
|
||||||
CLIOptions.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) &&
|
CLIOptions.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) &&
|
||||||
CLIOptions.o_filterByContainer.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
|
CLIOptions.o_filterByContainer.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
);
|
);
|
||||||
@ -268,18 +267,18 @@ namespace AssetStudioCLI
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
parsedAssetsList.Clear();
|
exportableAssetsList.Clear();
|
||||||
parsedAssetsList = filteredAssets;
|
exportableAssetsList = filteredAssets;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExportAssets()
|
public static void ExportAssets()
|
||||||
{
|
{
|
||||||
var savePath = CLIOptions.o_outputFolder.Value;
|
var savePath = CLIOptions.o_outputFolder.Value;
|
||||||
var toExportCount = parsedAssetsList.Count;
|
var toExportCount = exportableAssetsList.Count;
|
||||||
var exportedCount = 0;
|
var exportedCount = 0;
|
||||||
|
|
||||||
var groupOption = CLIOptions.o_groupAssetsBy.Value;
|
var groupOption = CLIOptions.o_groupAssetsBy.Value;
|
||||||
foreach (var asset in parsedAssetsList)
|
foreach (var asset in exportableAssetsList)
|
||||||
{
|
{
|
||||||
string exportPath;
|
string exportPath;
|
||||||
switch (groupOption)
|
switch (groupOption)
|
||||||
@ -384,7 +383,7 @@ namespace AssetStudioCLI
|
|||||||
new XElement("Assets",
|
new XElement("Assets",
|
||||||
new XAttribute("filename", filename),
|
new XAttribute("filename", filename),
|
||||||
new XAttribute("createdAt", DateTime.UtcNow.ToString("s")),
|
new XAttribute("createdAt", DateTime.UtcNow.ToString("s")),
|
||||||
parsedAssetsList.Select(
|
exportableAssetsList.Select(
|
||||||
asset => new XElement("Asset",
|
asset => new XElement("Asset",
|
||||||
new XElement("Name", asset.Text),
|
new XElement("Name", asset.Text),
|
||||||
new XElement("Container", asset.Container),
|
new XElement("Container", asset.Container),
|
||||||
@ -400,7 +399,7 @@ namespace AssetStudioCLI
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Logger.Info($"Finished exporting asset list with {parsedAssetsList.Count} items.");
|
Logger.Info($"Finished exporting asset list with {exportableAssetsList.Count} items.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExportLive2D()
|
public static void ExportLive2D()
|
||||||
@ -411,7 +410,7 @@ namespace AssetStudioCLI
|
|||||||
Progress.Reset();
|
Progress.Reset();
|
||||||
Logger.Info($"Searching for Live2D files...");
|
Logger.Info($"Searching for Live2D files...");
|
||||||
|
|
||||||
var cubismMocs = parsedAssetsList.Where(x =>
|
var cubismMocs = exportableAssetsList.Where(x =>
|
||||||
{
|
{
|
||||||
if (x.Type == ClassIDType.MonoBehaviour)
|
if (x.Type == ClassIDType.MonoBehaviour)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user