46 Commits

Author SHA1 Message Date
VaDiM
87151e065b [CLI] Fix for portrait parsing 2025-06-30 00:15:39 +03:00
VaDiM
f3a748ba6b Fix for portrait parsing 2025-06-30 00:03:25 +03:00
VaDiM
d07fc6d6f5 Fix for avg sprites 2025-04-07 16:03:20 +03:00
VaDiM
ee2976aaf1 Update version & dependencies 2025-04-07 13:32:17 +03:00
VaDiM
f144037bc0 Fix for new compression type 2025-04-07 13:23:04 +03:00
VaDiM
0e097bda04 Update README.md 2024-05-01 14:25:55 +03:00
VaDiM
9686eee3b7 Fix for 512x512 avg sprites
temporarily?
2024-04-11 22:30:42 +03:00
VaDiM
b6318e6d9b Fix for empty alpha tex 2024-01-09 11:18:06 +03:00
VaDiM
e0d90d1b1e Update AssetStudioGUI.csproj 2023-12-16 23:17:56 +03:00
VaDiM
dd727758a8 Update ver to v1.1.0 2023-12-16 23:11:27 +03:00
VaDiM
d44ed315e3 Merge branch 'AssetStudioMod' into ArknightsStudio 2023-12-16 22:57:32 +03:00
VaDiM
a00c857ac3 Update version to v0.17.4 2023-12-16 04:41:00 +03:00
VaDiM
5333757843 Update dependencies 2023-12-16 04:16:36 +03:00
VaDiM
4747687bec Update README.md 2023-12-15 15:11:48 +03:00
VaDiM
5c2ac1a5e8 Add .NET8 to the TargetFrameworks 2023-12-15 14:45:07 +03:00
VaDiM
e2eae53ac0 Update Live2DExtractor.cs 2023-12-15 01:24:54 +03:00
VaDiM
87347e8b60 [CLI] Update readme 2023-12-11 01:58:05 +03:00
VaDiM
0fdbddea55 [CLI] Fix asset filter for sprites 2023-12-11 01:58:00 +03:00
VaDiM
823190abb7 Improve parsing of Live2D Fade motions 2023-12-09 02:31:54 +03:00
VaDiM
60aef1b8ed [GUI] Add option to not build a tree structure 2023-12-09 02:25:59 +03:00
VaDiM
f82a73f018 [GUI] Add console logger 2023-12-06 17:28:10 +03:00
VaDiM
d42a1879ab [GUI] Add support for Drag&Drop files from a browser
Co-Authored-By: Luke <17146677+lukefz@users.noreply.github.com>
2023-12-05 00:38:24 +03:00
VaDiM
632e5f8d08 [GUI] Add support for .lnk files via Drag&Drop. Close #13 2023-12-04 04:10:49 +03:00
VaDiM
51d259464b Merge branch 'pr/16' into AssetStudioMod 2023-11-21 02:15:19 +03:00
VaDiM
efd06648ad Add support for separate PreloadData
https://github.com/Perfare/AssetStudio/issues/690

https://docs.unity.cn/550/Documentation/Manual/AssetBundleInternalStructure.html
2023-11-21 01:35:25 +03:00
VaDiM
b27482e22b Don't display SourceRevisionId in titles
https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/8.0/source-link
2023-11-21 00:31:09 +03:00
VaDiM
d572bd0e64 Add support for Live2D Fade motions 2023-11-20 19:56:41 +03:00
VaDiM
e415740373 Some fixes for Live2D export 2023-11-19 02:51:48 +03:00
VaDiM
e30b9e9e89 Fix bug with some avg sprites
- Changed face sprite detection method. We can't rely on the "IsWholeBody" flag
2023-11-08 13:57:57 +03:00
VaDiM
45bf9251c9 Fix crash when SpriteAtlas is missing 2023-11-07 01:31:27 +03:00
VaDiM
9632e88115 Update version to v1.0.1 2023-11-01 06:49:36 +03:00
VaDiM
bb19dd5019 Add support for avg sprite without separate alpha tex 2023-11-01 06:49:36 +03:00
Evaldas Ciakas
75ebe67713 Fix parsing secondary textures 2023-11-01 02:29:06 +02:00
VaDiM
ed7b0a2415 Unity 2022.2+ AnimationClip fix 2023-09-27 23:41:20 +03:00
VaDiM
22ab5c0633 Update README.md
- Added ArknightsStudio link
2023-09-24 16:19:09 +03:00
VaDiM
a2bc935850 [CLI] Improve help message 2023-09-24 16:14:26 +03:00
VaDiM
b6c6ceba1c Update CHANGELOG.md 2023-09-24 15:41:47 +03:00
VaDiM
7c0a6375b1 Update readme files 2023-09-24 15:41:21 +03:00
VaDiM
5c489c5f83 Merge branch 'AssetStudioMod' into ArknightsStudio 2023-09-24 15:40:23 +03:00
VaDiM
cb84c137e5 Add exception checking for gzip decompression 2023-09-24 10:41:19 +03:00
VaDiM
25c611fb9e Update README.md 2023-09-15 16:35:47 +03:00
VaDiM
e90af43459 Merge branch 'AssetStudioMod' into ArknightsStudio 2023-08-28 02:28:14 +03:00
VaDiM
381a7d89ae [AK][CLI] Add support for portrait sprites 2023-08-25 01:15:46 +03:00
VaDiM
572e3bf0d6 [AK][GUI] Add support for portrait sprites 2023-08-24 05:50:50 +03:00
VaDiM
3d7d51b54f [AK][CLI] Add support for sprites with an external alpha texture
- Added support for Arknights chararts sprites
- Added support for Arknights avg sprites
2023-08-17 01:22:16 +03:00
VaDiM
abbd27fde7 [AK][GUI] Add support for sprites with an external alpha texture
- Added support for Arknights chararts sprites
- Added support for Arknights avg sprites
2023-08-14 20:25:48 +03:00
60 changed files with 3715 additions and 711 deletions

View File

@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks> <TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>0.17.3.0</Version> <Version>1.2.0</Version>
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright> <Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,20 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks> <TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
<Version>0.17.3.0</Version> <Version>1.2.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2023</Copyright> <Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2025</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' "> <ItemGroup>
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.6" /> <PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<PackageReference Include="System.Memory" Version="4.5.4" /> <PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.IO.Compression" Version="4.0.0" /> <PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="K4os.Compression.LZ4" Version="1.1.11" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -37,9 +37,15 @@ namespace AssetStudio
{ {
filteredAssetTypesList.Add(ClassIDType.MonoScript); filteredAssetTypesList.Add(ClassIDType.MonoScript);
} }
if (classIDTypes.Contains(ClassIDType.Sprite)) if (classIDTypes.Contains(ClassIDType.Sprite) || classIDTypes.Contains(ClassIDType.AkPortraitSprite))
{ {
filteredAssetTypesList.Add(ClassIDType.Texture2D); filteredAssetTypesList.UnionWith(new HashSet<ClassIDType>
{
ClassIDType.Texture2D,
ClassIDType.SpriteAtlas,
ClassIDType.MonoBehaviour,
ClassIDType.MonoScript
});
} }
filteredAssetTypesList.UnionWith(classIDTypes); filteredAssetTypesList.UnionWith(classIDTypes);
@@ -82,7 +88,7 @@ namespace AssetStudio
MergeSplitAssets(fullPath, true); MergeSplitAssets(fullPath, true);
fileList.AddRange(Directory.GetFiles(fullPath, "*.*", SearchOption.AllDirectories)); fileList.AddRange(Directory.GetFiles(fullPath, "*.*", SearchOption.AllDirectories));
} }
else else if (File.Exists(fullPath))
{ {
parentPath = Path.GetDirectoryName(fullPath); parentPath = Path.GetDirectoryName(fullPath);
fileList.Add(fullPath); fileList.Add(fullPath);
@@ -133,7 +139,7 @@ namespace AssetStudio
private void LoadFile(FileReader reader) private void LoadFile(FileReader reader)
{ {
switch (reader.FileType) switch (reader?.FileType)
{ {
case FileType.AssetsFile: case FileType.AssetsFile:
LoadAssetsFile(reader); LoadAssetsFile(reader);
@@ -532,6 +538,9 @@ namespace AssetStudio
case ClassIDType.PlayerSettings: case ClassIDType.PlayerSettings:
obj = new PlayerSettings(objectReader); obj = new PlayerSettings(objectReader);
break; break;
case ClassIDType.PreloadData:
obj = new PreloadData(objectReader);
break;
case ClassIDType.RectTransform: case ClassIDType.RectTransform:
obj = new RectTransform(objectReader); obj = new RectTransform(objectReader);
break; break;
@@ -635,14 +644,17 @@ namespace AssetStudio
{ {
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas); m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
} }
else else if (m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlaOld))
{ {
m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlaOld);
if (m_SpriteAtlaOld.m_IsVariant) if (m_SpriteAtlaOld.m_IsVariant)
{ {
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas); m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
} }
} }
else
{
Logger.Warning($"\"{m_Sprite.m_Name}\": Sprite loading error. SpriteAtlas with PathID: \"{m_Sprite.m_SpriteAtlas.m_PathID}\" was not found.");
}
} }
} }
} }

View File

@@ -35,7 +35,7 @@ namespace AssetStudio
Lzma, Lzma,
Lz4, Lz4,
Lz4HC, Lz4HC,
Lzham Lz4Inv,
} }
public class BundleFile public class BundleFile
@@ -378,20 +378,31 @@ namespace AssetStudio
} }
case CompressionType.Lz4: case CompressionType.Lz4:
case CompressionType.Lz4HC: case CompressionType.Lz4HC:
case CompressionType.Lz4Inv:
{ {
var compressedSize = (int)blockInfo.compressedSize; var compressedSize = (int)blockInfo.compressedSize;
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize); var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
reader.Read(compressedBytes, 0, compressedSize); _ = reader.Read(compressedBytes, 0, compressedSize);
var uncompressedSize = (int)blockInfo.uncompressedSize; var uncompressedSize = (int)blockInfo.uncompressedSize;
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize); var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
var numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize); try
if (numWrite != uncompressedSize)
{ {
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); var compressedSpan = compressedBytes.AsSpan(0, compressedSize);
var uncompressedSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
var numWrite = compressionType == CompressionType.Lz4Inv
? LZ4Inv.Instance.Decompress(compressedSpan, uncompressedSpan)
: LZ4Codec.Decode(compressedSpan, uncompressedSpan);
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
}
finally
{
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
} }
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
break; break;
} }
default: default:

View File

@@ -3,6 +3,7 @@ namespace AssetStudio
{ {
public enum ClassIDType public enum ClassIDType
{ {
AkPortraitSprite = -2,
UnknownType = -1, UnknownType = -1,
Object = 0, Object = 0,
GameObject = 1, GameObject = 1,

View File

@@ -15,7 +15,6 @@ namespace AssetStudio
public T inWeight; public T inWeight;
public T outWeight; public T outWeight;
public Keyframe(ObjectReader reader, Func<T> readerFunc) public Keyframe(ObjectReader reader, Func<T> readerFunc)
{ {
time = reader.ReadSingle(); time = reader.ReadSingle();
@@ -294,15 +293,20 @@ namespace AssetStudio
public string path; public string path;
public ClassIDType classID; public ClassIDType classID;
public PPtr<MonoScript> script; public PPtr<MonoScript> script;
public int flags;
public FloatCurve(ObjectReader reader) public FloatCurve(ObjectReader reader)
{ {
var version = reader.version;
curve = new AnimationCurve<float>(reader, reader.ReadSingle); curve = new AnimationCurve<float>(reader, reader.ReadSingle);
attribute = reader.ReadAlignedString(); attribute = reader.ReadAlignedString();
path = reader.ReadAlignedString(); path = reader.ReadAlignedString();
classID = (ClassIDType)reader.ReadInt32(); classID = (ClassIDType)reader.ReadInt32();
script = new PPtr<MonoScript>(reader); script = new PPtr<MonoScript>(reader);
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 2)) //2022.2 and up
{
flags = reader.ReadInt32();
}
} }
} }
@@ -311,7 +315,6 @@ namespace AssetStudio
public float time; public float time;
public PPtr<Object> value; public PPtr<Object> value;
public PPtrKeyframe(ObjectReader reader) public PPtrKeyframe(ObjectReader reader)
{ {
time = reader.ReadSingle(); time = reader.ReadSingle();
@@ -326,10 +329,11 @@ namespace AssetStudio
public string path; public string path;
public int classID; public int classID;
public PPtr<MonoScript> script; public PPtr<MonoScript> script;
public int flags;
public PPtrCurve(ObjectReader reader) public PPtrCurve(ObjectReader reader)
{ {
var version = reader.version;
int numCurves = reader.ReadInt32(); int numCurves = reader.ReadInt32();
curve = new PPtrKeyframe[numCurves]; curve = new PPtrKeyframe[numCurves];
for (int i = 0; i < numCurves; i++) for (int i = 0; i < numCurves; i++)
@@ -341,6 +345,10 @@ namespace AssetStudio
path = reader.ReadAlignedString(); path = reader.ReadAlignedString();
classID = reader.ReadInt32(); classID = reader.ReadInt32();
script = new PPtr<MonoScript>(reader); script = new PPtr<MonoScript>(reader);
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 2)) //2022.2 and up
{
flags = reader.ReadInt32();
}
} }
} }
@@ -940,7 +948,6 @@ namespace AssetStudio
public AnimationClipBindingConstant m_ClipBindingConstant; public AnimationClipBindingConstant m_ClipBindingConstant;
public AnimationEvent[] m_Events; public AnimationEvent[] m_Events;
public AnimationClip(ObjectReader reader) : base(reader) public AnimationClip(ObjectReader reader) : base(reader)
{ {
if (version[0] >= 5)//5.0 and up if (version[0] >= 5)//5.0 and up

View File

@@ -1,7 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio namespace AssetStudio
{ {
@@ -23,22 +20,47 @@ namespace AssetStudio
{ {
public PPtr<Object>[] m_PreloadTable; public PPtr<Object>[] m_PreloadTable;
public KeyValuePair<string, AssetInfo>[] m_Container; public KeyValuePair<string, AssetInfo>[] m_Container;
public string m_AssetBundleName;
public string[] m_Dependencies;
public bool m_IsStreamedSceneAssetBundle;
public AssetBundle(ObjectReader reader) : base(reader) public AssetBundle(ObjectReader reader) : base(reader)
{ {
var m_PreloadTableSize = reader.ReadInt32(); var m_PreloadTableSize = reader.ReadInt32();
m_PreloadTable = new PPtr<Object>[m_PreloadTableSize]; m_PreloadTable = new PPtr<Object>[m_PreloadTableSize];
for (int i = 0; i < m_PreloadTableSize; i++) for (var i = 0; i < m_PreloadTableSize; i++)
{ {
m_PreloadTable[i] = new PPtr<Object>(reader); m_PreloadTable[i] = new PPtr<Object>(reader);
} }
var m_ContainerSize = reader.ReadInt32(); var m_ContainerSize = reader.ReadInt32();
m_Container = new KeyValuePair<string, AssetInfo>[m_ContainerSize]; m_Container = new KeyValuePair<string, AssetInfo>[m_ContainerSize];
for (int i = 0; i < m_ContainerSize; i++) for (var i = 0; i < m_ContainerSize; i++)
{ {
m_Container[i] = new KeyValuePair<string, AssetInfo>(reader.ReadAlignedString(), new AssetInfo(reader)); m_Container[i] = new KeyValuePair<string, AssetInfo>(reader.ReadAlignedString(), new AssetInfo(reader));
} }
var m_MainAsset = new AssetInfo(reader);
if (version[0] > 4 || (version[0] == 4 && version[1] >= 2)) //4.2 and up
{
var m_RuntimeCompatibility = reader.ReadUInt32();
}
if (version[0] >= 5) //5.0 and up
{
m_AssetBundleName = reader.ReadAlignedString();
var m_DependenciesSize = reader.ReadInt32();
m_Dependencies = new string[m_DependenciesSize];
for (var i = 0; i < m_DependenciesSize; i++)
{
m_Dependencies[i] = reader.ReadAlignedString();
}
m_IsStreamedSceneAssetBundle = reader.ReadBoolean();
}
} }
} }
} }

View File

@@ -0,0 +1,35 @@
namespace AssetStudio
{
public sealed class PreloadData : NamedObject
{
public PPtr<Object>[] m_Assets;
public PreloadData(ObjectReader reader) : base(reader)
{
var m_PreloadTableSize = reader.ReadInt32();
m_Assets = new PPtr<Object>[m_PreloadTableSize];
for (var i = 0; i < m_PreloadTableSize; i++)
{
m_Assets[i] = new PPtr<Object>(reader);
}
/*
if (version[0] >= 5) //5.0 and up
{
var m_DependenciesSize = reader.ReadInt32();
var m_Dependencies = new string[m_DependenciesSize];
for (var i = 0; i < m_DependenciesSize; i++)
{
m_Dependencies[i] = reader.ReadAlignedString();
}
}
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
{
var m_ExplicitDataLayout = reader.ReadBoolean();
}
*/
}
}
}

View File

@@ -12,7 +12,7 @@ namespace AssetStudio
public SecondarySpriteTexture(ObjectReader reader) public SecondarySpriteTexture(ObjectReader reader)
{ {
texture = new PPtr<Texture2D>(reader); texture = new PPtr<Texture2D>(reader);
name = reader.ReadStringToNull(); name = reader.ReadAlignedString();
} }
} }
@@ -182,6 +182,13 @@ namespace AssetStudio
public float width; public float width;
public float height; 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) public Rectf(BinaryReader reader)
{ {
x = reader.ReadSingle(); x = reader.ReadSingle();
@@ -205,6 +212,7 @@ namespace AssetStudio
public PPtr<SpriteAtlas> m_SpriteAtlas; public PPtr<SpriteAtlas> m_SpriteAtlas;
public SpriteRenderData m_RD; public SpriteRenderData m_RD;
public Vector2[][] m_PhysicsShape; public Vector2[][] m_PhysicsShape;
public bool akSplitAlpha;
public Sprite(ObjectReader reader) : base(reader) public Sprite(ObjectReader reader) : base(reader)
{ {
@@ -254,6 +262,8 @@ namespace AssetStudio
} }
} }
akSplitAlpha = false;
//vector m_Bones 2018 and up //vector m_Bones 2018 and up
} }
} }

View File

@@ -1,10 +1,10 @@
using System; using System;
namespace AssetStudioCLI namespace AssetStudio
{ {
// Represents set with 16 base colors using ANSI escape codes, which should be supported in most terminals // Represents set with 16 base colors using ANSI escape codes, which should be supported in most terminals
// (well, except for windows editions before windows 10) // (well, except for windows editions before windows 10)
public static class CLIAnsiColors public static class ColorConsole
{ {
public static readonly string public static readonly string
Black = "\u001b[30m", Black = "\u001b[30m",
@@ -27,7 +27,7 @@ namespace AssetStudioCLI
public static string Color(this string str, string ansiColor) public static string Color(this string str, string ansiColor)
{ {
if (!CLIWinAnsiFix.isAnsiSupported) if (!ColorConsoleHelper.isAnsiCodesSupported)
{ {
return str; return str;
} }
@@ -35,10 +35,10 @@ namespace AssetStudioCLI
return $"{ansiColor}{str}{Reset}"; return $"{ansiColor}{str}{Reset}";
} }
public static void ANSICodesTest() public static void AnsiCodesTest()
{ {
Console.WriteLine("ANSI escape codes test"); Console.WriteLine("ANSI escape codes test");
Console.WriteLine($"Supported: {CLIWinAnsiFix.isAnsiSupported}"); Console.WriteLine($"Supported: {ColorConsoleHelper.isAnsiCodesSupported}");
Console.WriteLine("\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m"); Console.WriteLine("\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m");
Console.WriteLine("\u001b[34m E \u001b[35m F \u001b[36m G \u001b[37m H \u001b[0m"); Console.WriteLine("\u001b[34m E \u001b[35m F \u001b[36m G \u001b[37m H \u001b[0m");
Console.WriteLine("\u001b[30;1m A \u001b[31;1m B \u001b[32;1m C \u001b[33;1m D \u001b[0m"); Console.WriteLine("\u001b[30;1m A \u001b[31;1m B \u001b[32;1m C \u001b[33;1m D \u001b[0m");

View File

@@ -2,11 +2,11 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace AssetStudioCLI namespace AssetStudio
{ {
static class CLIWinAnsiFix internal static class ColorConsoleHelper
{ {
public static readonly bool isAnsiSupported; public static readonly bool isAnsiCodesSupported;
private const int STD_OUTPUT_HANDLE = -11; private const int STD_OUTPUT_HANDLE = -11;
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
@@ -19,21 +19,21 @@ namespace AssetStudioCLI
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(int nStdHandle); private static extern IntPtr GetStdHandle(int nStdHandle);
static CLIWinAnsiFix() static ColorConsoleHelper()
{ {
bool isWin = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); var isWin = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
if (isWin) if (isWin)
{ {
isAnsiSupported = TryEnableVTMode(); isAnsiCodesSupported = TryEnableVTMode();
if (!isAnsiSupported) if (!isAnsiCodesSupported)
{ {
//Check for bash terminal emulator. E.g., Git Bash, Cmder //Check for bash terminal emulator. E.g., Git Bash, Cmder
isAnsiSupported = Environment.GetEnvironmentVariable("TERM") != null; isAnsiCodesSupported = Environment.GetEnvironmentVariable("TERM") != null;
} }
} }
else else
{ {
isAnsiSupported = true; isAnsiCodesSupported = true;
} }
} }
@@ -51,12 +51,7 @@ namespace AssetStudioCLI
outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode(iStdOut, outConsoleMode)) return SetConsoleMode(iStdOut, outConsoleMode);
{
return false;
}
return true;
} }
} }
} }

View File

@@ -35,10 +35,13 @@ namespace AssetStudio
return ""; return "";
} }
public static string ReadStringToNull(this BinaryReader reader, int maxLength = 32767) public static string ReadStringToNull(this BinaryReader reader, int maxLength = 32767, Encoding encoding = null)
{ {
if (encoding?.CodePage == 1200) //Unicode (UTF-16LE)
return reader.ReadUnicodeStringToNull(maxLength * 2);
var bytes = new List<byte>(); var bytes = new List<byte>();
int count = 0; var count = 0;
while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength) while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength)
{ {
var b = reader.ReadByte(); var b = reader.ReadByte();
@@ -49,7 +52,24 @@ namespace AssetStudio
bytes.Add(b); bytes.Add(b);
count++; count++;
} }
return Encoding.UTF8.GetString(bytes.ToArray()); return encoding?.GetString(bytes.ToArray()) ?? Encoding.UTF8.GetString(bytes.ToArray());
}
private static string ReadUnicodeStringToNull(this BinaryReader reader, int maxLength)
{
var bytes = new List<byte>();
var count = 0;
while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength)
{
var b = reader.ReadBytes(2);
if (b.Length < 2 || (b[0] == 0 && b[1] == 0))
{
break;
}
bytes.AddRange(b);
count += 2;
}
return Encoding.Unicode.GetString(bytes.ToArray());
} }
public static Quaternion ReadQuaternion(this BinaryReader reader) public static Quaternion ReadQuaternion(this BinaryReader reader)

View File

@@ -53,15 +53,24 @@ namespace AssetStudio
public static FileReader DecompressGZip(FileReader reader) public static FileReader DecompressGZip(FileReader reader)
{ {
using (reader) try
{ {
var stream = new MemoryStream(); using (reader)
using (var gs = new GZipStream(reader.BaseStream, CompressionMode.Decompress))
{ {
gs.CopyTo(stream); var stream = new MemoryStream();
using (var gs = new GZipStream(reader.BaseStream, CompressionMode.Decompress))
{
gs.CopyTo(stream);
}
stream.Position = 0;
return new FileReader(reader.FullPath, stream);
} }
stream.Position = 0; }
return new FileReader(reader.FullPath, stream); catch (System.Exception e)
{
Logger.Warning($"Error while decompressing gzip file {reader.FullPath}\r\n{e}");
reader.Dispose();
return null;
} }
} }

75
AssetStudio/LZ4/LZ4.cs Normal file
View File

@@ -0,0 +1,75 @@
using System;
namespace AssetStudio
{
public class LZ4
{
public static LZ4 Instance => new LZ4();
public virtual int Decompress(ReadOnlySpan<byte> cmp, Span<byte> dec)
{
int cmpPos = 0;
int decPos = 0;
do
{
var (encCount, litCount) = GetLiteralToken(cmp, ref cmpPos);
//Copy literal chunk
litCount = GetLength(litCount, cmp, ref cmpPos);
cmp.Slice(cmpPos, litCount).CopyTo(dec.Slice(decPos));
cmpPos += litCount;
decPos += litCount;
if (cmpPos >= cmp.Length)
{
break;
}
//Copy compressed chunk
int back = GetChunkEnd(cmp, ref cmpPos);
encCount = GetLength(encCount, cmp, ref cmpPos) + 4;
int encPos = decPos - back;
if (encCount <= back)
{
dec.Slice(encPos, encCount).CopyTo(dec.Slice(decPos));
decPos += encCount;
}
else
{
while (encCount-- > 0)
{
dec[decPos++] = dec[encPos++];
}
}
} while (cmpPos < cmp.Length && decPos < dec.Length);
return decPos;
}
protected virtual (int encCount, int litCount) GetLiteralToken(ReadOnlySpan<byte> cmp, ref int cmpPos) =>
((cmp[cmpPos] >> 0) & 0xf, (cmp[cmpPos++] >> 4) & 0xf);
protected virtual int GetChunkEnd(ReadOnlySpan<byte> cmp, ref int cmpPos) =>
cmp[cmpPos++] << 0 | cmp[cmpPos++] << 8;
protected virtual int GetLength(int length, ReadOnlySpan<byte> cmp, ref int cmpPos)
{
byte sum;
if (length == 0xf)
{
do
{
length += sum = cmp[cmpPos++];
} while (sum == 0xff);
}
return length;
}
}
}

15
AssetStudio/LZ4/LZ4Inv.cs Normal file
View File

@@ -0,0 +1,15 @@
using System;
namespace AssetStudio
{
public class LZ4Inv : LZ4
{
public new static LZ4Inv Instance => new LZ4Inv();
protected override (int encCount, int litCount) GetLiteralToken(ReadOnlySpan<byte> cmp, ref int cmpPos) =>
((cmp[cmpPos] >> 4) & 0xf, (cmp[cmpPos++] >> 0) & 0xf);
protected override int GetChunkEnd(ReadOnlySpan<byte> cmp, ref int cmpPos) =>
cmp[cmpPos++] << 8 | cmp[cmpPos++] << 0;
}
}

View File

@@ -2,11 +2,11 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net472;net6.0;net7.0;net8.0</TargetFrameworks>
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle> <AssemblyTitle>ArknightsStudio by aelurum</AssemblyTitle>
<AssemblyName>AssetStudioModCLI</AssemblyName> <AssemblyName>ArknightsStudioCLI</AssemblyName>
<Version>0.17.3.0</Version> <Version>1.2.0</Version>
<Copyright>Copyright © Perfare; Copyright © aelurum 2023</Copyright> <Copyright>Copyright © Perfare; Copyright © aelurum 2025</Copyright>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>

View File

@@ -29,6 +29,7 @@ namespace AssetStudioCLI
LogName = $"{appAssembly.Name}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log"; LogName = $"{appAssembly.Name}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log";
LogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogName); LogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogName);
var arch = Environment.Is64BitProcess ? "x64" : "x32"; var arch = Environment.Is64BitProcess ? "x64" : "x32";
Console.OutputEncoding = System.Text.Encoding.UTF8;
LogToFile(LoggerEvent.Verbose, $"---{appAssembly.Name} v{appAssembly.Version} [{arch}] | Logger launched---\n" + LogToFile(LoggerEvent.Verbose, $"---{appAssembly.Name} v{appAssembly.Version} [{arch}] | Logger launched---\n" +
$"CMD Args: {string.Join(" ", CLIOptions.cliArgs)}"); $"CMD Args: {string.Join(" ", CLIOptions.cliArgs)}");
@@ -36,15 +37,15 @@ namespace AssetStudioCLI
private static string ColorLogLevel(LoggerEvent logLevel) private static string ColorLogLevel(LoggerEvent logLevel)
{ {
string formattedLevel = $"[{logLevel}]"; var formattedLevel = $"[{logLevel}]";
switch (logLevel) switch (logLevel)
{ {
case LoggerEvent.Info: case LoggerEvent.Info:
return $"{formattedLevel.Color(CLIAnsiColors.BrightCyan)}"; return $"{formattedLevel.Color(ColorConsole.BrightCyan)}";
case LoggerEvent.Warning: case LoggerEvent.Warning:
return $"{formattedLevel.Color(CLIAnsiColors.BrightYellow)}"; return $"{formattedLevel.Color(ColorConsole.BrightYellow)}";
case LoggerEvent.Error: case LoggerEvent.Error:
return $"{formattedLevel.Color(CLIAnsiColors.BrightRed)}"; return $"{formattedLevel.Color(ColorConsole.BrightRed)}";
default: default:
return formattedLevel; return formattedLevel;
} }
@@ -59,7 +60,7 @@ namespace AssetStudioCLI
string formattedMessage; string formattedMessage;
if (consoleMode) if (consoleMode)
{ {
string colorLogLevel = ColorLogLevel(logMsgLevel); var colorLogLevel = ColorLogLevel(logMsgLevel);
formattedMessage = $"{colorLogLevel} {message}"; formattedMessage = $"{colorLogLevel} {message}";
if (multiLine) if (multiLine)
{ {

View File

@@ -0,0 +1,231 @@
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);
if (portraitsData._sprites.Length == 0)
return portraits;
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;
}
}
}

View File

@@ -0,0 +1,149 @@
using Arknights.AvgCharHubMono;
using AssetStudio;
using AssetStudioCLI;
using SixLabors.ImageSharp;
using System.Linq;
using System;
using System.Collections.Specialized;
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 scriptAssets = Studio.loadedAssetsList.FindAll(x =>
x.Type == ClassIDType.MonoBehaviour
&& x.Container == assetItem.Container);
if (scriptAssets.Count == 0)
{
Logger.Warning("No MonoBehaviours were found.");
return false;
}
OrderedDictionary spriteHubDict = null;
var isGrouped = false;
foreach (var scriptAsset in scriptAssets)
{
var scriptAssetDict = ((MonoBehaviour)scriptAsset.Asset).ToType();
if (scriptAssetDict == null)
{
Logger.Warning("MonoBehaviour is not readable.");
return false;
}
if (scriptAssetDict.Contains("spriteGroups"))
{
spriteHubDict = scriptAssetDict;
isGrouped = true;
break;
}
if (scriptAssetDict.Contains("sprites"))
{
spriteHubDict = scriptAssetDict;
break;
}
}
if (spriteHubDict == null)
{
Logger.Warning("AVGCharacterSpriteHub is not readable.");
return false;
}
var spriteHubJson = JsonConvert.SerializeObject(spriteHubDict);
if (isGrouped)
{
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;
}
}
}
}
}

View 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; }
}
}

View 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;
}
}
}

View 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; }
}
}

View File

@@ -1,4 +1,5 @@
using AssetStudio; using Arknights;
using AssetStudio;
namespace AssetStudioCLI namespace AssetStudioCLI
{ {
@@ -14,6 +15,7 @@ namespace AssetStudioCLI
public string Text; public string Text;
public string UniqueID; public string UniqueID;
public GameObjectNode Node; public GameObjectNode Node;
public PortraitSprite AkPortraitSprite;
public AssetItem(Object asset) public AssetItem(Object asset)
{ {
@@ -24,5 +26,17 @@ namespace AssetStudioCLI
m_PathID = asset.m_PathID; m_PathID = asset.m_PathID;
FullSize = asset.byteSize; 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;
}
} }
} }

View File

@@ -1,10 +1,14 @@
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.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
namespace AssetStudioCLI namespace AssetStudioCLI
{ {
@@ -235,11 +239,80 @@ 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_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)) if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false; return false;
var image = ((Sprite)item.Asset).GetImage(alphaMask);
var image = item.AkPortraitSprite.AkGetImage(spriteMaskMode: spriteMaskMode);
if (image != null) if (image != null)
{ {
using (image) using (image)
@@ -257,6 +330,11 @@ namespace AssetStudioCLI
public static bool ExportRawFile(AssetItem item, string exportPath) 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)) if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath))
return false; return false;
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData()); File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
@@ -292,9 +370,13 @@ namespace AssetStudioCLI
exportAllNodes, exportSkins, exportAnimations, exportBlendShape, castToBone, boneSize, exportAllUvsAsDiffuseMaps, scaleFactor, fbxVersion, fbxFormat == 1); exportAllNodes, exportSkins, exportAnimations, exportBlendShape, castToBone, boneSize, exportAllUvsAsDiffuseMaps, scaleFactor, fbxVersion, fbxFormat == 1);
} }
public static bool ExportDumpFile(AssetItem item, string exportPath) 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)) if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath))
return false; return false;
var str = item.Asset.Dump(); var str = item.Asset.Dump();
@@ -312,9 +394,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))
{ {
@@ -327,7 +409,7 @@ namespace AssetStudioCLI
Directory.CreateDirectory(dir); Directory.CreateDirectory(dir);
return true; return true;
} }
Logger.Error($"Export error. File \"{fullPath.Color(CLIAnsiColors.BrightRed)}\" already exist"); Logger.Error($"Export error. File \"{fullPath.Color(ColorConsole.BrightRed)}\" already exist");
return false; return false;
} }
@@ -450,6 +532,8 @@ namespace AssetStudioCLI
return ExportFont(item, exportPath); return ExportFont(item, exportPath);
case ClassIDType.Sprite: case ClassIDType.Sprite:
return ExportSprite(item, exportPath); return ExportSprite(item, exportPath);
case ClassIDType.AkPortraitSprite:
return ExportPortraitSprite(item, exportPath);
case ClassIDType.Mesh: case ClassIDType.Mesh:
return ExportMesh(item, exportPath); return ExportMesh(item, exportPath);
default: default:

View File

@@ -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,8 +14,10 @@ namespace AssetStudioCLI.Options
General, General,
Convert, Convert,
Logger, Logger,
Live2D,
FBX, FBX,
Filter, Filter,
Arknights,
Advanced, Advanced,
} }
@@ -58,6 +62,13 @@ namespace AssetStudioCLI.Options
NameAndContainer, NameAndContainer,
} }
internal enum AkSpriteAlphaMode
{
None,
InternalOnly,
SearchExternal
}
internal static class CLIOptions internal static class CLIOptions
{ {
public static bool isParsed; public static bool isParsed;
@@ -83,6 +94,9 @@ 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;
//live2d
public static Option<CubismLive2DExtractor.Live2DMotionMode> o_l2dMotionMode;
public static Option<bool> f_l2dForceBezier;
//fbx //fbx
public static Option<float> o_fbxScaleFactor; public static Option<float> o_fbxScaleFactor;
public static Option<int> o_fbxBoneSize; public static Option<int> o_fbxBoneSize;
@@ -91,6 +105,14 @@ namespace AssetStudioCLI.Options
public static Option<List<string>> o_filterByContainer; public static Option<List<string>> o_filterByContainer;
public static Option<List<string>> o_filterByPathID; public static Option<List<string>> o_filterByPathID;
public static Option<List<string>> o_filterByText; 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 //advanced
public static Option<ExportListType> o_exportAssetList; public static Option<ExportListType> o_exportAssetList;
public static Option<string> o_assemblyPath; public static Option<string> o_assemblyPath;
@@ -145,6 +167,7 @@ namespace AssetStudioCLI.Options
{ {
ClassIDType.Texture2D, ClassIDType.Texture2D,
ClassIDType.Sprite, ClassIDType.Sprite,
ClassIDType.AkPortraitSprite,
ClassIDType.TextAsset, ClassIDType.TextAsset,
ClassIDType.MonoBehaviour, ClassIDType.MonoBehaviour,
ClassIDType.Font, ClassIDType.Font,
@@ -177,8 +200,8 @@ namespace AssetStudioCLI.Options
optionDefaultValue: exportableAssetTypes, optionDefaultValue: exportableAssetTypes,
optionName: "-t, --asset-type <value(s)>", optionName: "-t, --asset-type <value(s)>",
optionDescription: "Specify asset type(s) to export\n" + optionDescription: "Specify asset type(s) to export\n" +
"<Value(s): tex2d, sprite, textAsset, monoBehaviour, font, shader, movieTexture,\n" + "<Value(s): tex2d, sprite, akPortrait, textAsset, monoBehaviour, font, shader,\n" +
"audio, video, mesh | all(default)>\n" + "movieTexture, audio, video, mesh | all(default)>\n" +
"All - export all asset types, which are listed in the values\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" + "*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", "Examples: \"-t sprite\" or \"-t tex2d,sprite,audio\" or \"-t tex2d;sprite;font\"\n",
@@ -203,7 +226,7 @@ namespace AssetStudioCLI.Options
optionDefaultValue: "ASExport", optionDefaultValue: "ASExport",
optionName: "-o, --output <path>", optionName: "-o, --output <path>",
optionDescription: "Specify path to the output folder\n" + optionDescription: "Specify path to the output folder\n" +
"If path isn't specifyed, 'ASExport' folder will be created in the program's work folder\n", "If path isn't specified, 'ASExport' folder will be created in the program's work folder\n",
optionHelpGroup: HelpGroups.General optionHelpGroup: HelpGroups.General
); );
o_displayHelp = new GroupedOption<bool> o_displayHelp = new GroupedOption<bool>
@@ -260,6 +283,30 @@ namespace AssetStudioCLI.Options
); );
#endregion #endregion
#region Init Cubism Live2D Options
o_l2dMotionMode = new GroupedOption<CubismLive2DExtractor.Live2DMotionMode>
(
optionDefaultValue: CubismLive2DExtractor.Live2DMotionMode.MonoBehaviour,
optionName: "--l2d-motion-mode <value>",
optionDescription: "Specify Live2D motion export mode\n" +
"<Value: monoBehaviour(default) | animationClip>\n" +
"MonoBehaviour - Try to export motions from MonoBehaviour Fade motions\n" +
"If no Fade motions are found, the AnimationClip method will be used\n" +
"AnimationClip - Try to export motions using AnimationClip assets\n" +
"Example: \"--l2d-motion-mode animationClip\"\n",
optionHelpGroup: HelpGroups.Live2D
);
f_l2dForceBezier = new GroupedOption<bool>
(
optionDefaultValue: false,
optionName: "--l2d-force-bezier",
optionDescription: "(Flag) If specified, Linear motion segments will be calculated as Bezier segments\n" +
"(May help if the exported motions look jerky/not smooth enough)",
optionHelpGroup: HelpGroups.Live2D,
isFlag: true
);
#endregion
#region Init FBX Options #region Init FBX Options
o_fbxScaleFactor = new GroupedOption<float> o_fbxScaleFactor = new GroupedOption<float>
( (
@@ -320,6 +367,65 @@ namespace AssetStudioCLI.Options
optionHelpGroup: HelpGroups.Filter optionHelpGroup: HelpGroups.Filter
); );
#endregion #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 #region Init Advanced Options
o_exportAssetList = new GroupedOption<ExportListType> o_exportAssetList = new GroupedOption<ExportListType>
@@ -350,7 +456,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\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, optionHelpGroup: HelpGroups.Advanced,
isFlag: true isFlag: true
); );
@@ -358,7 +464,7 @@ namespace AssetStudioCLI.Options
( (
optionDefaultValue: false, optionDefaultValue: false,
optionName: "--load-all", 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, optionHelpGroup: HelpGroups.Advanced,
isFlag: true isFlag: true
); );
@@ -369,8 +475,8 @@ namespace AssetStudioCLI.Options
{ {
cliArgs = args; cliArgs = args;
var brightYellow = CLIAnsiColors.BrightYellow; var brightYellow = ColorConsole.BrightYellow;
var brightRed = CLIAnsiColors.BrightRed; var brightRed = ColorConsole.BrightRed;
if (args.Length == 0 || args.Any(x => x.ToLower() == "-h" || x.ToLower() == "--help" || x.ToLower() == "-?")) if (args.Length == 0 || args.Any(x => x.ToLower() == "-h" || x.ToLower() == "--help" || x.ToLower() == "-?"))
{ {
@@ -411,6 +517,7 @@ namespace AssetStudioCLI.Options
} }
}; };
#region Parse "Working Mode" Option
var workModeOptionIndex = resplittedArgs.FindIndex(x => x.ToLower() == "-m" || x.ToLower() == "--mode"); var workModeOptionIndex = resplittedArgs.FindIndex(x => x.ToLower() == "-m" || x.ToLower() == "--mode");
if (workModeOptionIndex >= 0) if (workModeOptionIndex >= 0)
{ {
@@ -418,7 +525,7 @@ namespace AssetStudioCLI.Options
if (workModeOptionIndex + 1 >= resplittedArgs.Count) if (workModeOptionIndex + 1 >= resplittedArgs.Count)
{ {
Console.WriteLine($"{"Error during parsing options:".Color(brightRed)} Value for [{option.Color(brightRed)}] option was not found.\n"); Console.WriteLine($"{"Error during parsing options:".Color(brightRed)} Value for [{option.Color(brightRed)}] option was not found.\n");
TryShowOptionDescription(option, optionsDict); TryFindOptionDescription(option, optionsDict);
return; return;
} }
var value = resplittedArgs[workModeOptionIndex + 1]; var value = resplittedArgs[workModeOptionIndex + 1];
@@ -437,6 +544,7 @@ namespace AssetStudioCLI.Options
case "info": case "info":
o_workMode.Value = WorkMode.Info; o_workMode.Value = WorkMode.Info;
break; break;
case "l2d":
case "live2d": case "live2d":
o_workMode.Value = WorkMode.ExportLive2D; o_workMode.Value = WorkMode.ExportLive2D;
o_exportAssetTypes.Value = new List<ClassIDType>() o_exportAssetTypes.Value = new List<ClassIDType>()
@@ -463,11 +571,12 @@ namespace AssetStudioCLI.Options
break; break;
default: default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported working mode: [{value.Color(brightRed)}].\n"); Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported working mode: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_workMode.Description); ShowOptionDescription(o_workMode.Description);
return; return;
} }
resplittedArgs.RemoveRange(workModeOptionIndex, 2); resplittedArgs.RemoveRange(workModeOptionIndex, 2);
} }
#endregion
#region Parse Flags #region Parse Flags
for (int i = 0; i < resplittedArgs.Count; i++) for (int i = 0; i < resplittedArgs.Count; i++)
@@ -491,10 +600,28 @@ namespace AssetStudioCLI.Options
break; break;
default: default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{flag}] flag. This flag is not suitable for the current working mode [{o_workMode.Value}].\n"); Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{flag}] flag. This flag is not suitable for the current working mode [{o_workMode.Value}].\n");
Console.WriteLine(f_loadAllAssets.Description); ShowOptionDescription(f_loadAllAssets.Description, isFlag: true);
return; return;
} }
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;
case "--l2d-force-bezier":
if (o_workMode.Value != WorkMode.ExportLive2D)
{
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{flag}] flag. This flag is not suitable for the current working mode [{o_workMode.Value}].\n");
ShowOptionDescription(o_workMode.Description);
return;
}
f_l2dForceBezier.Value = true;
resplittedArgs.RemoveAt(i);
break;
} }
} }
#endregion #endregion
@@ -524,6 +651,9 @@ namespace AssetStudioCLI.Options
case "tex2d": case "tex2d":
o_exportAssetTypes.Value.Add(ClassIDType.Texture2D); o_exportAssetTypes.Value.Add(ClassIDType.Texture2D);
break; break;
case "akportrait":
o_exportAssetTypes.Value.Add(ClassIDType.AkPortraitSprite);
break;
case "audio": case "audio":
o_exportAssetTypes.Value.Add(ClassIDType.AudioClip); o_exportAssetTypes.Value.Add(ClassIDType.AudioClip);
break; break;
@@ -546,11 +676,11 @@ namespace AssetStudioCLI.Options
else else
{ {
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unknown asset type specified [{type.Color(brightRed)}].\n"); Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unknown asset type specified [{type.Color(brightRed)}].\n");
Console.WriteLine(o_exportAssetTypes.Description); ShowOptionDescription(o_exportAssetTypes.Description);
return; return;
} }
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Asset type [{type.Color(brightRed)}] is not supported for exporting.\n"); Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Asset type [{type.Color(brightRed)}] is not supported for exporting.\n");
Console.WriteLine(o_exportAssetTypes.Description); ShowOptionDescription(o_exportAssetTypes.Description);
return; return;
} }
} }
@@ -576,7 +706,7 @@ namespace AssetStudioCLI.Options
break; break;
default: default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported grouping option: [{value.Color(brightRed)}].\n"); Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported grouping option: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_groupAssetsBy.Description); ShowOptionDescription(o_groupAssetsBy.Description);
return; return;
} }
break; break;
@@ -627,7 +757,7 @@ namespace AssetStudioCLI.Options
break; break;
default: default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported log level value: [{value.Color(brightRed)}].\n"); Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported log level value: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_logLevel.Description); ShowOptionDescription(o_logLevel.Description);
return; return;
} }
break; break;
@@ -645,7 +775,7 @@ namespace AssetStudioCLI.Options
break; break;
default: default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported log output mode: [{value.Color(brightRed)}].\n"); Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported log output mode: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_logOutput.Description); ShowOptionDescription(o_logOutput.Description);
return; return;
} }
break; break;
@@ -673,7 +803,7 @@ namespace AssetStudioCLI.Options
break; break;
default: default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported image format: [{value.Color(brightRed)}].\n"); Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported image format: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_imageFormat.Description); ShowOptionDescription(o_imageFormat.Description);
return; return;
} }
break; break;
@@ -689,51 +819,62 @@ namespace AssetStudioCLI.Options
break; break;
default: default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported audio format: [{value.Color(brightRed)}].\n"); Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported audio format: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_audioFormat.Description); ShowOptionDescription(o_audioFormat.Description);
return;
}
break;
case "--l2d-motion-mode":
if (o_workMode.Value != WorkMode.ExportLive2D)
{
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. This option is not suitable for the current working mode [{o_workMode.Value}].\n");
ShowOptionDescription(o_workMode.Description);
return;
}
switch (value.ToLower())
{
case "fade":
case "monobehaviour":
o_l2dMotionMode.Value = CubismLive2DExtractor.Live2DMotionMode.MonoBehaviour;
break;
case "animationclip":
o_l2dMotionMode.Value = CubismLive2DExtractor.Live2DMotionMode.AnimationClip;
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported Live2D motion mode: [{value.Color(brightRed)}].\n");
ShowOptionDescription(o_l2dMotionMode.Description);
return; return;
} }
break; break;
case "--fbx-scale-factor": case "--fbx-scale-factor":
var isFloat = float.TryParse(value, out float floatValue);
if (isFloat && floatValue >= 0 && floatValue <= 100)
{ {
o_fbxScaleFactor.Value = floatValue; var isFloat = float.TryParse(value, out float floatValue);
} if (isFloat && floatValue >= 0 && floatValue <= 100)
else {
{ o_fbxScaleFactor.Value = floatValue;
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported scale factor value: [{value.Color(brightRed)}].\n"); }
Console.WriteLine(o_fbxScaleFactor.Description); else
return; {
} Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported scale factor value: [{value.Color(brightRed)}].\n");
break; ShowOptionDescription(o_fbxScaleFactor.Description);
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");
Console.WriteLine(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");
Console.WriteLine(o_exportAssetList.Description);
return; 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": case "--filter-by-name":
o_filterByName.Value.AddRange(ValueSplitter(value)); o_filterByName.Value.AddRange(ValueSplitter(value));
filterBy = filterBy == FilterBy.None ? FilterBy.Name : filterBy == FilterBy.Container ? FilterBy.NameAndContainer : filterBy; filterBy = filterBy == FilterBy.None ? FilterBy.Name : filterBy == FilterBy.Container ? FilterBy.NameAndContainer : filterBy;
@@ -750,6 +891,82 @@ namespace AssetStudioCLI.Options
o_filterByText.Value.AddRange(ValueSplitter(value)); o_filterByText.Value.AddRange(ValueSplitter(value));
filterBy = FilterBy.NameOrContainer; filterBy = FilterBy.NameOrContainer;
break; 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": case "--assembly-folder":
if (Directory.Exists(value)) if (Directory.Exists(value))
{ {
@@ -767,9 +984,9 @@ namespace AssetStudioCLI.Options
break; break;
default: default:
Console.WriteLine($"{"Error:".Color(brightRed)} Unknown option [{option.Color(brightRed)}].\n"); Console.WriteLine($"{"Error:".Color(brightRed)} Unknown option [{option.Color(brightRed)}].\n");
if (!TryShowOptionDescription(option, optionsDict)) if (!TryFindOptionDescription(option, optionsDict))
{ {
TryShowOptionDescription(option, flagsDict); TryFindOptionDescription(option, flagsDict, isFlag: true);
} }
return; return;
} }
@@ -780,12 +997,12 @@ namespace AssetStudioCLI.Options
if (optionsDict.Any(x => x.Key.Contains(option))) if (optionsDict.Any(x => x.Key.Contains(option)))
{ {
Console.WriteLine($"{"Error during parsing options:".Color(brightRed)} Value for [{option.Color(brightRed)}] option was not found.\n"); Console.WriteLine($"{"Error during parsing options:".Color(brightRed)} Value for [{option.Color(brightRed)}] option was not found.\n");
TryShowOptionDescription(option, optionsDict); TryFindOptionDescription(option, optionsDict);
} }
else if (flagsDict.Any(x => x.Key.Contains(option))) else if (flagsDict.Any(x => x.Key.Contains(option)))
{ {
Console.WriteLine($"{"Error:".Color(brightRed)} Unknown flag [{option.Color(brightRed)}].\n"); Console.WriteLine($"{"Error:".Color(brightRed)} Unknown flag [{option.Color(brightRed)}].\n");
TryShowOptionDescription(option, flagsDict); TryFindOptionDescription(option, flagsDict, isFlag: true);
} }
else else
{ {
@@ -795,7 +1012,7 @@ namespace AssetStudioCLI.Options
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("Unknown Error.".Color(CLIAnsiColors.Red)); Console.WriteLine("Unknown Error.".Color(ColorConsole.Red));
Console.WriteLine(ex); Console.WriteLine(ex);
return; return;
} }
@@ -824,14 +1041,21 @@ namespace AssetStudioCLI.Options
return value.Split(separator); return value.Split(separator);
} }
private static bool TryShowOptionDescription(string option, Dictionary<string, string> descDict) private static void ShowOptionDescription(string desc, bool isFlag = false)
{ {
var optionDesc = descDict.Where(x => x.Key.Contains(option)); var arg = isFlag ? "Flag" : "Option";
Console.WriteLine($"{arg} description:\n{desc}");
}
private static bool TryFindOptionDescription(string option, Dictionary<string, string> dict, bool isFlag = false)
{
var optionDesc = dict.Where(x => x.Key.Contains(option)).ToArray();
if (optionDesc.Any()) if (optionDesc.Any())
{ {
var arg = isFlag ? "flag" : "option";
var rand = new Random(); var rand = new Random();
var rndOption = optionDesc.ElementAt(rand.Next(0, optionDesc.Count())); var rndOption = optionDesc.ElementAt(rand.Next(0, optionDesc.Length));
Console.WriteLine($"Did you mean [{ $"{rndOption.Key}".Color(CLIAnsiColors.BrightYellow) }] option?"); Console.WriteLine($"Did you mean [{$"{rndOption.Key}".Color(ColorConsole.BrightYellow)}] {arg}?");
Console.WriteLine($"Here's a description of it: \n\n{rndOption.Value}"); Console.WriteLine($"Here's a description of it: \n\n{rndOption.Value}");
return true; return true;
@@ -873,7 +1097,7 @@ namespace AssetStudioCLI.Options
else else
{ {
var arch = Environment.Is64BitProcess ? "x64" : "x32"; 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}"); Console.WriteLine($"{usage}\n\n{helpMessage}");
} }
} }
@@ -924,7 +1148,7 @@ namespace AssetStudioCLI.Options
case WorkMode.ExportRaw: case WorkMode.ExportRaw:
case WorkMode.Dump: case WorkMode.Dump:
sb.AppendLine($"# Output Path: \"{o_outputFolder}\""); sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
if (o_workMode.Value != WorkMode.Export) if (o_workMode.Value != WorkMode.Export)
{ {
sb.AppendLine($"# Load All Assets: {f_loadAllAssets}"); sb.AppendLine($"# Load All Assets: {f_loadAllAssets}");
} }
@@ -934,12 +1158,17 @@ namespace AssetStudioCLI.Options
{ {
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 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 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}");
sb.AppendLine(ShowCurrentFilter()); sb.AppendLine(ShowCurrentFilter());
sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\""); sb.AppendLine($"# Assembly Path: \"{o_assemblyPath}\"");
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\""); sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
if (o_workMode.Value == WorkMode.Export) if (o_workMode.Value == WorkMode.Export)
{ {
@@ -968,7 +1197,9 @@ namespace AssetStudioCLI.Options
} }
else else
{ {
sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\""); sb.AppendLine($"# Live2D Motion Export Method: {o_l2dMotionMode}");
sb.AppendLine($"# Force Bezier: {f_l2dForceBezier }");
sb.AppendLine($"# Assembly Path: \"{o_assemblyPath}\"");
} }
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\""); sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
break; break;

View File

@@ -1,21 +1,25 @@
## AssetStudioModCLI ## ArknightsStudioCLI
CLI version of AssetStudioMod. CLI version of ArknightsStudio.
- Supported asset types for export: `Texture2D`, `Sprite`, `TextAsset`, `MonoBehaviour`, `Font`, `Shader`, `MovieTexture`, `AudioClip`, `VideoClip`, `Mesh`. - 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.* - *There are no plans to add support for `AnimationClip`, `Animator` for now.*
### Usage ### 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>] [-t, --asset-type <value(s)>] [-g, --group-option <value>]
[-o, --output <path>] [-h, --help] [-o, --output <path>] [-h, --help]
[--log-level <value>] [--log-output <value>] [--log-level <value>] [--log-output <value>]
[--image-format <value>] [--audio-format <value>] [--image-format <value>] [--audio-format <value>]
[--l2d-motion-mode <value>] [--l2d-force-bezier]
[--fbx-scale-factor <value>] [--fbx-bone-size <value>] [--fbx-scale-factor <value>] [--fbx-bone-size <value>]
[--filter-by-name <text>] [--filter-by-container <text>] [--filter-by-name <text>] [--filter-by-container <text>]
[--filter-by-pathid <text>] [--filter-by-text <text>] [--filter-by-pathid <text>] [--filter-by-text <text>]
[--export-asset-list <value>] [--assembly-folder <path>] [--spritealpha-mode <value>] [--alphatex-resampler <value>]
[--unity-version <text>] [--not-restore-extension] [--shadow-gamma <value>] [--original-avg-names]
[--load-all] [--add-aliases] [--export-asset-list <value>]
[--assembly-folder <path>] [--unity-version <text>]
[--not-restore-extension] [--load-all]
General Options: General Options:
-m, --mode <value> Specify working mode -m, --mode <value> Specify working mode
@@ -29,8 +33,8 @@ General Options:
Example: "-m info" Example: "-m info"
-t, --asset-type <value(s)> Specify asset type(s) to export -t, --asset-type <value(s)> Specify asset type(s) to export
<Value(s): tex2d, sprite, textAsset, monoBehaviour, font, shader, movieTexture, <Value(s): tex2d, sprite, akPortrait, textAsset, monoBehaviour, font, shader,
audio, video, mesh | all(default)> movieTexture, audio, video, mesh | all(default)>
All - export all asset types, which are listed in the values All - export all asset types, which are listed in the values
*To specify multiple asset types, write them separated by ',' or ';' without spaces *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" Examples: "-t sprite" or "-t tex2d,sprite,audio" or "-t tex2d;sprite;font"
@@ -45,7 +49,7 @@ General Options:
Example: "-g container" Example: "-g container"
-o, --output <path> Specify path to the output folder -o, --output <path> Specify path to the output folder
If path isn't specifyed, 'ASExport' folder will be created in the program's work folder If path isn't specified, 'ASExport' folder will be created in the program's work folder
-h, --help Display help and exit -h, --help Display help and exit
@@ -69,6 +73,17 @@ Convert Options:
None - Do not convert audios and export them in their own format None - Do not convert audios and export them in their own format
Example: "--audio-format wav" Example: "--audio-format wav"
Live2D Options:
--l2d-motion-mode <value> Specify Live2D motion export mode
<Value: monoBehaviour(default) | animationClip>
MonoBehaviour - Try to export motions from MonoBehaviour Fade motions
If no Fade motions are found, the AnimationClip method will be used
AnimationClip - Try to export motions using AnimationClip assets
Example: "--l2d-motion-mode animationClip"
--l2d-force-bezier (Flag) If specified, Linear motion segments will be calculated as Bezier segments
(May help if the exported motions look jerky/not smooth enough)
FBX Options: FBX Options:
--fbx-scale-factor <value> Specify the FBX Scale Factor --fbx-scale-factor <value> Specify the FBX Scale Factor
<Value: float number from 0 to 100 (default=1) <Value: float number from 0 to 100 (default=1)
@@ -97,6 +112,34 @@ Filter Options:
Example: "--filter-by-text portrait" or "--filter-by-text portrait,art" 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: Advanced Options:
--export-asset-list <value> Specify the format in which you want to export asset list --export-asset-list <value> Specify the format in which you want to export asset list
<Value: none(default) | xml> <Value: none(default) | xml>
@@ -108,9 +151,9 @@ Advanced Options:
--unity-version <text> Specify Unity version --unity-version <text> Specify Unity version
Example: "--unity-version 2017.4.39f1" 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 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) (Only for Dump, Info and ExportRaw modes)
``` ```

View File

@@ -7,14 +7,15 @@ using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using static AssetStudioCLI.Exporter; using static AssetStudioCLI.Exporter;
using static CubismLive2DExtractor.Live2DExtractor; using static CubismLive2DExtractor.Live2DExtractor;
using Ansi = AssetStudioCLI.CLIAnsiColors; using Ansi = AssetStudio.ColorConsole;
namespace AssetStudioCLI 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 List<BaseNode> gameObjectTree = new List<BaseNode>(); public static List<BaseNode> gameObjectTree = new List<BaseNode>();
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>();
@@ -54,7 +55,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);
var objectAssetItemDic = new Dictionary<AssetStudio.Object, AssetItem>(objectCount); var objectAssetItemDic = new Dictionary<AssetStudio.Object, AssetItem>(objectCount);
@@ -62,6 +62,7 @@ namespace AssetStudioCLI
var i = 0; var i = 0;
foreach (var assetsFile in assetsManager.assetsFileList) foreach (var assetsFile in assetsManager.assetsFileList)
{ {
var preloadTable = Array.Empty<PPtr<AssetStudio.Object>>();
foreach (var asset in assetsFile.Objects) foreach (var asset in assetsFile.Objects)
{ {
var assetItem = new AssetItem(asset); var assetItem = new AssetItem(asset);
@@ -70,22 +71,31 @@ namespace AssetStudioCLI
var isExportable = false; var isExportable = false;
switch (asset) switch (asset)
{ {
case PreloadData m_PreloadData:
preloadTable = m_PreloadData.m_Assets;
break;
case AssetBundle m_AssetBundle: case AssetBundle m_AssetBundle:
var isStreamedSceneAssetBundle = m_AssetBundle.m_IsStreamedSceneAssetBundle;
if (!isStreamedSceneAssetBundle)
{
preloadTable = m_AssetBundle.m_PreloadTable;
}
assetItem.Text = string.IsNullOrEmpty(m_AssetBundle.m_AssetBundleName) ? m_AssetBundle.m_Name : m_AssetBundle.m_AssetBundleName;
foreach (var m_Container in m_AssetBundle.m_Container) foreach (var m_Container in m_AssetBundle.m_Container)
{ {
var preloadIndex = m_Container.Value.preloadIndex; var preloadIndex = m_Container.Value.preloadIndex;
var preloadSize = m_Container.Value.preloadSize; var preloadSize = isStreamedSceneAssetBundle ? preloadTable.Length : m_Container.Value.preloadSize;
var preloadEnd = preloadIndex + preloadSize; var preloadEnd = preloadIndex + preloadSize;
for (int k = preloadIndex; k < preloadEnd; k++) for (var k = preloadIndex; k < preloadEnd; k++)
{ {
var pptr = m_AssetBundle.m_PreloadTable[k]; var pptr = preloadTable[k];
if (pptr.TryGet(out var obj)) if (pptr.TryGet(out var obj))
{ {
containers[obj] = m_Container.Key; containers[obj] = m_Container.Key;
} }
} }
} }
assetItem.Text = m_AssetBundle.m_Name;
break; break;
case ResourceManager m_ResourceManager: case ResourceManager m_ResourceManager:
foreach (var m_Container in m_ResourceManager.m_Container) foreach (var m_Container in m_ResourceManager.m_Container)
@@ -110,7 +120,7 @@ namespace AssetStudioCLI
if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath)) if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath))
assetItem.FullSize = asset.byteSize + m_VideoClip.m_ExternalResources.m_Size; assetItem.FullSize = asset.byteSize + m_VideoClip.m_ExternalResources.m_Size;
assetItem.Text = m_VideoClip.m_Name; assetItem.Text = m_VideoClip.m_Name;
break; break;
case Shader m_Shader: case Shader m_Shader:
assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name; assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name;
break; break;
@@ -142,23 +152,31 @@ 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 || (CLIOptions.f_loadAllAssets.Value && CLIOptions.o_exportAssetTypes.Value == CLIOptions.o_exportAssetTypes.DefaultValue)) if (isExportable || (CLIOptions.f_loadAllAssets.Value && CLIOptions.o_exportAssetTypes.Value == CLIOptions.o_exportAssetTypes.DefaultValue))
{ {
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.TryGetValue(asset.Asset, out var container))
{ {
asset.Container = containers[asset.Asset]; 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) if (CLIOptions.o_workMode.Value != WorkMode.ExportLive2D)
{ {
containers.Clear(); containers.Clear();
@@ -169,8 +187,7 @@ namespace AssetStudioCLI
{ {
BuildTreeStructure(objectAssetItemDic); BuildTreeStructure(objectAssetItemDic);
} }
var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {exportableAssetsList.Count} exportable assets";
var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {parsedAssetsList.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)
@@ -275,9 +292,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))
{ {
@@ -296,7 +313,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
@@ -329,34 +346,34 @@ namespace AssetStudioCLI
private static void FilterAssets() private 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.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)); filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByName.Value.Any(y => x.Text.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.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)); filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByContainer.Value.Any(y => x.Container.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.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) || 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) CLIOptions.o_filterByText.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
); );
@@ -366,7 +383,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.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) && 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) CLIOptions.o_filterByContainer.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
); );
@@ -377,18 +394,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)
@@ -493,7 +510,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),
@@ -509,7 +526,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 ExportSplitObjects() public static void ExportSplitObjects()
@@ -611,11 +628,13 @@ namespace AssetStudioCLI
{ {
var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput"); var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput");
var useFullContainerPath = false; var useFullContainerPath = false;
var motionMode = CLIOptions.o_l2dMotionMode.Value;
var forceBezier = CLIOptions.f_l2dForceBezier.Value;
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)
{ {
@@ -624,6 +643,7 @@ namespace AssetStudioCLI
} }
return false; return false;
}).Select(x => x.Asset).ToArray(); }).Select(x => x.Asset).ToArray();
if (cubismMocs.Length == 0) if (cubismMocs.Length == 0)
{ {
Logger.Default.Log(LoggerEvent.Info, "Live2D Cubism models were not found.", ignoreLevel: true); Logger.Default.Log(LoggerEvent.Info, "Live2D Cubism models were not found.", ignoreLevel: true);
@@ -631,7 +651,18 @@ namespace AssetStudioCLI
} }
if (cubismMocs.Length > 1) if (cubismMocs.Length > 1)
{ {
var basePathSet = cubismMocs.Select(x => containers[x].Substring(0, containers[x].LastIndexOf("/"))).ToHashSet(); var basePathSet = cubismMocs.Select(x =>
{
var pathLen = containers.TryGetValue(x, out var itemContainer) ? itemContainer.LastIndexOf("/") : 0;
pathLen = pathLen < 0 ? containers[x].Length : pathLen;
return itemContainer?.Substring(0, pathLen);
}).ToHashSet();
if (basePathSet.All(x => x == null))
{
Logger.Error($"Live2D Cubism export error: Cannot find any model related files.");
return;
}
if (basePathSet.Count != cubismMocs.Length) if (basePathSet.Count != cubismMocs.Length)
{ {
@@ -639,9 +670,16 @@ namespace AssetStudioCLI
Logger.Debug($"useFullContainerPath: {useFullContainerPath}"); Logger.Debug($"useFullContainerPath: {useFullContainerPath}");
} }
} }
var basePathList = useFullContainerPath ?
cubismMocs.Select(x => containers[x]).ToList() : var basePathList = cubismMocs.Select(x =>
cubismMocs.Select(x => containers[x].Substring(0, containers[x].LastIndexOf("/"))).ToList(); {
containers.TryGetValue(x, out var container);
container = useFullContainerPath
? container
: container?.Substring(0, container.LastIndexOf("/"));
return container;
}).Where(x => x != null).ToList();
var lookup = containers.ToLookup( var lookup = containers.ToLookup(
x => basePathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))), x => basePathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
x => x.Key x => x.Key
@@ -649,31 +687,31 @@ namespace AssetStudioCLI
var totalModelCount = lookup.LongCount(x => x.Key != null); var totalModelCount = lookup.LongCount(x => x.Key != null);
Logger.Info($"Found {totalModelCount} model(s)."); Logger.Info($"Found {totalModelCount} model(s).");
var name = "";
var modelCounter = 0; var modelCounter = 0;
foreach (var assets in lookup) foreach (var assets in lookup)
{ {
var container = assets.Key; var srcContainer = assets.Key;
if (container == null) if (srcContainer == null)
continue; continue;
name = container; var container = srcContainer;
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{container.Color(Ansi.BrightCyan)}\""); Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer.Color(Ansi.BrightCyan)}\"");
try try
{ {
var modelName = useFullContainerPath ? Path.GetFileNameWithoutExtension(container) : container.Substring(container.LastIndexOf('/') + 1); var modelName = useFullContainerPath ? Path.GetFileNameWithoutExtension(container) : container.Substring(container.LastIndexOf('/') + 1);
container = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container; container = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container;
var destPath = Path.Combine(baseDestPath, container) + Path.DirectorySeparatorChar; var destPath = Path.Combine(baseDestPath, container) + Path.DirectorySeparatorChar;
ExtractLive2D(assets, destPath, modelName, assemblyLoader); ExtractLive2D(assets, destPath, modelName, assemblyLoader, motionMode, forceBezier);
modelCounter++; modelCounter++;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error($"Live2D model export error: \"{name}\"", ex); Logger.Error($"Live2D model export error: \"{srcContainer}\"", ex);
} }
Progress.Report(modelCounter, (int)totalModelCount); Progress.Report(modelCounter, (int)totalModelCount);
} }
var status = modelCounter > 0 ? var status = modelCounter > 0 ?
$"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightCyan)}\"" : $"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightCyan)}\"" :
"Nothing exported."; "Nothing exported.";

View File

@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks> <TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>0.17.3.0</Version> <Version>1.2.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © hozuki 2020</Copyright> <Copyright>Copyright © Perfare 2018-2022; Copyright © hozuki 2020</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>

View File

@@ -49,7 +49,7 @@
this.label7 = new System.Windows.Forms.Label(); this.label7 = new System.Windows.Forms.Label();
this.modVersionLabel = new System.Windows.Forms.Label(); this.modVersionLabel = new System.Windows.Forms.Label();
this.label4 = 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.checkUpdatesLinkLabel = new System.Windows.Forms.LinkLabel();
this.tabPage2 = new System.Windows.Forms.TabPage(); this.tabPage2 = new System.Windows.Forms.TabPage();
this.licenseRichTextBox = new System.Windows.Forms.RichTextBox(); this.licenseRichTextBox = new System.Windows.Forms.RichTextBox();
@@ -116,8 +116,7 @@
this.label2.Name = "label2"; this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(347, 46); this.label2.Size = new System.Drawing.Size(347, 46);
this.label2.TabIndex = 0; this.label2.TabIndex = 0;
this.label2.Text = "AssetStudio is a tool for exploring, extracting, and exporting assets and asset b" + this.label2.Text = "ArknightsStudio is a modified version of AssetStudio designed for Arknights.";
"undles.";
this.label2.UseCompatibleTextRendering = true; this.label2.UseCompatibleTextRendering = true;
// //
// textBox2 // textBox2
@@ -164,7 +163,7 @@
this.tableLayoutPanel2.ColumnCount = 3; 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, 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.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.label16, 0, 0);
this.tableLayoutPanel2.Controls.Add(this.label17, 1, 0); this.tableLayoutPanel2.Controls.Add(this.label17, 1, 0);
this.tableLayoutPanel2.Controls.Add(this.gitPerfareLinkLabel, 2, 0); this.tableLayoutPanel2.Controls.Add(this.gitPerfareLinkLabel, 2, 0);
@@ -194,7 +193,7 @@
// //
this.label17.AutoSize = true; this.label17.AutoSize = true;
this.label17.BackColor = System.Drawing.Color.Transparent; 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.Name = "label17";
this.label17.Size = new System.Drawing.Size(110, 13); this.label17.Size = new System.Drawing.Size(110, 13);
this.label17.TabIndex = 10; this.label17.TabIndex = 10;
@@ -204,7 +203,7 @@
// //
this.gitPerfareLinkLabel.AutoSize = true; this.gitPerfareLinkLabel.AutoSize = true;
this.gitPerfareLinkLabel.BackColor = System.Drawing.Color.Transparent; 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.Name = "gitPerfareLinkLabel";
this.gitPerfareLinkLabel.Size = new System.Drawing.Size(67, 13); this.gitPerfareLinkLabel.Size = new System.Drawing.Size(67, 13);
this.gitPerfareLinkLabel.TabIndex = 11; this.gitPerfareLinkLabel.TabIndex = 11;
@@ -226,7 +225,7 @@
// //
this.label19.AutoSize = true; this.label19.AutoSize = true;
this.label19.BackColor = System.Drawing.Color.Transparent; 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.Name = "label19";
this.label19.Size = new System.Drawing.Size(113, 13); this.label19.Size = new System.Drawing.Size(113, 13);
this.label19.TabIndex = 13; this.label19.TabIndex = 13;
@@ -236,7 +235,7 @@
// //
this.gitAelurumLinkLabel.AutoSize = true; this.gitAelurumLinkLabel.AutoSize = true;
this.gitAelurumLinkLabel.BackColor = System.Drawing.Color.Transparent; 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.Name = "gitAelurumLinkLabel";
this.gitAelurumLinkLabel.Size = new System.Drawing.Size(67, 13); this.gitAelurumLinkLabel.Size = new System.Drawing.Size(67, 13);
this.gitAelurumLinkLabel.TabIndex = 14; this.gitAelurumLinkLabel.TabIndex = 14;
@@ -250,13 +249,13 @@
this.tableLayoutPanel1.ColumnCount = 3; 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, 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.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.label5, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.productNamelabel, 1, 0); this.tableLayoutPanel1.Controls.Add(this.productNamelabel, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.label7, 0, 1); this.tableLayoutPanel1.Controls.Add(this.label7, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.modVersionLabel, 1, 1); this.tableLayoutPanel1.Controls.Add(this.modVersionLabel, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.label4, 0, 2); 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.Controls.Add(this.checkUpdatesLinkLabel, 2, 1);
this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 80); this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 80);
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.Name = "tableLayoutPanel1";
@@ -285,11 +284,11 @@
// //
this.productNamelabel.AutoSize = true; this.productNamelabel.AutoSize = true;
this.productNamelabel.BackColor = System.Drawing.Color.Transparent; 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.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.TabIndex = 1;
this.productNamelabel.Text = "AssetStudioModGUI"; this.productNamelabel.Text = "ArknightsStudioGUI";
// //
// label7 // label7
// //
@@ -297,16 +296,16 @@
this.label7.BackColor = System.Drawing.Color.Transparent; this.label7.BackColor = System.Drawing.Color.Transparent;
this.label7.Location = new System.Drawing.Point(5, 20); this.label7.Location = new System.Drawing.Point(5, 20);
this.label7.Name = "label7"; 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.TabIndex = 2;
this.label7.Text = "Mod version:"; this.label7.Text = "Version:";
// //
// modVersionLabel // modVersionLabel
// //
this.modVersionLabel.AutoSize = true; this.modVersionLabel.AutoSize = true;
this.modVersionLabel.BackColor = System.Drawing.Color.Transparent; 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.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.Name = "modVersionLabel";
this.modVersionLabel.Size = new System.Drawing.Size(52, 13); this.modVersionLabel.Size = new System.Drawing.Size(52, 13);
this.modVersionLabel.TabIndex = 3; this.modVersionLabel.TabIndex = 3;
@@ -323,21 +322,21 @@
this.label4.TabIndex = 4; this.label4.TabIndex = 4;
this.label4.Text = "Based on:"; this.label4.Text = "Based on:";
// //
// label8 // basedOnLabel
// //
this.label8.AutoSize = true; this.basedOnLabel.AutoSize = true;
this.label8.BackColor = System.Drawing.Color.Transparent; this.basedOnLabel.BackColor = System.Drawing.Color.Transparent;
this.label8.Location = new System.Drawing.Point(102, 38); this.basedOnLabel.Location = new System.Drawing.Point(101, 38);
this.label8.Name = "label8"; this.basedOnLabel.Name = "basedOnLabel";
this.label8.Size = new System.Drawing.Size(108, 13); this.basedOnLabel.Size = new System.Drawing.Size(123, 13);
this.label8.TabIndex = 5; this.basedOnLabel.TabIndex = 5;
this.label8.Text = "AssetStudio v0.16.47"; this.basedOnLabel.Text = "AssetStudioMod v0.17.0";
// //
// checkUpdatesLinkLabel // checkUpdatesLinkLabel
// //
this.checkUpdatesLinkLabel.AutoSize = true; this.checkUpdatesLinkLabel.AutoSize = true;
this.checkUpdatesLinkLabel.BackColor = System.Drawing.Color.Transparent; 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.Name = "checkUpdatesLinkLabel";
this.checkUpdatesLinkLabel.Size = new System.Drawing.Size(96, 13); this.checkUpdatesLinkLabel.Size = new System.Drawing.Size(96, 13);
this.checkUpdatesLinkLabel.TabIndex = 6; this.checkUpdatesLinkLabel.TabIndex = 6;
@@ -391,7 +390,7 @@
this.productTitleLabel.Name = "productTitleLabel"; this.productTitleLabel.Name = "productTitleLabel";
this.productTitleLabel.Size = new System.Drawing.Size(384, 30); this.productTitleLabel.Size = new System.Drawing.Size(384, 30);
this.productTitleLabel.TabIndex = 1; this.productTitleLabel.TabIndex = 1;
this.productTitleLabel.Text = "AssetStudioModGUI"; this.productTitleLabel.Text = "ArknightsStudioGUI";
this.productTitleLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.productTitleLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
// //
// CloseButton // CloseButton
@@ -492,7 +491,7 @@
private System.Windows.Forms.Label label7; private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label modVersionLabel; private System.Windows.Forms.Label modVersionLabel;
private System.Windows.Forms.Label label4; 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.LinkLabel checkUpdatesLinkLabel;
private System.Windows.Forms.RichTextBox licenseRichTextBox; private System.Windows.Forms.RichTextBox licenseRichTextBox;
private System.Windows.Forms.TextBox textBox2; private System.Windows.Forms.TextBox textBox2;

View File

@@ -10,13 +10,16 @@ namespace AssetStudioGUI
public AboutForm() public AboutForm()
{ {
InitializeComponent(); InitializeComponent();
var productName = Application.ProductName;
var arch = Environment.Is64BitProcess ? "x64" : "x32"; var arch = Environment.Is64BitProcess ? "x64" : "x32";
var appAssembly = typeof(Program).Assembly.GetName();
var productName = appAssembly.Name;
var productVer = appAssembly.Version.ToString();
Text += " " + productName; Text += " " + productName;
productTitleLabel.Text = productName; productTitleLabel.Text = productName;
productVersionLabel.Text = $"v{Application.ProductVersion} [{arch}]"; productVersionLabel.Text = $"v{productVer} [{arch}]";
productNamelabel.Text = productName; productNamelabel.Text = productName;
modVersionLabel.Text = Application.ProductVersion; modVersionLabel.Text = productVer;
basedOnLabel.Text = "AssetStudioMod v0.17.4";
licenseRichTextBox.Text = GetLicenseText(); licenseRichTextBox.Text = GetLicenseText();
} }
@@ -41,7 +44,7 @@ namespace AssetStudioGUI
private void checkUpdatesLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 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 UseShellExecute = true
}; };

View File

@@ -2,13 +2,13 @@
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFrameworks>net472;net6.0-windows;net7.0-windows</TargetFrameworks> <TargetFrameworks>net472;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>Resources\as.ico</ApplicationIcon> <ApplicationIcon>Resources\as.ico</ApplicationIcon>
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle> <AssemblyTitle>ArknightsStudio by aelurum</AssemblyTitle>
<AssemblyName>AssetStudioModGUI</AssemblyName> <AssemblyName>ArknightsStudioGUI</AssemblyName>
<Version>0.17.3.0</Version> <Version>1.2.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2021-2023</Copyright> <Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2021-2025</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
@@ -54,14 +54,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft-WindowsAPICodePack-Core-6.0" Version="1.1.6-preview.2.24bd88f" /> <PackageReference Include="Microsoft-WindowsAPICodePack-Core-6.0" Version="1.1.6" />
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell-6.0" Version="1.1.6-preview.2.24bd88f" /> <PackageReference Include="Microsoft-WindowsAPICodePack-Shell-6.0" Version="1.1.6" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' "> <ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
<PackageReference Include="OpenTK.Graphics" Version="4.8.0" /> <PackageReference Include="OpenTK.Graphics" Version="4.8.2" />
<PackageReference Include="OpenTK.Windowing.Desktop" Version="4.8.0" /> <PackageReference Include="OpenTK.Windowing.Desktop" Version="4.8.2" />
<Reference Include="OpenTK.WinForms"> <Reference Include="OpenTK.WinForms">
<HintPath>Libraries\OpenTK.WinForms.dll</HintPath> <HintPath>Libraries\OpenTK.WinForms.dll</HintPath>
</Reference> </Reference>

View File

@@ -41,6 +41,12 @@
this.displayAll = new System.Windows.Forms.ToolStripMenuItem(); this.displayAll = new System.Windows.Forms.ToolStripMenuItem();
this.enablePreview = new System.Windows.Forms.ToolStripMenuItem(); this.enablePreview = new System.Windows.Forms.ToolStripMenuItem();
this.displayInfo = 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.toolStripMenuItem14 = new System.Windows.Forms.ToolStripMenuItem();
this.specifyUnityVersion = new System.Windows.Forms.ToolStripTextBox(); this.specifyUnityVersion = new System.Windows.Forms.ToolStripTextBox();
this.showExpOpt = new System.Windows.Forms.ToolStripMenuItem(); this.showExpOpt = new System.Windows.Forms.ToolStripMenuItem();
@@ -77,6 +83,8 @@
this.allToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.allToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.debugMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.debugMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem15 = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem15 = new System.Windows.Forms.ToolStripMenuItem();
this.showConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.writeLogToFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.exportClassStructuresMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exportClassStructuresMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.splitContainer1 = new System.Windows.Forms.SplitContainer();
@@ -231,6 +239,12 @@
this.displayAll, this.displayAll,
this.enablePreview, this.enablePreview,
this.displayInfo, this.displayInfo,
this.akSeparator1,
this.akTitleMenuItem,
this.akFixFaceSpriteNamesToolStripMenuItem,
this.akUseExternalAlphaToolStripMenuItem,
this.akSeparator2,
this.buildTreeStructureToolStripMenuItem,
this.toolStripMenuItem14, this.toolStripMenuItem14,
this.showExpOpt}); this.showExpOpt});
this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
@@ -241,7 +255,7 @@
// //
this.displayAll.CheckOnClick = true; this.displayAll.CheckOnClick = true;
this.displayAll.Name = "displayAll"; 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.Text = "Display all assets";
this.displayAll.ToolTipText = "Check this option will display all types assets. Not extractable assets can expor" + this.displayAll.ToolTipText = "Check this option will display all types assets. Not extractable assets can expor" +
"t the RAW file."; "t the RAW file.";
@@ -253,7 +267,7 @@
this.enablePreview.CheckOnClick = true; this.enablePreview.CheckOnClick = true;
this.enablePreview.CheckState = System.Windows.Forms.CheckState.Checked; this.enablePreview.CheckState = System.Windows.Forms.CheckState.Checked;
this.enablePreview.Name = "enablePreview"; 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.Text = "Enable preview";
this.enablePreview.ToolTipText = "Toggle the loading and preview of readable assets, such as images, sounds, text, " + 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."; "etc.\r\nDisable preview if you have performance or compatibility issues.";
@@ -265,18 +279,71 @@
this.displayInfo.CheckOnClick = true; this.displayInfo.CheckOnClick = true;
this.displayInfo.CheckState = System.Windows.Forms.CheckState.Checked; this.displayInfo.CheckState = System.Windows.Forms.CheckState.Checked;
this.displayInfo.Name = "displayInfo"; 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.Text = "Display asset information";
this.displayInfo.ToolTipText = "Toggle the overlay that shows information about each asset, eg. image size, forma" + this.displayInfo.ToolTipText = "Toggle the overlay that shows information about each asset, eg. image size, forma" +
"t, audio bitrate, etc."; "t, audio bitrate, etc.";
this.displayInfo.CheckedChanged += new System.EventHandler(this.displayAssetInfo_Check); 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(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);
//
// toolStripMenuItem14 // toolStripMenuItem14
// //
this.toolStripMenuItem14.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.toolStripMenuItem14.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.specifyUnityVersion}); this.specifyUnityVersion});
this.toolStripMenuItem14.Name = "toolStripMenuItem14"; 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"; this.toolStripMenuItem14.Text = "Specify Unity version";
// //
// specifyUnityVersion // specifyUnityVersion
@@ -290,7 +357,7 @@
// showExpOpt // showExpOpt
// //
this.showExpOpt.Name = "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.Text = "Export options";
this.showExpOpt.Click += new System.EventHandler(this.showExpOpt_Click); this.showExpOpt.Click += new System.EventHandler(this.showExpOpt_Click);
// //
@@ -536,6 +603,8 @@
// //
this.debugMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.debugMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripMenuItem15, this.toolStripMenuItem15,
this.showConsoleToolStripMenuItem,
this.writeLogToFileToolStripMenuItem,
this.exportClassStructuresMenuItem}); this.exportClassStructuresMenuItem});
this.debugMenuItem.Name = "debugMenuItem"; this.debugMenuItem.Name = "debugMenuItem";
this.debugMenuItem.Size = new System.Drawing.Size(54, 20); this.debugMenuItem.Size = new System.Drawing.Size(54, 20);
@@ -549,6 +618,24 @@
this.toolStripMenuItem15.Text = "Show all error messages"; this.toolStripMenuItem15.Text = "Show all error messages";
this.toolStripMenuItem15.Click += new System.EventHandler(this.toolStripMenuItem15_Click); this.toolStripMenuItem15.Click += new System.EventHandler(this.toolStripMenuItem15_Click);
// //
// showConsoleToolStripMenuItem
//
this.showConsoleToolStripMenuItem.Checked = true;
this.showConsoleToolStripMenuItem.CheckOnClick = true;
this.showConsoleToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
this.showConsoleToolStripMenuItem.Name = "showConsoleToolStripMenuItem";
this.showConsoleToolStripMenuItem.Size = new System.Drawing.Size(200, 22);
this.showConsoleToolStripMenuItem.Text = "Show console logger";
this.showConsoleToolStripMenuItem.Click += new System.EventHandler(this.showConsoleToolStripMenuItem_Click);
//
// writeLogToFileToolStripMenuItem
//
this.writeLogToFileToolStripMenuItem.CheckOnClick = true;
this.writeLogToFileToolStripMenuItem.Name = "writeLogToFileToolStripMenuItem";
this.writeLogToFileToolStripMenuItem.Size = new System.Drawing.Size(200, 22);
this.writeLogToFileToolStripMenuItem.Text = "Write log to file";
this.writeLogToFileToolStripMenuItem.CheckedChanged += new System.EventHandler(this.writeLogToFileToolStripMenuItem_CheckedChanged);
//
// exportClassStructuresMenuItem // exportClassStructuresMenuItem
// //
this.exportClassStructuresMenuItem.Name = "exportClassStructuresMenuItem"; this.exportClassStructuresMenuItem.Name = "exportClassStructuresMenuItem";
@@ -1256,6 +1343,7 @@
this.Name = "AssetStudioGUIForm"; this.Name = "AssetStudioGUIForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "AssetStudioModGUI"; this.Text = "AssetStudioModGUI";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.AssetStudioGUIForm_FormClosing);
this.DragDrop += new System.Windows.Forms.DragEventHandler(this.AssetStudioGUIForm_DragDrop); this.DragDrop += new System.Windows.Forms.DragEventHandler(this.AssetStudioGUIForm_DragDrop);
this.DragEnter += new System.Windows.Forms.DragEventHandler(this.AssetStudioGUIForm_DragEnter); this.DragEnter += new System.Windows.Forms.DragEventHandler(this.AssetStudioGUIForm_DragEnter);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.AssetStudioForm_KeyDown); this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.AssetStudioForm_KeyDown);
@@ -1405,6 +1493,14 @@
private System.Windows.Forms.ToolStripMenuItem showRelatedAssetsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem showRelatedAssetsToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator7; private System.Windows.Forms.ToolStripSeparator toolStripSeparator7;
private System.Windows.Forms.ListView assetListView; 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;
} }
} }

View File

@@ -1,4 +1,5 @@
using AssetStudio; using Arknights;
using AssetStudio;
using Newtonsoft.Json; using Newtonsoft.Json;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using System; using System;
@@ -18,6 +19,8 @@ using System.Timers;
using System.Windows.Forms; using System.Windows.Forms;
using static AssetStudioGUI.Studio; using static AssetStudioGUI.Studio;
using Font = AssetStudio.Font; using Font = AssetStudio.Font;
using SharpImage = SixLabors.ImageSharp;
using SharpImageFormat = SixLabors.ImageSharp.PixelFormats;
using Microsoft.WindowsAPICodePack.Taskbar; using Microsoft.WindowsAPICodePack.Taskbar;
#if NET472 #if NET472
using OpenTK; using OpenTK;
@@ -47,6 +50,7 @@ namespace AssetStudioGUI
#region SpriteControl #region SpriteControl
private SpriteMaskMode spriteMaskVisibleMode = SpriteMaskMode.On; private SpriteMaskMode spriteMaskVisibleMode = SpriteMaskMode.On;
private bool showDebugInfo = false;
#endregion #endregion
#region TexControl #region TexControl
@@ -112,21 +116,34 @@ namespace AssetStudioGUI
[DllImport("gdi32.dll")] [DllImport("gdi32.dll")]
private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts); private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts);
private string guiTitle = string.Empty;
public AssetStudioGUIForm() public AssetStudioGUIForm()
{ {
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
ConsoleWindow.RunConsole(Properties.Settings.Default.showConsole);
InitializeComponent(); InitializeComponent();
Text = $"{Application.ProductName} v{Application.ProductVersion}";
var appAssembly = typeof(Program).Assembly.GetName();
guiTitle = $"{appAssembly.Name} v{appAssembly.Version}";
Text = guiTitle;
delayTimer = new System.Timers.Timer(800); delayTimer = new System.Timers.Timer(800);
delayTimer.Elapsed += new ElapsedEventHandler(delayTimer_Elapsed); delayTimer.Elapsed += delayTimer_Elapsed;
displayAll.Checked = Properties.Settings.Default.displayAll; displayAll.Checked = Properties.Settings.Default.displayAll;
displayInfo.Checked = Properties.Settings.Default.displayInfo; displayInfo.Checked = Properties.Settings.Default.displayInfo;
enablePreview.Checked = Properties.Settings.Default.enablePreview; 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(); FMODinit();
listSearchFilterMode.SelectedIndex = 0; listSearchFilterMode.SelectedIndex = 0;
logger = new GUILogger(StatusStripUpdate); logger = new GUILogger(StatusStripUpdate);
Logger.Default = logger; Logger.Default = logger;
writeLogToFileToolStripMenuItem.Checked = Properties.Settings.Default.useFileLogger;
Progress.Default = new Progress<int>(SetProgressBarValue); Progress.Default = new Progress<int>(SetProgressBarValue);
Studio.StatusStripUpdate = StatusStripUpdate; Studio.StatusStripUpdate = StatusStripUpdate;
} }
@@ -135,21 +152,32 @@ namespace AssetStudioGUI
{ {
if (e.Data.GetDataPresent(DataFormats.FileDrop)) if (e.Data.GetDataPresent(DataFormats.FileDrop))
{ {
e.Effect = DragDropEffects.Move; e.Effect = DragDropEffects.Copy;
} }
} }
private async void AssetStudioGUIForm_DragDrop(object sender, DragEventArgs e) private async void AssetStudioGUIForm_DragDrop(object sender, DragEventArgs e)
{ {
var paths = (string[])e.Data.GetData(DataFormats.FileDrop); var paths = (string[])e.Data.GetData(DataFormats.FileDrop);
if (paths.Length > 0) if (paths.Length == 0)
return;
ResetForm();
for (var i = 0; i < paths.Length; i++)
{ {
ResetForm(); if (paths[i].ToLower().EndsWith(".lnk"))
assetsManager.SpecifyUnityVersion = specifyUnityVersion.Text; {
await Task.Run(() => assetsManager.LoadFilesAndFolders(out openDirectoryBackup, paths)); var targetPath = LnkReader.GetLnkTarget(paths[i]);
saveDirectoryBackup = openDirectoryBackup; if (!string.IsNullOrEmpty(targetPath))
BuildAssetStructures(); {
paths[i] = targetPath;
}
}
} }
assetsManager.SpecifyUnityVersion = specifyUnityVersion.Text;
await Task.Run(() => assetsManager.LoadFilesAndFolders(out openDirectoryBackup, paths));
saveDirectoryBackup = openDirectoryBackup;
BuildAssetStructures();
} }
private async void loadFile_Click(object sender, EventArgs e) private async void loadFile_Click(object sender, EventArgs e)
@@ -218,17 +246,11 @@ namespace AssetStudioGUI
return; return;
} }
(var productName, var treeNodeCollection) = await Task.Run(() => BuildAssetData()); var (productName, treeNodeCollection) = await Task.Run(() => BuildAssetData());
var typeMap = await Task.Run(() => BuildClassStructure()); var typeMap = await Task.Run(() => BuildClassStructure());
productName = string.IsNullOrEmpty(productName) ? "no productName" : productName;
if (!string.IsNullOrEmpty(productName)) Text = $"{guiTitle} - {productName} - {assetsManager.assetsFileList[0].unityVersion} - {assetsManager.assetsFileList[0].m_TargetPlatform}";
{
Text = $"{Application.ProductName} v{Application.ProductVersion} - {productName} - {assetsManager.assetsFileList[0].unityVersion} - {assetsManager.assetsFileList[0].m_TargetPlatform}";
}
else
{
Text = $"{Application.ProductName} v{Application.ProductVersion} - no productName - {assetsManager.assetsFileList[0].unityVersion} - {assetsManager.assetsFileList[0].m_TargetPlatform}";
}
assetListView.VirtualListSize = visibleAssets.Count; assetListView.VirtualListSize = visibleAssets.Count;
@@ -252,6 +274,9 @@ namespace AssetStudioGUI
typeMap.Clear(); typeMap.Clear();
classesListView.EndUpdate(); classesListView.EndUpdate();
if (akFixFaceSpriteNamesToolStripMenuItem.Checked)
FixFaceSpriteNames();
var types = exportableAssets.Select(x => x.Type).Distinct().OrderBy(x => x.ToString()).ToArray(); var types = exportableAssets.Select(x => x.Type).Distinct().OrderBy(x => x.ToString()).ToArray();
foreach (var type in types) foreach (var type in types)
{ {
@@ -351,7 +376,7 @@ namespace AssetStudioGUI
break; 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) switch (e.KeyCode)
{ {
@@ -363,6 +388,10 @@ namespace AssetStudioGUI
spriteMaskVisibleMode = spriteMaskVisibleMode == SpriteMaskMode.MaskOnly ? SpriteMaskMode.On : SpriteMaskMode.MaskOnly; spriteMaskVisibleMode = spriteMaskVisibleMode == SpriteMaskMode.MaskOnly ? SpriteMaskMode.On : SpriteMaskMode.MaskOnly;
need = true; need = true;
break; break;
case Keys.D:
showDebugInfo = !showDebugInfo;
need = true;
break;
} }
} }
if (need) if (need)
@@ -417,6 +446,7 @@ namespace AssetStudioGUI
switch (lastSelectedItem.Type) switch (lastSelectedItem.Type)
{ {
case ClassIDType.Texture2D: case ClassIDType.Texture2D:
case ClassIDType.AkPortraitSprite:
case ClassIDType.Sprite: case ClassIDType.Sprite:
{ {
if (enablePreview.Checked && imageTexture != null) if (enablePreview.Checked && imageTexture != null)
@@ -817,6 +847,9 @@ namespace AssetStudioGUI
case ClassIDType.Sprite: case ClassIDType.Sprite:
PreviewSprite(assetItem, assetItem.Asset as Sprite); PreviewSprite(assetItem, assetItem.Asset as Sprite);
break; break;
case ClassIDType.AkPortraitSprite:
PreviewAkPortraitSprite(assetItem);
break;
case ClassIDType.Animator: case ClassIDType.Animator:
StatusStripUpdate("Can be exported to FBX file."); StatusStripUpdate("Can be exported to FBX file.");
break; break;
@@ -1262,7 +1295,31 @@ namespace AssetStudioGUI
private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite) 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) if (image != null)
{ {
var bitmap = new DirectBitmap(image); var bitmap = new DirectBitmap(image);
@@ -1270,10 +1327,33 @@ namespace AssetStudioGUI
assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n"; assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n";
PreviewTexture(bitmap); 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"; var sb = new StringBuilder();
StatusStripUpdate("'Ctrl'+'A' - Enable/Disable alpha mask usage. 'Ctrl'+'M' - Show alpha mask only.");
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 else
@@ -1282,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) private void PreviewTexture(DirectBitmap bitmap)
{ {
imageTexture?.Dispose(); imageTexture?.Dispose();
@@ -1340,7 +1439,7 @@ namespace AssetStudioGUI
private void ResetForm() private void ResetForm()
{ {
Text = $"{Application.ProductName} v{Application.ProductVersion}"; Text = guiTitle;
assetsManager.Clear(); assetsManager.Clear();
assemblyLoader.Clear(); assemblyLoader.Clear();
exportableAssets.Clear(); exportableAssets.Clear();
@@ -1379,6 +1478,34 @@ namespace AssetStudioGUI
FMODreset(); 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) private void tabControl2_SelectedIndexChanged(object sender, EventArgs e)
{ {
if (tabControl2.SelectedIndex == 1 && lastSelectedItem != null) if (tabControl2.SelectedIndex == 1 && lastSelectedItem != null)
@@ -1867,6 +1994,26 @@ namespace AssetStudioGUI
sceneTreeView.EndUpdate(); 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) private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{ {
var aboutForm = new AboutForm(); var aboutForm = new AboutForm();
@@ -1996,6 +2143,38 @@ namespace AssetStudioGUI
} }
} }
private void showConsoleToolStripMenuItem_Click(object sender, EventArgs e)
{
var showConsole = showConsoleToolStripMenuItem.Checked;
if (showConsole)
ConsoleWindow.ShowConsoleWindow();
else
ConsoleWindow.HideConsoleWindow();
Properties.Settings.Default.showConsole = showConsole;
Properties.Settings.Default.Save();
}
private void writeLogToFileToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
{
var useFileLogger = writeLogToFileToolStripMenuItem.Checked;
logger.UseFileLogger = useFileLogger;
Properties.Settings.Default.useFileLogger = useFileLogger;
Properties.Settings.Default.Save();
}
private void AssetStudioGUIForm_FormClosing(object sender, FormClosingEventArgs e)
{
Logger.Verbose("Closing AssetStudio");
}
private void buildTreeStructureToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.buildTreeStructure = buildTreeStructureToolStripMenuItem.Checked;
Properties.Settings.Default.Save();
}
#region FMOD #region FMOD
private void FMODinit() private void FMODinit()
{ {

View File

@@ -0,0 +1,299 @@
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);
tex = avgSprite.FullTexture.ConvertToImage(true);
var facePos = tex.Width == 512 ? avgSprite.FacePos / 2 : avgSprite.FacePos; // ?
var faceSize = tex.Width == 512 ? avgSprite.FaceSize / 2 : avgSprite.FaceSize;
if (new Size(faceImage.Width, faceImage.Height) != avgSprite.FaceSize)
{
faceImage.Mutate(x => x.Resize(new ResizeOptions { Size = faceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
faceAlpha.Mutate(x => x.Resize(new ResizeOptions { Size = faceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
}
tex.Mutate(x => x.DrawImage(faceImage, facePos, opacity: 1f));
alphaTex.Mutate(x => x.DrawImage(faceAlpha, 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);
if (portraitsData._sprites.Length == 0)
return portraits;
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;
}
}
}

View File

@@ -0,0 +1,149 @@
using Arknights.AvgCharHubMono;
using AssetStudio;
using AssetStudioGUI;
using SixLabors.ImageSharp;
using System.Linq;
using System;
using System.Collections.Specialized;
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 scriptAssets = Studio.exportableAssets.FindAll(x =>
x.Type == ClassIDType.MonoBehaviour
&& x.Container == assetItem.Container);
if (scriptAssets.Count == 0)
{
Logger.Warning("No MonoBehaviours were found.");
return false;
}
OrderedDictionary spriteHubDict = null;
var isGrouped = false;
foreach (var scriptAsset in scriptAssets)
{
var scriptAssetDict = ((MonoBehaviour)scriptAsset.Asset).ToType();
if (scriptAssetDict == null)
{
Logger.Warning("MonoBehaviour is not readable.");
return false;
}
if (scriptAssetDict.Contains("spriteGroups"))
{
spriteHubDict = scriptAssetDict;
isGrouped = true;
break;
}
if (scriptAssetDict.Contains("sprites"))
{
spriteHubDict = scriptAssetDict;
break;
}
}
if (spriteHubDict == null)
{
Logger.Warning("AVGCharacterSpriteHub is not readable.");
return false;
}
var spriteHubJson = JsonConvert.SerializeObject(spriteHubDict);
if (isGrouped)
{
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;
}
}
}
}
}

View 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; }
}
}

View 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;
}
}
}

View 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; }
}
}

View File

@@ -1,5 +1,6 @@
using System.Windows.Forms; using System.Windows.Forms;
using AssetStudio; using AssetStudio;
using Arknights;
namespace AssetStudioGUI namespace AssetStudioGUI
{ {
@@ -15,6 +16,7 @@ namespace AssetStudioGUI
public string InfoText; public string InfoText;
public string UniqueID; public string UniqueID;
public GameObjectTreeNode TreeNode; public GameObjectTreeNode TreeNode;
public PortraitSprite AkPortraitSprite;
public AssetItem(Object asset) public AssetItem(Object asset)
{ {
@@ -26,6 +28,19 @@ namespace AssetStudioGUI
FullSize = asset.byteSize; 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() public void SetSubItems()
{ {
SubItems.AddRange(new[] SubItems.AddRange(new[]

View File

@@ -0,0 +1,67 @@
using System;
using System.Runtime.InteropServices;
using AssetStudio;
namespace AssetStudioGUI
{
internal static class ConsoleWindow
{
private enum CtrlSignalType
{
CTRL_C_EVENT,
CTRL_BREAK_EVENT,
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlSignalType ctrlSignal);
private static EventHandler eventHandler;
private static IntPtr ConsoleWindowHandle;
private static readonly int SW_HIDE = 0;
private static readonly int SW_SHOW = 5;
private static bool CloseEventHandler(CtrlSignalType ctrlSignal)
{
switch (ctrlSignal)
{
case CtrlSignalType.CTRL_C_EVENT:
case CtrlSignalType.CTRL_BREAK_EVENT:
return true;
default:
Logger.Verbose("Closing AssetStudio");
return false;
}
}
public static void RunConsole(bool showConsole)
{
AllocConsole();
ConsoleWindowHandle = GetConsoleWindow();
eventHandler += CloseEventHandler;
SetConsoleCtrlHandler(eventHandler, true);
if (!showConsole)
HideConsoleWindow();
}
public static void ShowConsoleWindow()
{
ShowWindow(ConsoleWindowHandle, SW_SHOW);
}
public static void HideConsoleWindow()
{
ShowWindow(ConsoleWindowHandle, SW_HIDE);
}
}
}

View File

@@ -0,0 +1,164 @@
// Shortcut (.lnk) file reader
// by aelurum
// Based on https://github.com/libyal/liblnk/blob/main/documentation/Windows%20Shortcut%20File%20(LNK)%20format.asciidoc
using AssetStudio;
using System;
using System.IO;
using System.Text;
namespace AssetStudioGUI
{
public static class LnkReader
{
[Flags]
private enum LnkDataFlags
{
//The LNK file contains a link target identifier
HasTargetIDList = 0x00000001,
//The LNK file contains location information
HasLinkInfo = 0x00000002,
}
[Flags]
private enum LnkLocFlags
{
//The linked file is on a volume
//If set the volume information and the local path contain data
VolumeIDAndLocalBasePath = 0x0001,
//The linked file is on a network share
//If set the network share information and common path contain data
CommonNetworkRelativeLinkAndPathSuffix = 0x0002
}
[Flags]
private enum PathTypeFlags
{
IsUnicodeLocalPath = 0x01,
IsUnicodeNetShareName = 0x02,
IsUnicodeCommonPath = 0x04
}
public static string GetLnkTarget(string filePath)
{
var targetPath = string.Empty;
var pathType = (PathTypeFlags)0;
Encoding sysEncoding;
try
{
sysEncoding = GetSysEncoding();
Logger.Debug($"System default text encoding: {sysEncoding.CodePage}");
}
catch (Exception ex)
{
Logger.Error("Text encoding error", ex);
return null;
}
using (var reader = new FileReader(filePath))
{
reader.Endian = EndianType.LittleEndian;
var headerSize = reader.ReadUInt32(); //76 bytes
reader.Position = 20; //skip LNK class identifier (GUID)
var dataFlags = (LnkDataFlags)reader.ReadUInt32();
if ((dataFlags & LnkDataFlags.HasLinkInfo) == 0)
{
Logger.Warning("Unsupported type of .lnk file. Link info was not found.");
return null;
}
reader.Position = headerSize;
//Skip the shell item ID list
if ((dataFlags & LnkDataFlags.HasTargetIDList) != 0)
{
var itemIDListSize = reader.ReadUInt16();
reader.Position += itemIDListSize;
}
//The offsets is relative to the start of the location information block
var locInfoPos = reader.Position;
var locInfoFullSize = reader.ReadUInt32();
if (locInfoFullSize == 0)
{
Logger.Warning("Unsupported type of .lnk file. Link info was not found.");
return null;
}
var locInfoHeaderSize = reader.ReadUInt32();
var locFlags = (LnkLocFlags)reader.ReadUInt32();
//Offset to the volume information block
var offsetVolumeInfo = reader.ReadUInt32();
//Offset to the ANSI local path
var offsetLocalPath = reader.ReadUInt32();
//Offset to the network share information block
var offsetNetInfo = reader.ReadUInt32();
//Offset to the ANSI common path. 0 if not available
var offsetCommonPath = reader.ReadUInt32();
if (locInfoHeaderSize > 28)
{
//Offset to the Unicode local path
offsetLocalPath = reader.ReadUInt32();
pathType |= PathTypeFlags.IsUnicodeLocalPath;
}
if (locInfoHeaderSize > 32)
{
//Offset to the Unicode common path
offsetCommonPath = reader.ReadUInt32();
pathType |= PathTypeFlags.IsUnicodeCommonPath;
}
//Read local path, if exist
if (offsetLocalPath > 0)
{
reader.Position = locInfoPos + offsetLocalPath;
targetPath = (pathType & PathTypeFlags.IsUnicodeLocalPath) != 0
? reader.ReadStringToNull(encoding: Encoding.Unicode)
: reader.ReadStringToNull(encoding: sysEncoding);
}
//Read network path, if exist
if (locFlags == LnkLocFlags.CommonNetworkRelativeLinkAndPathSuffix)
{
reader.Position = locInfoPos + offsetNetInfo;
var netInfoSize = reader.ReadUInt32();
var netInfoFlags = reader.ReadUInt32();
//Offset to the ANSI network share name. The offset is relative to the start of the network share information block
var offsetNetShareName = reader.ReadUInt32();
if (offsetNetShareName > 20)
{
reader.Position = locInfoPos + offsetNetInfo + 20;
//Offset to the Unicode network share name
offsetNetShareName = reader.ReadUInt32();
pathType |= PathTypeFlags.IsUnicodeNetShareName;
}
if (offsetNetShareName > 0)
{
reader.Position = locInfoPos + offsetNetInfo + offsetNetShareName;
targetPath = (pathType & PathTypeFlags.IsUnicodeNetShareName) != 0
? reader.ReadStringToNull(encoding: Encoding.Unicode)
: reader.ReadStringToNull(encoding: sysEncoding);
}
}
//Read common path, if exist
if (offsetCommonPath > 0)
{
reader.Position = locInfoPos + offsetCommonPath;
var commonPath = (pathType & PathTypeFlags.IsUnicodeCommonPath) != 0
? reader.ReadStringToNull(encoding: Encoding.Unicode)
: reader.ReadStringToNull(encoding: sysEncoding);
targetPath = Path.Combine(targetPath, commonPath);
}
}
return targetPath;
}
private static Encoding GetSysEncoding()
{
#if !NETFRAMEWORK
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
#endif
return Encoding.GetEncoding(0);
}
}
}

View File

@@ -45,6 +45,12 @@
this.topng = new System.Windows.Forms.RadioButton(); this.topng = new System.Windows.Forms.RadioButton();
this.tobmp = new System.Windows.Forms.RadioButton(); this.tobmp = new System.Windows.Forms.RadioButton();
this.converttexture = new System.Windows.Forms.CheckBox(); this.converttexture = new System.Windows.Forms.CheckBox();
this.l2dGroupBox = new System.Windows.Forms.GroupBox();
this.l2dMotionExportMethodPanel = new System.Windows.Forms.Panel();
this.l2dMonoBehaviourRadioButton = new System.Windows.Forms.RadioButton();
this.l2dAnimationClipRadioButton = new System.Windows.Forms.RadioButton();
this.l2dMotionExportMethodLabel = new System.Windows.Forms.Label();
this.l2dForceBezierCheckBox = new System.Windows.Forms.CheckBox();
this.groupBox2 = new System.Windows.Forms.GroupBox(); this.groupBox2 = new System.Windows.Forms.GroupBox();
this.exportAllUvsAsDiffuseMaps = new System.Windows.Forms.CheckBox(); this.exportAllUvsAsDiffuseMaps = new System.Windows.Forms.CheckBox();
this.exportBlendShape = new System.Windows.Forms.CheckBox(); this.exportBlendShape = new System.Windows.Forms.CheckBox();
@@ -63,18 +69,34 @@
this.castToBone = new System.Windows.Forms.CheckBox(); this.castToBone = new System.Windows.Forms.CheckBox();
this.exportAllNodes = new System.Windows.Forms.CheckBox(); this.exportAllNodes = new System.Windows.Forms.CheckBox();
this.eulerFilter = new System.Windows.Forms.CheckBox(); this.eulerFilter = new System.Windows.Forms.CheckBox();
this.exportUvsTooltip = new System.Windows.Forms.ToolTip(this.components); 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.groupBox1.SuspendLayout();
this.panel1.SuspendLayout(); this.panel1.SuspendLayout();
this.l2dGroupBox.SuspendLayout();
this.l2dMotionExportMethodPanel.SuspendLayout();
this.groupBox2.SuspendLayout(); this.groupBox2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.scaleFactor)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.scaleFactor)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.boneSize)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.boneSize)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.filterPrecision)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.filterPrecision)).BeginInit();
this.akSpritesAlphaGroupBox.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.akAlphaMaskGammaTrackBar)).BeginInit();
this.akSpritesExportGroupBox.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// OKbutton // 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.Name = "OKbutton";
this.OKbutton.Size = new System.Drawing.Size(75, 23); this.OKbutton.Size = new System.Drawing.Size(75, 23);
this.OKbutton.TabIndex = 6; this.OKbutton.TabIndex = 6;
@@ -85,7 +107,7 @@
// Cancel // Cancel
// //
this.Cancel.DialogResult = System.Windows.Forms.DialogResult.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.Name = "Cancel";
this.Cancel.Size = new System.Drawing.Size(75, 23); this.Cancel.Size = new System.Drawing.Size(75, 23);
this.Cancel.TabIndex = 7; this.Cancel.TabIndex = 7;
@@ -106,8 +128,8 @@
this.groupBox1.Controls.Add(this.converttexture); this.groupBox1.Controls.Add(this.converttexture);
this.groupBox1.Location = new System.Drawing.Point(12, 13); this.groupBox1.Location = new System.Drawing.Point(12, 13);
this.groupBox1.Name = "groupBox1"; this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(301, 362); this.groupBox1.Size = new System.Drawing.Size(301, 272);
this.groupBox1.TabIndex = 9; this.groupBox1.TabIndex = 1;
this.groupBox1.TabStop = false; this.groupBox1.TabStop = false;
this.groupBox1.Text = "Export"; this.groupBox1.Text = "Export";
// //
@@ -119,7 +141,7 @@
this.exportSpriteWithAlphaMask.Location = new System.Drawing.Point(6, 150); this.exportSpriteWithAlphaMask.Location = new System.Drawing.Point(6, 150);
this.exportSpriteWithAlphaMask.Name = "exportSpriteWithAlphaMask"; this.exportSpriteWithAlphaMask.Name = "exportSpriteWithAlphaMask";
this.exportSpriteWithAlphaMask.Size = new System.Drawing.Size(205, 17); this.exportSpriteWithAlphaMask.Size = new System.Drawing.Size(205, 17);
this.exportSpriteWithAlphaMask.TabIndex = 11; this.exportSpriteWithAlphaMask.TabIndex = 6;
this.exportSpriteWithAlphaMask.Text = "Export sprites with alpha mask applied"; this.exportSpriteWithAlphaMask.Text = "Export sprites with alpha mask applied";
this.exportSpriteWithAlphaMask.UseVisualStyleBackColor = true; this.exportSpriteWithAlphaMask.UseVisualStyleBackColor = true;
// //
@@ -131,7 +153,7 @@
this.openAfterExport.Location = new System.Drawing.Point(6, 196); this.openAfterExport.Location = new System.Drawing.Point(6, 196);
this.openAfterExport.Name = "openAfterExport"; this.openAfterExport.Name = "openAfterExport";
this.openAfterExport.Size = new System.Drawing.Size(137, 17); this.openAfterExport.Size = new System.Drawing.Size(137, 17);
this.openAfterExport.TabIndex = 10; this.openAfterExport.TabIndex = 8;
this.openAfterExport.Text = "Open folder after export"; this.openAfterExport.Text = "Open folder after export";
this.openAfterExport.UseVisualStyleBackColor = true; this.openAfterExport.UseVisualStyleBackColor = true;
// //
@@ -143,9 +165,9 @@
this.restoreExtensionName.Location = new System.Drawing.Point(6, 63); this.restoreExtensionName.Location = new System.Drawing.Point(6, 63);
this.restoreExtensionName.Name = "restoreExtensionName"; this.restoreExtensionName.Name = "restoreExtensionName";
this.restoreExtensionName.Size = new System.Drawing.Size(275, 17); this.restoreExtensionName.Size = new System.Drawing.Size(275, 17);
this.restoreExtensionName.TabIndex = 9; this.restoreExtensionName.TabIndex = 3;
this.restoreExtensionName.Text = "Try to restore/Use original TextAsset extension name"; this.restoreExtensionName.Text = "Try to restore/Use original TextAsset extension name";
this.exportUvsTooltip.SetToolTip(this.restoreExtensionName, "If not checked, AssetStudio will export all TextAssets with the \".txt\" extension"); this.optionTooltip.SetToolTip(this.restoreExtensionName, "If not checked, AssetStudio will export all TextAssets with the \".txt\" extension");
this.restoreExtensionName.UseVisualStyleBackColor = true; this.restoreExtensionName.UseVisualStyleBackColor = true;
// //
// assetGroupOptions // assetGroupOptions
@@ -161,7 +183,7 @@
this.assetGroupOptions.Location = new System.Drawing.Point(6, 35); this.assetGroupOptions.Location = new System.Drawing.Point(6, 35);
this.assetGroupOptions.Name = "assetGroupOptions"; this.assetGroupOptions.Name = "assetGroupOptions";
this.assetGroupOptions.Size = new System.Drawing.Size(165, 21); this.assetGroupOptions.Size = new System.Drawing.Size(165, 21);
this.assetGroupOptions.TabIndex = 8; this.assetGroupOptions.TabIndex = 2;
// //
// label6 // label6
// //
@@ -169,7 +191,7 @@
this.label6.Location = new System.Drawing.Point(6, 18); this.label6.Location = new System.Drawing.Point(6, 18);
this.label6.Name = "label6"; this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(127, 13); this.label6.Size = new System.Drawing.Size(127, 13);
this.label6.TabIndex = 7; this.label6.TabIndex = 1;
this.label6.Text = "Group exported assets by"; this.label6.Text = "Group exported assets by";
// //
// convertAudio // convertAudio
@@ -180,7 +202,7 @@
this.convertAudio.Location = new System.Drawing.Point(6, 173); this.convertAudio.Location = new System.Drawing.Point(6, 173);
this.convertAudio.Name = "convertAudio"; this.convertAudio.Name = "convertAudio";
this.convertAudio.Size = new System.Drawing.Size(179, 17); this.convertAudio.Size = new System.Drawing.Size(179, 17);
this.convertAudio.TabIndex = 6; this.convertAudio.TabIndex = 7;
this.convertAudio.Text = "Convert AudioClip to WAV(PCM)"; this.convertAudio.Text = "Convert AudioClip to WAV(PCM)";
this.convertAudio.UseVisualStyleBackColor = true; this.convertAudio.UseVisualStyleBackColor = true;
// //
@@ -202,8 +224,7 @@
this.towebp.Location = new System.Drawing.Point(201, 7); this.towebp.Location = new System.Drawing.Point(201, 7);
this.towebp.Name = "towebp"; this.towebp.Name = "towebp";
this.towebp.Size = new System.Drawing.Size(54, 17); this.towebp.Size = new System.Drawing.Size(54, 17);
this.towebp.TabIndex = 5; this.towebp.TabIndex = 4;
this.towebp.TabStop = true;
this.towebp.Text = "Webp"; this.towebp.Text = "Webp";
this.towebp.UseVisualStyleBackColor = true; this.towebp.UseVisualStyleBackColor = true;
// //
@@ -213,7 +234,7 @@
this.totga.Location = new System.Drawing.Point(150, 7); this.totga.Location = new System.Drawing.Point(150, 7);
this.totga.Name = "totga"; this.totga.Name = "totga";
this.totga.Size = new System.Drawing.Size(44, 17); this.totga.Size = new System.Drawing.Size(44, 17);
this.totga.TabIndex = 2; this.totga.TabIndex = 3;
this.totga.Text = "Tga"; this.totga.Text = "Tga";
this.totga.UseVisualStyleBackColor = true; this.totga.UseVisualStyleBackColor = true;
// //
@@ -223,7 +244,7 @@
this.tojpg.Location = new System.Drawing.Point(97, 7); this.tojpg.Location = new System.Drawing.Point(97, 7);
this.tojpg.Name = "tojpg"; this.tojpg.Name = "tojpg";
this.tojpg.Size = new System.Drawing.Size(48, 17); this.tojpg.Size = new System.Drawing.Size(48, 17);
this.tojpg.TabIndex = 4; this.tojpg.TabIndex = 2;
this.tojpg.Text = "Jpeg"; this.tojpg.Text = "Jpeg";
this.tojpg.UseVisualStyleBackColor = true; this.tojpg.UseVisualStyleBackColor = true;
// //
@@ -234,7 +255,7 @@
this.topng.Location = new System.Drawing.Point(50, 7); this.topng.Location = new System.Drawing.Point(50, 7);
this.topng.Name = "topng"; this.topng.Name = "topng";
this.topng.Size = new System.Drawing.Size(44, 17); this.topng.Size = new System.Drawing.Size(44, 17);
this.topng.TabIndex = 3; this.topng.TabIndex = 1;
this.topng.TabStop = true; this.topng.TabStop = true;
this.topng.Text = "Png"; this.topng.Text = "Png";
this.topng.UseVisualStyleBackColor = true; this.topng.UseVisualStyleBackColor = true;
@@ -245,7 +266,7 @@
this.tobmp.Location = new System.Drawing.Point(3, 7); this.tobmp.Location = new System.Drawing.Point(3, 7);
this.tobmp.Name = "tobmp"; this.tobmp.Name = "tobmp";
this.tobmp.Size = new System.Drawing.Size(46, 17); this.tobmp.Size = new System.Drawing.Size(46, 17);
this.tobmp.TabIndex = 2; this.tobmp.TabIndex = 0;
this.tobmp.Text = "Bmp"; this.tobmp.Text = "Bmp";
this.tobmp.UseVisualStyleBackColor = true; this.tobmp.UseVisualStyleBackColor = true;
// //
@@ -257,10 +278,76 @@
this.converttexture.Location = new System.Drawing.Point(6, 87); this.converttexture.Location = new System.Drawing.Point(6, 87);
this.converttexture.Name = "converttexture"; this.converttexture.Name = "converttexture";
this.converttexture.Size = new System.Drawing.Size(116, 17); this.converttexture.Size = new System.Drawing.Size(116, 17);
this.converttexture.TabIndex = 1; this.converttexture.TabIndex = 4;
this.converttexture.Text = "Convert Texture2D"; this.converttexture.Text = "Convert Texture2D";
this.converttexture.UseVisualStyleBackColor = true; this.converttexture.UseVisualStyleBackColor = true;
// //
// l2dGroupBox
//
this.l2dGroupBox.Controls.Add(this.l2dMotionExportMethodPanel);
this.l2dGroupBox.Controls.Add(this.l2dMotionExportMethodLabel);
this.l2dGroupBox.Controls.Add(this.l2dForceBezierCheckBox);
this.l2dGroupBox.Location = new System.Drawing.Point(12, 275);
this.l2dGroupBox.Name = "l2dGroupBox";
this.l2dGroupBox.Size = new System.Drawing.Size(301, 100);
this.l2dGroupBox.TabIndex = 2;
this.l2dGroupBox.TabStop = false;
this.l2dGroupBox.Text = "Cubism Live2D";
//
// l2dMotionExportMethodPanel
//
this.l2dMotionExportMethodPanel.Controls.Add(this.l2dMonoBehaviourRadioButton);
this.l2dMotionExportMethodPanel.Controls.Add(this.l2dAnimationClipRadioButton);
this.l2dMotionExportMethodPanel.Location = new System.Drawing.Point(18, 40);
this.l2dMotionExportMethodPanel.Name = "l2dMotionExportMethodPanel";
this.l2dMotionExportMethodPanel.Size = new System.Drawing.Size(263, 27);
this.l2dMotionExportMethodPanel.TabIndex = 2;
//
// l2dMonoBehaviourRadioButton
//
this.l2dMonoBehaviourRadioButton.AccessibleName = "MonoBehaviour";
this.l2dMonoBehaviourRadioButton.AutoSize = true;
this.l2dMonoBehaviourRadioButton.Checked = true;
this.l2dMonoBehaviourRadioButton.Location = new System.Drawing.Point(3, 5);
this.l2dMonoBehaviourRadioButton.Name = "l2dMonoBehaviourRadioButton";
this.l2dMonoBehaviourRadioButton.Size = new System.Drawing.Size(167, 17);
this.l2dMonoBehaviourRadioButton.TabIndex = 0;
this.l2dMonoBehaviourRadioButton.TabStop = true;
this.l2dMonoBehaviourRadioButton.Text = "MonoBehaviour (Fade motion)";
this.optionTooltip.SetToolTip(this.l2dMonoBehaviourRadioButton, "If no Fade motions are found, the AnimationClip method will be used");
this.l2dMonoBehaviourRadioButton.UseVisualStyleBackColor = true;
//
// l2dAnimationClipRadioButton
//
this.l2dAnimationClipRadioButton.AccessibleName = "AnimationClip";
this.l2dAnimationClipRadioButton.AutoSize = true;
this.l2dAnimationClipRadioButton.Location = new System.Drawing.Point(172, 5);
this.l2dAnimationClipRadioButton.Name = "l2dAnimationClipRadioButton";
this.l2dAnimationClipRadioButton.Size = new System.Drawing.Size(88, 17);
this.l2dAnimationClipRadioButton.TabIndex = 1;
this.l2dAnimationClipRadioButton.Text = "AnimationClip";
this.l2dAnimationClipRadioButton.UseVisualStyleBackColor = true;
//
// l2dMotionExportMethodLabel
//
this.l2dMotionExportMethodLabel.AutoSize = true;
this.l2dMotionExportMethodLabel.Location = new System.Drawing.Point(6, 21);
this.l2dMotionExportMethodLabel.Name = "l2dMotionExportMethodLabel";
this.l2dMotionExportMethodLabel.Size = new System.Drawing.Size(109, 13);
this.l2dMotionExportMethodLabel.TabIndex = 1;
this.l2dMotionExportMethodLabel.Text = "Motion export method";
//
// l2dForceBezierCheckBox
//
this.l2dForceBezierCheckBox.AutoSize = true;
this.l2dForceBezierCheckBox.Location = new System.Drawing.Point(6, 77);
this.l2dForceBezierCheckBox.Name = "l2dForceBezierCheckBox";
this.l2dForceBezierCheckBox.Size = new System.Drawing.Size(278, 17);
this.l2dForceBezierCheckBox.TabIndex = 3;
this.l2dForceBezierCheckBox.Text = "Calculate Linear motion segments as Bezier segments";
this.optionTooltip.SetToolTip(this.l2dForceBezierCheckBox, "May help if the exported motions look jerky/not smooth enough");
this.l2dForceBezierCheckBox.UseVisualStyleBackColor = true;
//
// groupBox2 // groupBox2
// //
this.groupBox2.AutoSize = true; this.groupBox2.AutoSize = true;
@@ -284,7 +371,7 @@
this.groupBox2.Location = new System.Drawing.Point(313, 13); this.groupBox2.Location = new System.Drawing.Point(313, 13);
this.groupBox2.Name = "groupBox2"; this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(224, 362); this.groupBox2.Size = new System.Drawing.Size(224, 362);
this.groupBox2.TabIndex = 11; this.groupBox2.TabIndex = 3;
this.groupBox2.TabStop = false; this.groupBox2.TabStop = false;
this.groupBox2.Text = "Fbx"; this.groupBox2.Text = "Fbx";
// //
@@ -295,9 +382,9 @@
this.exportAllUvsAsDiffuseMaps.Location = new System.Drawing.Point(6, 185); this.exportAllUvsAsDiffuseMaps.Location = new System.Drawing.Point(6, 185);
this.exportAllUvsAsDiffuseMaps.Name = "exportAllUvsAsDiffuseMaps"; this.exportAllUvsAsDiffuseMaps.Name = "exportAllUvsAsDiffuseMaps";
this.exportAllUvsAsDiffuseMaps.Size = new System.Drawing.Size(168, 17); this.exportAllUvsAsDiffuseMaps.Size = new System.Drawing.Size(168, 17);
this.exportAllUvsAsDiffuseMaps.TabIndex = 23; this.exportAllUvsAsDiffuseMaps.TabIndex = 9;
this.exportAllUvsAsDiffuseMaps.Text = "Export all UVs as diffuse maps"; this.exportAllUvsAsDiffuseMaps.Text = "Export all UVs as diffuse maps";
this.exportUvsTooltip.SetToolTip(this.exportAllUvsAsDiffuseMaps, "Unchecked: UV1 exported as normal map. Check this if your export is missing a UV " + this.optionTooltip.SetToolTip(this.exportAllUvsAsDiffuseMaps, "Unchecked: UV1 exported as normal map. Check this if your export is missing a UV " +
"map."); "map.");
this.exportAllUvsAsDiffuseMaps.UseVisualStyleBackColor = true; this.exportAllUvsAsDiffuseMaps.UseVisualStyleBackColor = true;
// //
@@ -309,7 +396,7 @@
this.exportBlendShape.Location = new System.Drawing.Point(6, 138); this.exportBlendShape.Location = new System.Drawing.Point(6, 138);
this.exportBlendShape.Name = "exportBlendShape"; this.exportBlendShape.Name = "exportBlendShape";
this.exportBlendShape.Size = new System.Drawing.Size(114, 17); this.exportBlendShape.Size = new System.Drawing.Size(114, 17);
this.exportBlendShape.TabIndex = 22; this.exportBlendShape.TabIndex = 7;
this.exportBlendShape.Text = "Export blendshape"; this.exportBlendShape.Text = "Export blendshape";
this.exportBlendShape.UseVisualStyleBackColor = true; this.exportBlendShape.UseVisualStyleBackColor = true;
// //
@@ -321,7 +408,7 @@
this.exportAnimations.Location = new System.Drawing.Point(6, 114); this.exportAnimations.Location = new System.Drawing.Point(6, 114);
this.exportAnimations.Name = "exportAnimations"; this.exportAnimations.Name = "exportAnimations";
this.exportAnimations.Size = new System.Drawing.Size(109, 17); this.exportAnimations.Size = new System.Drawing.Size(109, 17);
this.exportAnimations.TabIndex = 21; this.exportAnimations.TabIndex = 6;
this.exportAnimations.Text = "Export animations"; this.exportAnimations.Text = "Export animations";
this.exportAnimations.UseVisualStyleBackColor = true; this.exportAnimations.UseVisualStyleBackColor = true;
// //
@@ -336,7 +423,7 @@
this.scaleFactor.Location = new System.Drawing.Point(83, 243); this.scaleFactor.Location = new System.Drawing.Point(83, 243);
this.scaleFactor.Name = "scaleFactor"; this.scaleFactor.Name = "scaleFactor";
this.scaleFactor.Size = new System.Drawing.Size(60, 20); this.scaleFactor.Size = new System.Drawing.Size(60, 20);
this.scaleFactor.TabIndex = 20; this.scaleFactor.TabIndex = 13;
this.scaleFactor.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.scaleFactor.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
this.scaleFactor.Value = new decimal(new int[] { this.scaleFactor.Value = new decimal(new int[] {
1, 1,
@@ -350,7 +437,7 @@
this.label5.Location = new System.Drawing.Point(6, 245); this.label5.Location = new System.Drawing.Point(6, 245);
this.label5.Name = "label5"; this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(64, 13); this.label5.Size = new System.Drawing.Size(64, 13);
this.label5.TabIndex = 19; this.label5.TabIndex = 12;
this.label5.Text = "ScaleFactor"; this.label5.Text = "ScaleFactor";
// //
// fbxFormat // fbxFormat
@@ -363,7 +450,7 @@
this.fbxFormat.Location = new System.Drawing.Point(77, 275); this.fbxFormat.Location = new System.Drawing.Point(77, 275);
this.fbxFormat.Name = "fbxFormat"; this.fbxFormat.Name = "fbxFormat";
this.fbxFormat.Size = new System.Drawing.Size(61, 21); this.fbxFormat.Size = new System.Drawing.Size(61, 21);
this.fbxFormat.TabIndex = 18; this.fbxFormat.TabIndex = 15;
// //
// label4 // label4
// //
@@ -371,7 +458,7 @@
this.label4.Location = new System.Drawing.Point(6, 280); this.label4.Location = new System.Drawing.Point(6, 280);
this.label4.Name = "label4"; this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(59, 13); this.label4.Size = new System.Drawing.Size(59, 13);
this.label4.TabIndex = 17; this.label4.TabIndex = 14;
this.label4.Text = "FBXFormat"; this.label4.Text = "FBXFormat";
// //
// fbxVersion // fbxVersion
@@ -388,7 +475,7 @@
this.fbxVersion.Location = new System.Drawing.Point(77, 308); this.fbxVersion.Location = new System.Drawing.Point(77, 308);
this.fbxVersion.Name = "fbxVersion"; this.fbxVersion.Name = "fbxVersion";
this.fbxVersion.Size = new System.Drawing.Size(47, 21); this.fbxVersion.Size = new System.Drawing.Size(47, 21);
this.fbxVersion.TabIndex = 16; this.fbxVersion.TabIndex = 17;
// //
// label3 // label3
// //
@@ -396,7 +483,7 @@
this.label3.Location = new System.Drawing.Point(6, 311); this.label3.Location = new System.Drawing.Point(6, 311);
this.label3.Name = "label3"; this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(62, 13); this.label3.Size = new System.Drawing.Size(62, 13);
this.label3.TabIndex = 15; this.label3.TabIndex = 16;
this.label3.Text = "FBXVersion"; this.label3.Text = "FBXVersion";
// //
// boneSize // boneSize
@@ -428,7 +515,7 @@
this.exportSkins.Location = new System.Drawing.Point(6, 90); this.exportSkins.Location = new System.Drawing.Point(6, 90);
this.exportSkins.Name = "exportSkins"; this.exportSkins.Name = "exportSkins";
this.exportSkins.Size = new System.Drawing.Size(83, 17); this.exportSkins.Size = new System.Drawing.Size(83, 17);
this.exportSkins.TabIndex = 8; this.exportSkins.TabIndex = 5;
this.exportSkins.Text = "Export skins"; this.exportSkins.Text = "Export skins";
this.exportSkins.UseVisualStyleBackColor = true; this.exportSkins.UseVisualStyleBackColor = true;
// //
@@ -438,7 +525,7 @@
this.label1.Location = new System.Drawing.Point(26, 42); this.label1.Location = new System.Drawing.Point(26, 42);
this.label1.Name = "label1"; this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(72, 13); this.label1.Size = new System.Drawing.Size(72, 13);
this.label1.TabIndex = 7; this.label1.TabIndex = 2;
this.label1.Text = "FilterPrecision"; this.label1.Text = "FilterPrecision";
// //
// filterPrecision // filterPrecision
@@ -452,7 +539,7 @@
this.filterPrecision.Location = new System.Drawing.Point(127, 40); this.filterPrecision.Location = new System.Drawing.Point(127, 40);
this.filterPrecision.Name = "filterPrecision"; this.filterPrecision.Name = "filterPrecision";
this.filterPrecision.Size = new System.Drawing.Size(51, 20); this.filterPrecision.Size = new System.Drawing.Size(51, 20);
this.filterPrecision.TabIndex = 6; this.filterPrecision.TabIndex = 3;
this.filterPrecision.Value = new decimal(new int[] { this.filterPrecision.Value = new decimal(new int[] {
25, 25,
0, 0,
@@ -465,7 +552,7 @@
this.castToBone.Location = new System.Drawing.Point(6, 161); this.castToBone.Location = new System.Drawing.Point(6, 161);
this.castToBone.Name = "castToBone"; this.castToBone.Name = "castToBone";
this.castToBone.Size = new System.Drawing.Size(131, 17); this.castToBone.Size = new System.Drawing.Size(131, 17);
this.castToBone.TabIndex = 5; this.castToBone.TabIndex = 8;
this.castToBone.Text = "All nodes cast to bone"; this.castToBone.Text = "All nodes cast to bone";
this.castToBone.UseVisualStyleBackColor = true; this.castToBone.UseVisualStyleBackColor = true;
// //
@@ -489,17 +576,145 @@
this.eulerFilter.Location = new System.Drawing.Point(6, 22); this.eulerFilter.Location = new System.Drawing.Point(6, 22);
this.eulerFilter.Name = "eulerFilter"; this.eulerFilter.Name = "eulerFilter";
this.eulerFilter.Size = new System.Drawing.Size(72, 17); this.eulerFilter.Size = new System.Drawing.Size(72, 17);
this.eulerFilter.TabIndex = 3; this.eulerFilter.TabIndex = 1;
this.eulerFilter.Text = "EulerFilter"; this.eulerFilter.Text = "EulerFilter";
this.eulerFilter.UseVisualStyleBackColor = true; 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 // ExportOptions
// //
this.AcceptButton = this.OKbutton; this.AcceptButton = this.OKbutton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.Cancel; 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.groupBox2);
this.Controls.Add(this.groupBox1); this.Controls.Add(this.groupBox1);
this.Controls.Add(this.Cancel); this.Controls.Add(this.Cancel);
@@ -516,11 +731,20 @@
this.groupBox1.PerformLayout(); this.groupBox1.PerformLayout();
this.panel1.ResumeLayout(false); this.panel1.ResumeLayout(false);
this.panel1.PerformLayout(); this.panel1.PerformLayout();
this.l2dGroupBox.ResumeLayout(false);
this.l2dGroupBox.PerformLayout();
this.l2dMotionExportMethodPanel.ResumeLayout(false);
this.l2dMotionExportMethodPanel.PerformLayout();
this.groupBox2.ResumeLayout(false); this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout(); this.groupBox2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.scaleFactor)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.scaleFactor)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.boneSize)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.boneSize)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.filterPrecision)).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.ResumeLayout(false);
this.PerformLayout(); this.PerformLayout();
@@ -559,8 +783,25 @@
private System.Windows.Forms.CheckBox restoreExtensionName; private System.Windows.Forms.CheckBox restoreExtensionName;
private System.Windows.Forms.CheckBox openAfterExport; private System.Windows.Forms.CheckBox openAfterExport;
private System.Windows.Forms.CheckBox exportAllUvsAsDiffuseMaps; private System.Windows.Forms.CheckBox exportAllUvsAsDiffuseMaps;
private System.Windows.Forms.ToolTip exportUvsTooltip; private System.Windows.Forms.ToolTip optionTooltip;
private System.Windows.Forms.CheckBox exportSpriteWithAlphaMask; private System.Windows.Forms.CheckBox exportSpriteWithAlphaMask;
private System.Windows.Forms.RadioButton towebp; 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;
private System.Windows.Forms.RadioButton l2dAnimationClipRadioButton;
private System.Windows.Forms.RadioButton l2dMonoBehaviourRadioButton;
private System.Windows.Forms.Panel l2dMotionExportMethodPanel;
} }
} }

View File

@@ -1,5 +1,6 @@
using AssetStudio; using AssetStudio;
using System; using System;
using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
namespace AssetStudioGUI namespace AssetStudioGUI
@@ -14,15 +15,8 @@ namespace AssetStudioGUI
converttexture.Checked = Properties.Settings.Default.convertTexture; converttexture.Checked = Properties.Settings.Default.convertTexture;
exportSpriteWithAlphaMask.Checked = Properties.Settings.Default.exportSpriteWithMask; exportSpriteWithAlphaMask.Checked = Properties.Settings.Default.exportSpriteWithMask;
convertAudio.Checked = Properties.Settings.Default.convertAudio; convertAudio.Checked = Properties.Settings.Default.convertAudio;
var str = Properties.Settings.Default.convertType.ToString(); var defaultImageType = Properties.Settings.Default.convertType.ToString();
foreach (Control c in panel1.Controls) ((RadioButton)panel1.Controls.Cast<Control>().First(x => x.Text == defaultImageType)).Checked = true;
{
if (c.Text == str)
{
((RadioButton)c).Checked = true;
break;
}
}
openAfterExport.Checked = Properties.Settings.Default.openAfterExport; openAfterExport.Checked = Properties.Settings.Default.openAfterExport;
eulerFilter.Checked = Properties.Settings.Default.eulerFilter; eulerFilter.Checked = Properties.Settings.Default.eulerFilter;
filterPrecision.Value = Properties.Settings.Default.filterPrecision; filterPrecision.Value = Properties.Settings.Default.filterPrecision;
@@ -37,6 +31,16 @@ namespace AssetStudioGUI
fbxVersion.SelectedIndex = Properties.Settings.Default.fbxVersion; fbxVersion.SelectedIndex = Properties.Settings.Default.fbxVersion;
fbxFormat.SelectedIndex = Properties.Settings.Default.fbxFormat; 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;
} }
private void OKbutton_Click(object sender, EventArgs e) private void OKbutton_Click(object sender, EventArgs e)
@@ -46,14 +50,8 @@ namespace AssetStudioGUI
Properties.Settings.Default.convertTexture = converttexture.Checked; Properties.Settings.Default.convertTexture = converttexture.Checked;
Properties.Settings.Default.exportSpriteWithMask = exportSpriteWithAlphaMask.Checked; Properties.Settings.Default.exportSpriteWithMask = exportSpriteWithAlphaMask.Checked;
Properties.Settings.Default.convertAudio = convertAudio.Checked; Properties.Settings.Default.convertAudio = convertAudio.Checked;
foreach (Control c in panel1.Controls) var checkedImageType = (RadioButton)panel1.Controls.Cast<Control>().First(x => ((RadioButton)x).Checked);
{ Properties.Settings.Default.convertType = (ImageFormat)Enum.Parse(typeof(ImageFormat), checkedImageType.Text);
if (((RadioButton)c).Checked)
{
Properties.Settings.Default.convertType = (ImageFormat)Enum.Parse(typeof(ImageFormat), c.Text);
break;
}
}
Properties.Settings.Default.openAfterExport = openAfterExport.Checked; Properties.Settings.Default.openAfterExport = openAfterExport.Checked;
Properties.Settings.Default.eulerFilter = eulerFilter.Checked; Properties.Settings.Default.eulerFilter = eulerFilter.Checked;
Properties.Settings.Default.filterPrecision = filterPrecision.Value; Properties.Settings.Default.filterPrecision = filterPrecision.Value;
@@ -67,11 +65,27 @@ namespace AssetStudioGUI
Properties.Settings.Default.scaleFactor = scaleFactor.Value; Properties.Settings.Default.scaleFactor = scaleFactor.Value;
Properties.Settings.Default.fbxVersion = fbxVersion.SelectedIndex; Properties.Settings.Default.fbxVersion = fbxVersion.SelectedIndex;
Properties.Settings.Default.fbxFormat = fbxFormat.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;
Properties.Settings.Default.Save(); Properties.Settings.Default.Save();
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
Close(); 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) private void Cancel_Click(object sender, EventArgs e)
{ {
DialogResult = DialogResult.Cancel; DialogResult = DialogResult.Cancel;

View File

@@ -117,7 +117,7 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<metadata name="exportUvsTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="optionTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value> <value>17, 17</value>
</metadata> </metadata>
</root> </root>

View File

@@ -1,4 +1,7 @@
using AssetStudio; using Arknights;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using AssetStudio;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -238,12 +241,69 @@ namespace AssetStudioGUI
} }
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 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 type = Properties.Settings.Default.convertType;
var spriteMaskMode = Properties.Settings.Default.exportSpriteWithMask ? SpriteMaskMode.Export : SpriteMaskMode.Off; var spriteMaskMode = Properties.Settings.Default.exportSpriteWithMask ? SpriteMaskMode.Export : SpriteMaskMode.Off;
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath)) if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false; return false;
var image = ((Sprite)item.Asset).GetImage(spriteMaskMode: spriteMaskMode);
var image = item.AkPortraitSprite.AkGetImage(spriteMaskMode: spriteMaskMode);
if (image != null) if (image != null)
{ {
using (image) using (image)
@@ -260,15 +320,17 @@ namespace AssetStudioGUI
public static bool ExportRawFile(AssetItem item, string exportPath) public static bool ExportRawFile(AssetItem item, string exportPath)
{ {
if (item.Asset == null)
return false;
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath)) if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath))
return false; return false;
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData()); File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
return true; 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); fullPath = Path.Combine(dir, fileName + extension);
if (!File.Exists(fullPath)) if (!File.Exists(fullPath))
{ {
@@ -337,6 +399,8 @@ namespace AssetStudioGUI
public static bool ExportDumpFile(AssetItem item, string exportPath) public static bool ExportDumpFile(AssetItem item, string exportPath)
{ {
if (item.Asset == null)
return false;
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath)) if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath))
return false; return false;
var str = item.Asset.Dump(); var str = item.Asset.Dump();
@@ -377,6 +441,8 @@ namespace AssetStudioGUI
return ExportMovieTexture(item, exportPath); return ExportMovieTexture(item, exportPath);
case ClassIDType.Sprite: case ClassIDType.Sprite:
return ExportSprite(item, exportPath); return ExportSprite(item, exportPath);
case ClassIDType.AkPortraitSprite:
return ExportPortraitSprite(item, exportPath);
case ClassIDType.Animator: case ClassIDType.Animator:
return ExportAnimator(item, exportPath); return ExportAnimator(item, exportPath);
case ClassIDType.AnimationClip: case ClassIDType.AnimationClip:

View File

@@ -1,5 +1,8 @@
using AssetStudio; using AssetStudio;
using System; using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms; using System.Windows.Forms;
namespace AssetStudioGUI namespace AssetStudioGUI
@@ -7,15 +10,122 @@ namespace AssetStudioGUI
class GUILogger : ILogger class GUILogger : ILogger
{ {
public bool ShowErrorMessage = false; public bool ShowErrorMessage = false;
private bool IsFileLoggerRunning = false;
private string LoggerInitString;
private string FileLogName;
private string FileLogPath;
private Action<string> action; private Action<string> action;
private bool _useFileLogger = false;
public bool UseFileLogger
{
get => _useFileLogger;
set
{
_useFileLogger = value;
if (_useFileLogger && !IsFileLoggerRunning)
{
var appAssembly = typeof(Program).Assembly.GetName();
FileLogName = $"{appAssembly.Name}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log";
FileLogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FileLogName);
LogToFile(LoggerEvent.Verbose, $"# {LoggerInitString} - Logger launched #");
IsFileLoggerRunning = true;
}
else if (!_useFileLogger && IsFileLoggerRunning)
{
LogToFile(LoggerEvent.Verbose, "# Logger closed #");
IsFileLoggerRunning = false;
}
}
}
public GUILogger(Action<string> action) public GUILogger(Action<string> action)
{ {
this.action = action; this.action = action;
var appAssembly = typeof(Program).Assembly.GetName();
var arch = Environment.Is64BitProcess ? "x64" : "x32";
var frameworkName = AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
LoggerInitString = $"{appAssembly.Name} v{appAssembly.Version} [{arch}] [{frameworkName}]";
try
{
Console.Title = $"Console Logger - {appAssembly.Name} v{appAssembly.Version}";
Console.OutputEncoding = System.Text.Encoding.UTF8;
}
catch
{
// ignored
}
Console.WriteLine($"# {LoggerInitString}");
}
private static string ColorLogLevel(LoggerEvent logLevel)
{
var formattedLevel = $"[{logLevel}]";
switch (logLevel)
{
case LoggerEvent.Info:
return $"{formattedLevel.Color(ColorConsole.BrightCyan)}";
case LoggerEvent.Warning:
return $"{formattedLevel.Color(ColorConsole.BrightYellow)}";
case LoggerEvent.Error:
return $"{formattedLevel.Color(ColorConsole.BrightRed)}";
default:
return formattedLevel;
}
}
private static string FormatMessage(LoggerEvent logMsgLevel, string message, bool toConsole)
{
message = message.TrimEnd();
var multiLine = message.Contains('\n');
string formattedMessage;
if (toConsole)
{
var colorLogLevel = ColorLogLevel(logMsgLevel);
formattedMessage = $"{colorLogLevel} {message}";
if (multiLine)
{
formattedMessage = formattedMessage.Replace("\n", $"\n{colorLogLevel} ");
}
}
else
{
var curTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
message = Regex.Replace(message, @"\e\[[0-9;]*m(?:\e\[K)?", ""); //Delete ANSI colors
var logLevel = $"{logMsgLevel.ToString().ToUpper(),-7}";
formattedMessage = $"{curTime} | {logLevel} | {message}";
if (multiLine)
{
formattedMessage = formattedMessage.Replace("\n", $"\n{curTime} | {logLevel} | ");
}
}
return formattedMessage;
}
private async void LogToFile(LoggerEvent logMsgLevel, string message)
{
using (var sw = new StreamWriter(FileLogPath, append: true, System.Text.Encoding.UTF8))
{
await sw.WriteLineAsync(FormatMessage(logMsgLevel, message, toConsole: false));
}
} }
public void Log(LoggerEvent loggerEvent, string message, bool ignoreLevel) public void Log(LoggerEvent loggerEvent, string message, bool ignoreLevel)
{ {
//File logger
if (_useFileLogger)
{
LogToFile(loggerEvent, message);
}
//Console logger
Console.WriteLine(FormatMessage(loggerEvent, message, toConsole: true));
//GUI logger
switch (loggerEvent) switch (loggerEvent)
{ {
case LoggerEvent.Error: case LoggerEvent.Error:

View File

@@ -12,7 +12,7 @@ namespace AssetStudioGUI.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.8.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -286,5 +286,137 @@ namespace AssetStudioGUI.Properties {
this["exportSpriteWithMask"] = value; this["exportSpriteWithMask"] = value;
} }
} }
[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")]
public global::CubismLive2DExtractor.Live2DMotionMode l2dMotionMode {
get {
return ((global::CubismLive2DExtractor.Live2DMotionMode)(this["l2dMotionMode"]));
}
set {
this["l2dMotionMode"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool l2dForceBezier {
get {
return ((bool)(this["l2dForceBezier"]));
}
set {
this["l2dForceBezier"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool showConsole {
get {
return ((bool)(this["showConsole"]));
}
set {
this["showConsole"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool useFileLogger {
get {
return ((bool)(this["useFileLogger"]));
}
set {
this["useFileLogger"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool buildTreeStructure {
get {
return ((bool)(this["buildTreeStructure"]));
}
set {
this["buildTreeStructure"] = value;
}
}
} }
} }

View File

@@ -68,5 +68,38 @@
<Setting Name="exportSpriteWithMask" Type="System.Boolean" Scope="User"> <Setting Name="exportSpriteWithMask" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value> <Value Profile="(Default)">True</Value>
</Setting> </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>
<Setting Name="l2dForceBezier" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="showConsole" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="useFileLogger" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="buildTreeStructure" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@@ -158,10 +158,13 @@ namespace AssetStudioGUI
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count); var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
var objectAssetItemDic = new Dictionary<Object, AssetItem>(objectCount); var objectAssetItemDic = new Dictionary<Object, AssetItem>(objectCount);
var containers = new List<(PPtr<Object>, string)>(); var containers = new List<(PPtr<Object>, string)>();
int i = 0; allContainers.Clear();
var i = 0;
Progress.Reset(); Progress.Reset();
foreach (var assetsFile in assetsManager.assetsFileList) foreach (var assetsFile in assetsManager.assetsFileList)
{ {
var preloadTable = Array.Empty<PPtr<Object>>();
foreach (var asset in assetsFile.Objects) foreach (var asset in assetsFile.Objects)
{ {
var assetItem = new AssetItem(asset); var assetItem = new AssetItem(asset);
@@ -170,6 +173,9 @@ namespace AssetStudioGUI
var exportable = false; var exportable = false;
switch (asset) switch (asset)
{ {
case PreloadData m_PreloadData:
preloadTable = m_PreloadData.m_Assets;
break;
case GameObject m_GameObject: case GameObject m_GameObject:
assetItem.Text = m_GameObject.m_Name; assetItem.Text = m_GameObject.m_Name;
break; break;
@@ -187,7 +193,7 @@ namespace AssetStudioGUI
break; break;
case VideoClip m_VideoClip: case VideoClip m_VideoClip:
if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath)) if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath))
assetItem.FullSize = asset.byteSize + (long)m_VideoClip.m_ExternalResources.m_Size; assetItem.FullSize = asset.byteSize + m_VideoClip.m_ExternalResources.m_Size;
assetItem.Text = m_VideoClip.m_Name; assetItem.Text = m_VideoClip.m_Name;
exportable = true; exportable = true;
break; break;
@@ -226,17 +232,23 @@ namespace AssetStudioGUI
productName = m_PlayerSettings.productName; productName = m_PlayerSettings.productName;
break; break;
case AssetBundle m_AssetBundle: case AssetBundle m_AssetBundle:
var isStreamedSceneAssetBundle = m_AssetBundle.m_IsStreamedSceneAssetBundle;
if (!isStreamedSceneAssetBundle)
{
preloadTable = m_AssetBundle.m_PreloadTable;
}
assetItem.Text = string.IsNullOrEmpty(m_AssetBundle.m_AssetBundleName) ? m_AssetBundle.m_Name : m_AssetBundle.m_AssetBundleName;
foreach (var m_Container in m_AssetBundle.m_Container) foreach (var m_Container in m_AssetBundle.m_Container)
{ {
var preloadIndex = m_Container.Value.preloadIndex; var preloadIndex = m_Container.Value.preloadIndex;
var preloadSize = m_Container.Value.preloadSize; var preloadSize = isStreamedSceneAssetBundle ? preloadTable.Length : m_Container.Value.preloadSize;
var preloadEnd = preloadIndex + preloadSize; var preloadEnd = preloadIndex + preloadSize;
for (int k = preloadIndex; k < preloadEnd; k++) for (var k = preloadIndex; k < preloadEnd; k++)
{ {
containers.Add((m_AssetBundle.m_PreloadTable[k], m_Container.Key)); containers.Add((preloadTable[k], m_Container.Key));
} }
} }
assetItem.Text = m_AssetBundle.m_Name;
break; break;
case ResourceManager m_ResourceManager: case ResourceManager m_ResourceManager:
foreach (var m_Container in m_ResourceManager.m_Container) foreach (var m_Container in m_ResourceManager.m_Container)
@@ -259,12 +271,22 @@ namespace AssetStudioGUI
Progress.Report(++i, objectCount); Progress.Report(++i, objectCount);
} }
} }
foreach ((var pptr, var container) in containers) foreach (var (pptr, container) in containers)
{ {
if (pptr.TryGet(out var obj)) if (pptr.TryGet(out var obj))
{ {
objectAssetItemDic[obj].Container = container; var asset = objectAssetItemDic[obj];
asset.Container = container;
allContainers[obj] = 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) foreach (var tmp in exportableAssets)
@@ -275,12 +297,19 @@ namespace AssetStudioGUI
visibleAssets = exportableAssets; visibleAssets = exportableAssets;
if (!Properties.Settings.Default.buildTreeStructure)
{
Logger.Info("Building tree structure step is skipped");
objectAssetItemDic.Clear();
return (productName, new List<TreeNode>());
}
Logger.Info("Building tree structure..."); Logger.Info("Building tree structure...");
var treeNodeCollection = new List<TreeNode>(); var treeNodeCollection = new List<TreeNode>();
var treeNodeDictionary = new Dictionary<GameObject, GameObjectTreeNode>(); var treeNodeDictionary = new Dictionary<GameObject, GameObjectTreeNode>();
var assetsFileCount = assetsManager.assetsFileList.Count; var assetsFileCount = assetsManager.assetsFileList.Count;
int j = 0; var j = 0;
Progress.Reset(); Progress.Reset();
foreach (var assetsFile in assetsManager.assetsFileList) foreach (var assetsFile in assetsManager.assetsFileList)
{ {
@@ -348,7 +377,6 @@ namespace AssetStudioGUI
Progress.Report(++j, assetsFileCount); Progress.Report(++j, assetsFileCount);
} }
treeNodeDictionary.Clear(); treeNodeDictionary.Clear();
objectAssetItemDic.Clear(); objectAssetItemDic.Clear();
return (productName, treeNodeCollection); return (productName, treeNodeCollection);
@@ -386,7 +414,6 @@ namespace AssetStudioGUI
typeMap.Add(assetsFile.unityVersion, items); typeMap.Add(assetsFile.unityVersion, items);
} }
} }
return typeMap; return typeMap;
} }
@@ -726,7 +753,7 @@ namespace AssetStudioGUI
public static string DumpAsset(Object obj) public static string DumpAsset(Object obj)
{ {
var str = obj.Dump(); var str = obj?.Dump();
if (str == null && obj is MonoBehaviour m_MonoBehaviour) if (str == null && obj is MonoBehaviour m_MonoBehaviour)
{ {
var type = MonoBehaviourToTypeTree(m_MonoBehaviour); var type = MonoBehaviourToTypeTree(m_MonoBehaviour);
@@ -745,6 +772,8 @@ namespace AssetStudioGUI
public static void ExportLive2D(Object[] cubismMocs, string exportPath) public static void ExportLive2D(Object[] cubismMocs, string exportPath)
{ {
var baseDestPath = Path.Combine(exportPath, "Live2DOutput"); var baseDestPath = Path.Combine(exportPath, "Live2DOutput");
var motionMode = Properties.Settings.Default.l2dMotionMode;
var forceBezier = Properties.Settings.Default.l2dForceBezier;
ThreadPool.QueueUserWorkItem(state => ThreadPool.QueueUserWorkItem(state =>
{ {
@@ -753,48 +782,73 @@ namespace AssetStudioGUI
var useFullContainerPath = false; var useFullContainerPath = false;
if (cubismMocs.Length > 1) if (cubismMocs.Length > 1)
{ {
var basePathSet = cubismMocs.Select(x => allContainers[x].Substring(0, allContainers[x].LastIndexOf("/"))).ToHashSet(); var basePathSet = cubismMocs.Select(x =>
{
var pathLen = allContainers.TryGetValue(x, out var itemContainer) ? itemContainer.LastIndexOf("/") : 0;
pathLen = pathLen < 0 ? allContainers[x].Length : pathLen;
return itemContainer?.Substring(0, pathLen);
}).ToHashSet();
if (basePathSet.All(x => x == null))
{
Logger.Error($"Live2D Cubism export error\r\nCannot find any model related files");
StatusStripUpdate("Live2D export canceled");
Progress.Reset();
return;
}
if (basePathSet.Count != cubismMocs.Length) if (basePathSet.Count != cubismMocs.Length)
{ {
useFullContainerPath = true; useFullContainerPath = true;
} }
} }
var basePathList = useFullContainerPath ?
cubismMocs.Select(x => allContainers[x]).ToList() : var basePathList = cubismMocs.Select(x =>
cubismMocs.Select(x => allContainers[x].Substring(0, allContainers[x].LastIndexOf("/"))).ToList(); {
allContainers.TryGetValue(x, out var container);
container = useFullContainerPath
? container
: container?.Substring(0, container.LastIndexOf("/"));
return container;
}).Where(x => x != null).ToList();
var lookup = allContainers.ToLookup( var lookup = allContainers.ToLookup(
x => basePathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))), x => basePathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
x => x.Key x => x.Key
); );
var totalModelCount = lookup.LongCount(x => x.Key != null); var totalModelCount = lookup.LongCount(x => x.Key != null);
var name = "";
var modelCounter = 0; var modelCounter = 0;
foreach (var assets in lookup) foreach (var assets in lookup)
{ {
var container = assets.Key; var srcContainer = assets.Key;
if (container == null) if (srcContainer == null)
continue; continue;
name = container; var container = srcContainer;
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{container}\"..."); Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer}\"...");
try try
{ {
var modelName = useFullContainerPath ? Path.GetFileNameWithoutExtension(container) : container.Substring(container.LastIndexOf('/') + 1); var modelName = useFullContainerPath ? Path.GetFileNameWithoutExtension(container) : container.Substring(container.LastIndexOf('/') + 1);
container = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container; container = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container;
var destPath = Path.Combine(baseDestPath, container) + Path.DirectorySeparatorChar; var destPath = Path.Combine(baseDestPath, container) + Path.DirectorySeparatorChar;
ExtractLive2D(assets, destPath, modelName, assemblyLoader); ExtractLive2D(assets, destPath, modelName, assemblyLoader, motionMode, forceBezier);
modelCounter++; modelCounter++;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error($"Live2D model export error: \"{name}\"", ex); Logger.Error($"Live2D model export error: \"{srcContainer}\"", ex);
} }
Progress.Report(modelCounter, (int)totalModelCount); Progress.Report(modelCounter, (int)totalModelCount);
} }
Logger.Info($"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s)."); Logger.Info($"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s).");
if (modelCounter < totalModelCount)
{
var total = (int)totalModelCount;
Progress.Report(total, total);
}
if (Properties.Settings.Default.openAfterExport && modelCounter > 0) if (Properties.Settings.Default.openAfterExport && modelCounter > 0)
{ {
OpenFolderInExplorer(exportPath); OpenFolderInExplorer(exportPath);

View File

@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks> <TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
<Version>0.17.3.0</Version> <Version>1.2.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2023</Copyright> <Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2025</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
@@ -22,7 +22,7 @@
<PackageReference Include="Kyaru.Texture2DDecoder"> <PackageReference Include="Kyaru.Texture2DDecoder">
<Version>0.17.0</Version> <Version>0.17.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" /> <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">

View File

@@ -0,0 +1,30 @@
using System;
namespace CubismLive2DExtractor
{
public class AnimationCurve
{
public CubismKeyframeData[] m_Curve { get; set; }
public int m_PreInfinity { get; set; }
public int m_PostInfinity { get; set; }
public int m_RotationOrder { get; set; }
}
public class CubismFadeMotion
{
public string m_Name { get; set; }
public string MotionName { get; set; }
public float FadeInTime { get; set; }
public float FadeOutTime { get; set; }
public string[] ParameterIds { get; set; }
public AnimationCurve[] ParameterCurves { get; set; }
public float[] ParameterFadeInTimes { get; set; }
public float[] ParameterFadeOutTimes { get; set; }
public float MotionLength { get; set; }
public CubismFadeMotion()
{
ParameterIds = Array.Empty<string>();
}
}
}

View File

@@ -0,0 +1,26 @@
namespace CubismLive2DExtractor
{
public class CubismKeyframeData
{
public float time { get; set; }
public float value { get; set; }
public float inSlope { get; set; }
public float outSlope { get; set; }
public int weightedMode { get; set; }
public float inWeight { get; set; }
public float outWeight { get; set; }
public CubismKeyframeData() { }
public CubismKeyframeData(ImportedKeyframe<float> keyframe)
{
time = keyframe.time;
value = keyframe.value;
inSlope = keyframe.inSlope;
outSlope = keyframe.outSlope;
weightedMode = 0;
inWeight = 0;
outWeight = 0;
}
}
}

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using AssetStudio; using AssetStudio;
@@ -73,7 +72,7 @@ namespace CubismLive2DExtractor
if (iAnim.TrackList.Count == 0 || iAnim.Events.Count == 0) if (iAnim.TrackList.Count == 0 || iAnim.Events.Count == 0)
{ {
Logger.Warning($"[Motion Converter] {iAnim.Name} has {iAnim.TrackList.Count} tracks and {iAnim.Events.Count} event!."); Logger.Warning($"[Motion Converter] \"{iAnim.Name}\" has {iAnim.TrackList.Count} tracks and {iAnim.Events.Count} event!.");
} }
} }
} }
@@ -84,7 +83,7 @@ namespace CubismLive2DExtractor
GetLive2dPath(binding, out var target, out var boneName); GetLive2dPath(binding, out var target, out var boneName);
if (string.IsNullOrEmpty(boneName)) if (string.IsNullOrEmpty(boneName))
{ {
Logger.Warning($"[Motion Converter] {iAnim.Name} read fail on binding {Array.IndexOf(m_ClipBindingConstant.genericBindings, binding)}"); Logger.Warning($"[Motion Converter] \"{iAnim.Name}\" read fail on binding {Array.IndexOf(m_ClipBindingConstant.genericBindings, binding)}");
return; return;
} }
@@ -99,7 +98,7 @@ namespace CubismLive2DExtractor
GetLive2dPath(binding, out var target, out var boneName); GetLive2dPath(binding, out var target, out var boneName);
if (string.IsNullOrEmpty(boneName)) if (string.IsNullOrEmpty(boneName))
{ {
Logger.Warning($"[Motion Converter] {iAnim.Name} read fail on binding {Array.IndexOf(m_ClipBindingConstant.genericBindings, binding)}"); Logger.Warning($"[Motion Converter] \"{iAnim.Name}\" read fail on binding {Array.IndexOf(m_ClipBindingConstant.genericBindings, binding)}");
return; return;
} }

View File

@@ -1,7 +1,9 @@
using System; // File Format Specifications
// https://github.com/Live2D/CubismSpecs/blob/master/FileFormats/motion3.json.md
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
namespace CubismLive2DExtractor namespace CubismLive2DExtractor
{ {
@@ -18,24 +20,238 @@ namespace CubismLive2DExtractor
public float Fps; public float Fps;
public bool Loop; public bool Loop;
public bool AreBeziersRestricted; public bool AreBeziersRestricted;
public float FadeInTime;
public float FadeOutTime;
public int CurveCount; public int CurveCount;
public int TotalSegmentCount; public int TotalSegmentCount;
public int TotalPointCount; public int TotalPointCount;
public int UserDataCount; public int UserDataCount;
public int TotalUserDataSize; public int TotalUserDataSize;
}; }
public class SerializableCurve public class SerializableCurve
{ {
public string Target; public string Target;
public string Id; public string Id;
public float FadeInTime;
public float FadeOutTime;
public List<float> Segments; public List<float> Segments;
}; }
public class SerializableUserData public class SerializableUserData
{ {
public float Time; public float Time;
public string Value; public string Value;
} }
private static void AddSegments(
CubismKeyframeData curve,
CubismKeyframeData preCurve,
CubismKeyframeData nextCurve,
SerializableCurve cubismCurve,
bool forceBezier,
ref int totalPointCount,
ref int totalSegmentCount,
ref int j
)
{
if (Math.Abs(curve.time - preCurve.time - 0.01f) < 0.0001f) // InverseSteppedSegment
{
if (nextCurve.value == curve.value)
{
cubismCurve.Segments.Add(3f); // Segment ID
cubismCurve.Segments.Add(nextCurve.time);
cubismCurve.Segments.Add(nextCurve.value);
j += 1;
totalPointCount += 1;
totalSegmentCount++;
return;
}
}
if (float.IsPositiveInfinity(curve.inSlope)) // SteppedSegment
{
cubismCurve.Segments.Add(2f); // Segment ID
cubismCurve.Segments.Add(curve.time);
cubismCurve.Segments.Add(curve.value);
totalPointCount += 1;
}
else if (preCurve.outSlope == 0f && Math.Abs(curve.inSlope) < 0.0001f && !forceBezier) // LinearSegment
{
cubismCurve.Segments.Add(0f); // Segment ID
cubismCurve.Segments.Add(curve.time);
cubismCurve.Segments.Add(curve.value);
totalPointCount += 1;
}
else // BezierSegment
{
var tangentLength = (curve.time - preCurve.time) / 3f;
cubismCurve.Segments.Add(1f); // Segment ID
cubismCurve.Segments.Add(preCurve.time + tangentLength);
cubismCurve.Segments.Add(preCurve.outSlope * tangentLength + preCurve.value);
cubismCurve.Segments.Add(curve.time - tangentLength);
cubismCurve.Segments.Add(curve.value - curve.inSlope * tangentLength);
cubismCurve.Segments.Add(curve.time);
cubismCurve.Segments.Add(curve.value);
totalPointCount += 3;
}
totalSegmentCount++;
}
public CubismMotion3Json(CubismFadeMotion fadeMotion, HashSet<string> paramNames, HashSet<string> partNames, bool forceBezier)
{
Version = 3;
Meta = new SerializableMeta
{
// Duration of the motion in seconds.
Duration = fadeMotion.MotionLength,
// Framerate of the motion in seconds.
Fps = 30,
// [Optional] Status of the looping of the motion.
Loop = true,
// [Optional] Status of the restriction of Bezier handles'X translations.
AreBeziersRestricted = true,
// [Optional] Time of the overall Fade-In for easing in seconds.
FadeInTime = fadeMotion.FadeInTime,
// [Optional] Time of the overall Fade-Out for easing in seconds.
FadeOutTime = fadeMotion.FadeOutTime,
// The total number of curves.
CurveCount = (int)fadeMotion.ParameterCurves.LongCount(x => x.m_Curve.Length > 0),
// [Optional] The total number of UserData.
UserDataCount = 0
};
// Motion curves.
Curves = new SerializableCurve[Meta.CurveCount];
var totalSegmentCount = 1;
var totalPointCount = 1;
var actualCurveCount = 0;
for (var i = 0; i < fadeMotion.ParameterCurves.Length; i++)
{
if (fadeMotion.ParameterCurves[i].m_Curve.Length == 0)
continue;
string target;
string paramId = fadeMotion.ParameterIds[i];
switch (paramId)
{
case "Opacity":
case "EyeBlink":
case "LipSync":
target = "Model";
break;
default:
if (paramNames.Contains(paramId))
{
target = "Parameter";
}
else if (partNames.Contains(paramId))
{
target = "PartOpacity";
}
else
{
target = paramId.ToLower().Contains("part") ? "PartOpacity" : "Parameter";
AssetStudio.Logger.Warning($"[{fadeMotion.m_Name}] Binding error: Unable to find \"{paramId}\" among the model parts/parameters");
}
break;
}
Curves[actualCurveCount] = new SerializableCurve
{
// Target type.
Target = target,
// Identifier for mapping curve to target.
Id = paramId,
// [Optional] Time of the Fade - In for easing in seconds.
FadeInTime = fadeMotion.ParameterFadeInTimes[i],
// [Optional] Time of the Fade - Out for easing in seconds.
FadeOutTime = fadeMotion.ParameterFadeOutTimes[i],
// Flattened segments.
Segments = new List<float>
{
// First point
fadeMotion.ParameterCurves[i].m_Curve[0].time,
fadeMotion.ParameterCurves[i].m_Curve[0].value
}
};
for (var j = 1; j < fadeMotion.ParameterCurves[i].m_Curve.Length; j++)
{
var curve = fadeMotion.ParameterCurves[i].m_Curve[j];
var preCurve = fadeMotion.ParameterCurves[i].m_Curve[j - 1];
var next = fadeMotion.ParameterCurves[i].m_Curve.ElementAtOrDefault(j + 1);
var nextCurve = next ?? new CubismKeyframeData();
AddSegments(curve, preCurve, nextCurve, Curves[actualCurveCount], forceBezier, ref totalPointCount, ref totalSegmentCount, ref j);
}
actualCurveCount++;
}
// The total number of segments (from all curves).
Meta.TotalSegmentCount = totalSegmentCount;
// The total number of points (from all segments of all curves).
Meta.TotalPointCount = totalPointCount;
UserData = Array.Empty<SerializableUserData>();
// [Optional] The total size of UserData in bytes.
Meta.TotalUserDataSize = 0;
}
public CubismMotion3Json(ImportedKeyframedAnimation animation, bool forceBezier)
{
Version = 3;
Meta = new SerializableMeta
{
Duration = animation.Duration,
Fps = animation.SampleRate,
Loop = true,
AreBeziersRestricted = true,
FadeInTime = 0,
FadeOutTime = 0,
CurveCount = animation.TrackList.Count,
UserDataCount = animation.Events.Count
};
Curves = new SerializableCurve[Meta.CurveCount];
var totalSegmentCount = 1;
var totalPointCount = 1;
for (var i = 0; i < Meta.CurveCount; i++)
{
var track = animation.TrackList[i];
Curves[i] = new SerializableCurve
{
Target = track.Target,
Id = track.Name,
FadeInTime = -1,
FadeOutTime = -1,
Segments = new List<float>
{
0f,
track.Curve[0].value
}
};
for (var j = 1; j < track.Curve.Count; j++)
{
var curve = new CubismKeyframeData(track.Curve[j]);
var preCurve = new CubismKeyframeData(track.Curve[j - 1]);
var next = track.Curve.ElementAtOrDefault(j + 1);
var nextCurve = next != null ? new CubismKeyframeData(next) : new CubismKeyframeData();
AddSegments(curve, preCurve, nextCurve, Curves[i], forceBezier, ref totalPointCount, ref totalSegmentCount, ref j);
}
}
Meta.TotalSegmentCount = totalSegmentCount;
Meta.TotalPointCount = totalPointCount;
UserData = new SerializableUserData[Meta.UserDataCount];
var totalUserDataSize = 0;
for (var i = 0; i < Meta.UserDataCount; i++)
{
var @event = animation.Events[i];
UserData[i] = new SerializableUserData
{
Time = @event.time,
Value = @event.value
};
totalUserDataSize += @event.value.Length;
}
Meta.TotalUserDataSize = totalUserDataSize;
}
} }
} }

View File

@@ -18,7 +18,7 @@ namespace CubismLive2DExtractor
{ {
public static class Live2DExtractor public static class Live2DExtractor
{ {
public static void ExtractLive2D(IGrouping<string, AssetStudio.Object> assets, string destPath, string modelName, AssemblyLoader assemblyLoader) public static void ExtractLive2D(IGrouping<string, AssetStudio.Object> assets, string destPath, string modelName, AssemblyLoader assemblyLoader, Live2DMotionMode motionMode, bool forceBezier = false)
{ {
var destTexturePath = Path.Combine(destPath, "textures") + Path.DirectorySeparatorChar; var destTexturePath = Path.Combine(destPath, "textures") + Path.DirectorySeparatorChar;
var destMotionPath = Path.Combine(destPath, "motions") + Path.DirectorySeparatorChar; var destMotionPath = Path.Combine(destPath, "motions") + Path.DirectorySeparatorChar;
@@ -26,20 +26,75 @@ namespace CubismLive2DExtractor
Directory.CreateDirectory(destPath); Directory.CreateDirectory(destPath);
Directory.CreateDirectory(destTexturePath); Directory.CreateDirectory(destTexturePath);
var monoBehaviours = new List<MonoBehaviour>(); var expressionList = new List<MonoBehaviour>();
var texture2Ds = new List<Texture2D>(); var fadeMotionList = new List<MonoBehaviour>();
var gameObjects = new List<GameObject>(); var gameObjects = new List<GameObject>();
var animationClips = new List<AnimationClip>(); var animationClips = new List<AnimationClip>();
var textures = new SortedSet<string>();
var eyeBlinkParameters = new HashSet<string>();
var lipSyncParameters = new HashSet<string>();
var parameterNames = new HashSet<string>();
var partNames = new HashSet<string>();
MonoBehaviour physics = null;
foreach (var asset in assets) foreach (var asset in assets)
{ {
switch (asset) switch (asset)
{ {
case MonoBehaviour m_MonoBehaviour: case MonoBehaviour m_MonoBehaviour:
monoBehaviours.Add(m_MonoBehaviour); if (m_MonoBehaviour.m_Script.TryGet(out var m_Script))
{
switch (m_Script.m_ClassName)
{
case "CubismMoc":
File.WriteAllBytes($"{destPath}{modelName}.moc3", ParseMoc(m_MonoBehaviour)); //moc
break;
case "CubismPhysicsController":
physics = physics ?? m_MonoBehaviour;
break;
case "CubismExpressionData":
expressionList.Add(m_MonoBehaviour);
break;
case "CubismFadeMotionData":
fadeMotionList.Add(m_MonoBehaviour);
break;
case "CubismEyeBlinkParameter":
if (m_MonoBehaviour.m_GameObject.TryGet(out var blinkGameObject))
{
eyeBlinkParameters.Add(blinkGameObject.m_Name);
}
break;
case "CubismMouthParameter":
if (m_MonoBehaviour.m_GameObject.TryGet(out var mouthGameObject))
{
lipSyncParameters.Add(mouthGameObject.m_Name);
}
break;
case "CubismParameter":
if (m_MonoBehaviour.m_GameObject.TryGet(out var paramGameObject))
{
parameterNames.Add(paramGameObject.m_Name);
}
break;
case "CubismPart":
if (m_MonoBehaviour.m_GameObject.TryGet(out var partGameObject))
{
partNames.Add(partGameObject.m_Name);
}
break;
}
}
break; break;
case Texture2D m_Texture2D: case Texture2D m_Texture2D:
texture2Ds.Add(m_Texture2D); using (var image = m_Texture2D.ConvertToImage(flip: true))
{
using (var file = File.OpenWrite($"{destTexturePath}{m_Texture2D.m_Name}.png"))
{
image.WriteToStream(file, ImageFormat.Png);
}
textures.Add($"textures/{m_Texture2D.m_Name}.png"); //texture
}
break; break;
case GameObject m_GameObject: case GameObject m_GameObject:
gameObjects.Add(m_GameObject); gameObjects.Add(m_GameObject);
@@ -50,15 +105,12 @@ namespace CubismLive2DExtractor
} }
} }
//physics if (textures.Count == 0)
var physics = monoBehaviours.FirstOrDefault(x =>
{ {
if (x.m_Script.TryGet(out var m_Script)) Logger.Warning($"No textures found for \"{modelName}\" model.");
{ }
return m_Script.m_ClassName == "CubismPhysicsController";
} //physics
return false;
});
if (physics != null) if (physics != null)
{ {
try try
@@ -73,36 +125,51 @@ namespace CubismLive2DExtractor
} }
} }
//moc
var moc = monoBehaviours.First(x =>
{
if (x.m_Script.TryGet(out var m_Script))
{
return m_Script.m_ClassName == "CubismMoc";
}
return false;
});
File.WriteAllBytes($"{destPath}{modelName}.moc3", ParseMoc(moc));
//texture
var textures = new SortedSet<string>();
foreach (var texture2D in texture2Ds)
{
using (var image = texture2D.ConvertToImage(flip: true))
{
textures.Add($"textures/{texture2D.m_Name}.png");
using (var file = File.OpenWrite($"{destTexturePath}{texture2D.m_Name}.png"))
{
image.WriteToStream(file, ImageFormat.Png);
}
}
}
//motion //motion
var motions = new SortedDictionary<string, JArray>(); var motions = new SortedDictionary<string, JArray>();
if (gameObjects.Count > 0) if (motionMode == Live2DMotionMode.MonoBehaviour && fadeMotionList.Count > 0) //motion from MonoBehaviour
{ {
Logger.Debug("Motion export method: MonoBehaviour (Fade motion)");
Directory.CreateDirectory(destMotionPath);
foreach (var fadeMotionMono in fadeMotionList)
{
var fadeMotionObj = fadeMotionMono.ToType();
if (fadeMotionObj == null)
{
var m_Type = fadeMotionMono.ConvertToTypeTree(assemblyLoader);
fadeMotionObj = fadeMotionMono.ToType(m_Type);
if (fadeMotionObj == null)
{
Logger.Warning($"Fade motion \"{fadeMotionMono.m_Name}\" is not readable.");
continue;
}
}
var fadeMotion = JsonConvert.DeserializeObject<CubismFadeMotion>(JsonConvert.SerializeObject(fadeMotionObj));
if (fadeMotion.ParameterIds.Length == 0)
continue;
var motionJson = new CubismMotion3Json(fadeMotion, parameterNames, partNames, forceBezier);
var animName = Path.GetFileNameWithoutExtension(fadeMotion.m_Name);
if (motions.ContainsKey(animName))
{
animName = $"{animName}_{fadeMotion.GetHashCode()}";
if (motions.ContainsKey(animName))
continue;
}
var motionPath = new JObject(new JProperty("File", $"motions/{animName}.motion3.json"));
motions.Add(animName, new JArray(motionPath));
File.WriteAllText($"{destMotionPath}{animName}.motion3.json", JsonConvert.SerializeObject(motionJson, Formatting.Indented, new MyJsonConverter()));
}
}
else if (gameObjects.Count > 0) //motion from AnimationClip
{
var exportMethod = motionMode == Live2DMotionMode.AnimationClip
? "AnimationClip"
: "AnimationClip (no Fade motions found)";
Logger.Debug($"Motion export method: {exportMethod}");
var rootTransform = gameObjects[0].m_Transform; var rootTransform = gameObjects[0].m_Transform;
while (rootTransform.m_Father.TryGet(out var m_Father)) while (rootTransform.m_Father.TryGet(out var m_Father))
{ {
@@ -114,114 +181,37 @@ namespace CubismLive2DExtractor
{ {
Directory.CreateDirectory(destMotionPath); Directory.CreateDirectory(destMotionPath);
} }
foreach (ImportedKeyframedAnimation animation in converter.AnimationList) foreach (var animation in converter.AnimationList)
{ {
var json = new CubismMotion3Json var motionJson = new CubismMotion3Json(animation, forceBezier);
{
Version = 3,
Meta = new CubismMotion3Json.SerializableMeta
{
Duration = animation.Duration,
Fps = animation.SampleRate,
Loop = true,
AreBeziersRestricted = true,
CurveCount = animation.TrackList.Count,
UserDataCount = animation.Events.Count
},
Curves = new CubismMotion3Json.SerializableCurve[animation.TrackList.Count]
};
int totalSegmentCount = 1;
int totalPointCount = 1;
for (int i = 0; i < animation.TrackList.Count; i++)
{
var track = animation.TrackList[i];
json.Curves[i] = new CubismMotion3Json.SerializableCurve
{
Target = track.Target,
Id = track.Name,
Segments = new List<float> { 0f, track.Curve[0].value }
};
for (var j = 1; j < track.Curve.Count; j++)
{
var curve = track.Curve[j];
var preCurve = track.Curve[j - 1];
if (Math.Abs(curve.time - preCurve.time - 0.01f) < 0.0001f) //InverseSteppedSegment
{
var nextCurve = track.Curve[j + 1];
if (nextCurve.value == curve.value)
{
json.Curves[i].Segments.Add(3f);
json.Curves[i].Segments.Add(nextCurve.time);
json.Curves[i].Segments.Add(nextCurve.value);
j += 1;
totalPointCount += 1;
totalSegmentCount++;
continue;
}
}
if (float.IsPositiveInfinity(curve.inSlope)) //SteppedSegment
{
json.Curves[i].Segments.Add(2f);
json.Curves[i].Segments.Add(curve.time);
json.Curves[i].Segments.Add(curve.value);
totalPointCount += 1;
}
else if (preCurve.outSlope == 0f && Math.Abs(curve.inSlope) < 0.0001f) //LinearSegment
{
json.Curves[i].Segments.Add(0f);
json.Curves[i].Segments.Add(curve.time);
json.Curves[i].Segments.Add(curve.value);
totalPointCount += 1;
}
else //BezierSegment
{
var tangentLength = (curve.time - preCurve.time) / 3f;
json.Curves[i].Segments.Add(1f);
json.Curves[i].Segments.Add(preCurve.time + tangentLength);
json.Curves[i].Segments.Add(preCurve.outSlope * tangentLength + preCurve.value);
json.Curves[i].Segments.Add(curve.time - tangentLength);
json.Curves[i].Segments.Add(curve.value - curve.inSlope * tangentLength);
json.Curves[i].Segments.Add(curve.time);
json.Curves[i].Segments.Add(curve.value);
totalPointCount += 3;
}
totalSegmentCount++;
}
}
json.Meta.TotalSegmentCount = totalSegmentCount;
json.Meta.TotalPointCount = totalPointCount;
json.UserData = new CubismMotion3Json.SerializableUserData[animation.Events.Count]; var animName = animation.Name;
var totalUserDataSize = 0; if (motions.ContainsKey(animName))
for (var i = 0; i < animation.Events.Count; i++)
{ {
var @event = animation.Events[i]; animName = $"{animName}_{animation.GetHashCode()}";
json.UserData[i] = new CubismMotion3Json.SerializableUserData
{ if (motions.ContainsKey(animName))
Time = @event.time, continue;
Value = @event.value
};
totalUserDataSize += @event.value.Length;
} }
json.Meta.TotalUserDataSize = totalUserDataSize; var motionPath = new JObject(new JProperty("File", $"motions/{animName}.motion3.json"));
motions.Add(animName, new JArray(motionPath));
var motionPath = new JObject(new JProperty("File", $"motions/{animation.Name}.motion3.json")); File.WriteAllText($"{destMotionPath}{animName}.motion3.json", JsonConvert.SerializeObject(motionJson, Formatting.Indented, new MyJsonConverter()));
motions.Add(animation.Name, new JArray(motionPath));
File.WriteAllText($"{destMotionPath}{animation.Name}.motion3.json", JsonConvert.SerializeObject(json, Formatting.Indented, new MyJsonConverter()));
} }
} }
if (motions.Count == 0)
{
Logger.Warning($"No motions found for \"{modelName}\" model.");
}
//expression //expression
var expressions = new JArray(); var expressions = new JArray();
var monoBehaviourArray = monoBehaviours.Where(x => x.m_Name.EndsWith(".exp3")).ToArray(); if (expressionList.Count > 0)
if (monoBehaviourArray.Length > 0)
{ {
Directory.CreateDirectory(destExpressionPath); Directory.CreateDirectory(destExpressionPath);
} }
foreach (var monoBehaviour in monoBehaviourArray) foreach (var monoBehaviour in expressionList)
{ {
var fullName = monoBehaviour.m_Name; var expressionName = monoBehaviour.m_Name.Replace(".exp3", "");
var expressionName = fullName.Replace(".exp3", "");
var expressionObj = monoBehaviour.ToType(); var expressionObj = monoBehaviour.ToType();
if (expressionObj == null) if (expressionObj == null)
{ {
@@ -238,57 +228,38 @@ namespace CubismLive2DExtractor
expressions.Add(new JObject expressions.Add(new JObject
{ {
{ "Name", expressionName }, { "Name", expressionName },
{ "File", $"expressions/{fullName}.json" } { "File", $"expressions/{expressionName}.exp3.json" }
}); });
File.WriteAllText($"{destExpressionPath}{fullName}.json", JsonConvert.SerializeObject(expression, Formatting.Indented)); File.WriteAllText($"{destExpressionPath}{expressionName}.exp3.json", JsonConvert.SerializeObject(expression, Formatting.Indented));
} }
//model //group
var groups = new List<CubismModel3Json.SerializableGroup>(); var groups = new List<CubismModel3Json.SerializableGroup>();
var eyeBlinkParameters = monoBehaviours.Where(x => //Try looking for group IDs among the gameObjects
{
x.m_Script.TryGet(out var m_Script);
return m_Script?.m_ClassName == "CubismEyeBlinkParameter";
}).Select(x =>
{
x.m_GameObject.TryGet(out var m_GameObject);
return m_GameObject?.m_Name;
}).ToHashSet();
if (eyeBlinkParameters.Count == 0) if (eyeBlinkParameters.Count == 0)
{ {
eyeBlinkParameters = gameObjects.Where(x => eyeBlinkParameters = gameObjects.Where(x =>
{ x.m_Name.ToLower().Contains("eye")
return x.m_Name.ToLower().Contains("eye")
&& x.m_Name.ToLower().Contains("open") && x.m_Name.ToLower().Contains("open")
&& (x.m_Name.ToLower().Contains('l') || x.m_Name.ToLower().Contains('r')); && (x.m_Name.ToLower().Contains('l') || x.m_Name.ToLower().Contains('r'))
}).Select(x => x.m_Name).ToHashSet(); ).Select(x => x.m_Name).ToHashSet();
} }
if (lipSyncParameters.Count == 0)
{
lipSyncParameters = gameObjects.Where(x =>
x.m_Name.ToLower().Contains("mouth")
&& x.m_Name.ToLower().Contains("open")
&& x.m_Name.ToLower().Contains('y')
).Select(x => x.m_Name).ToHashSet();
}
groups.Add(new CubismModel3Json.SerializableGroup groups.Add(new CubismModel3Json.SerializableGroup
{ {
Target = "Parameter", Target = "Parameter",
Name = "EyeBlink", Name = "EyeBlink",
Ids = eyeBlinkParameters.ToArray() Ids = eyeBlinkParameters.ToArray()
}); });
var lipSyncParameters = monoBehaviours.Where(x =>
{
x.m_Script.TryGet(out var m_Script);
return m_Script?.m_ClassName == "CubismMouthParameter";
}).Select(x =>
{
x.m_GameObject.TryGet(out var m_GameObject);
return m_GameObject?.m_Name;
}).ToHashSet();
if (lipSyncParameters.Count == 0)
{
lipSyncParameters = gameObjects.Where(x =>
{
return x.m_Name.ToLower().Contains("mouth")
&& x.m_Name.ToLower().Contains("open")
&& x.m_Name.ToLower().Contains('y');
}).Select(x => x.m_Name).ToHashSet();
}
groups.Add(new CubismModel3Json.SerializableGroup groups.Add(new CubismModel3Json.SerializableGroup
{ {
Target = "Parameter", Target = "Parameter",
@@ -296,6 +267,7 @@ namespace CubismLive2DExtractor
Ids = lipSyncParameters.ToArray() Ids = lipSyncParameters.ToArray()
}); });
//model
var model3 = new CubismModel3Json var model3 = new CubismModel3Json
{ {
Version = 3, Version = 3,

View File

@@ -0,0 +1,8 @@
namespace CubismLive2DExtractor
{
public enum Live2DMotionMode
{
MonoBehaviour,
AnimationClip
}
}

View File

@@ -32,7 +32,7 @@ namespace AssetStudio
} }
else 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; Image<Bgra32> tex = null;
if (spriteMaskMode != SpriteMaskMode.MaskOnly) if (spriteMaskMode != SpriteMaskMode.MaskOnly)
@@ -154,9 +154,16 @@ namespace AssetStudio
if (triangles.Length < 1024) if (triangles.Length < 1024)
{ {
var rectP = new RectangularPolygon(0, 0, rect.Width, rect.Height); var rectP = new RectangularPolygon(0, 0, rect.Width, rect.Height);
spriteImage.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, rectP.Clip(path))); try
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical)); {
return spriteImage; spriteImage.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, rectP.Clip(path)));
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
return spriteImage;
}
catch (ArgumentOutOfRangeException)
{
// ignored
}
} }
using (var mask = new Image<Bgra32>(rect.Width, rect.Height, SixLabors.ImageSharp.Color.Black)) using (var mask = new Image<Bgra32>(rect.Width, rect.Height, SixLabors.ImageSharp.Color.Black))
{ {
@@ -167,9 +174,9 @@ namespace AssetStudio
return spriteImage; return spriteImage;
} }
} }
catch catch (Exception e)
{ {
// ignored Logger.Warning($"{m_Sprite.m_Name} Unable to render the packed sprite correctly.\n{e}");
} }
} }

View File

@@ -1,80 +1,42 @@
# Changelog # Changelog
## v0.17.3.0 [13-09-2023] ## v1.1.0 (based on v0.17.4) [16-12-2023]
- [CLI] Added support for exporting split objects (fbx) (https://github.com/aelurum/AssetStudio/pull/10) - Added support for avg sprite without separate alpha tex
- [CLI] Fixed display of asset names in the exported asset list in some working modes - Changed avg face sprite detection method, since we can't rely on the "IsWholeBody" flag
- [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] ## v1.0.0 (based on v0.17.3) [24-09-2023]
- [GUI] Improved Scene Hierarchy tab - Base version changed to AssetStudioMod v0.17.x
- Added "Related assets" item to the context menu (https://github.com/aelurum/AssetStudio/issues/7) - Replaced NetVips lib with ImageSharp lib
- [GUI] Added app.manifest for net472 build - Removed arknights related grouping options
- Added long paths support (win10 v1607+) - Added option to export avg character sprites with aliases in their names (if exist)
- Fixed blurring at high DPI with scaling - Added support for portrait sprites
- [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
- Added CLI version - Added CLI version
- [GUI] Added context menu with "Select all", "Clear selection", "Expand all" and "Collapse all" options to the "Scene Hierarchy" tab - Fixed some bugs
- 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
## v0.16.8.1 [25-11-2021] ## v0.15.47.4 [25-01-2022]
- Uses System.Drawing lib instead of ImageSharp for process textures - Fixed bug with wrong "isWholeBody" flag for the last sprite in a sprite list
- 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 ## v0.15.47.3 [17-09-2021]
- Added "Dump selected assets" option to right click menu - Added support for avg Sprite Groups
- Added 'selected assets count' info to status strip when you select assets
- Added 'exported count / total export count' info to status strip during export ## v0.15.47.2 [04-08-2021]
- "Show error message" option on the "Debug" tab has been renamed to "Show all error messages" and is now disabled by default - Added support for avg character sprites with "isWholeBody" flag
- "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 ## v0.15.47.1 [06-02-2021]
- Fixed a rare bug for resource files with the same name, that caused their data to be overwritten and become incorrect - 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

147
README.md
View File

@@ -1,107 +1,86 @@
# AssetStudioMod # ArknightsStudio
[![Build status](https://ci.appveyor.com/api/projects/status/5qyai0hqs0ktyara/branch/AssetStudioMod?svg=true)](https://ci.appveyor.com/project/aelurum/assetstudiomod/branch/AssetStudioMod) [![Latest release](https://img.shields.io/github/v/release/aelurum/AssetStudio?filter=ak*&label=Latest%20release)](https://github.com/aelurum/AssetStudio/tags) [![Download latest release](https://img.shields.io/github/v/release/aelurum/AssetStudio?filter=ak*&label=Download&labelColor=blue)](https://github.com/aelurum/AssetStudio/tags)
**AssetStudioMod** - modified version of Perfare's [AssetStudio](https://github.com/Perfare/AssetStudio), mainly focused on UI optimization and some functionality enhancements. [![Build status](https://ci.appveyor.com/api/projects/status/857ucvvp0cykv1ni?svg=true)](https://ci.appveyor.com/project/aelurum/arknightsstudio) [![Download latest build](https://img.shields.io/badge/Download_latest_build-brightgreen)](https://ci.appveyor.com/project/aelurum/arknightsstudio/build/artifacts)
**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.** **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. ## ArknightsStudio Features
Unfortunately, I can't continue Perfare's work and keep AssetStudio up to date.
## Game specific modifications
- ArknightsStudio - soon<6F>
## AssetStudio Features
- Support version:
- 3.4 - 2022.1
- 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
- CLI version (for Windows, Linux, Mac) - CLI version (for Windows, Linux, Mac)
- `Animator` and `AnimationClip` assets are not supported in the CLI version - `Animator` and `AnimationClip` assets are not supported in the CLI version
- Support of sprites with alpha mask - Support of sprites with alpha texture
- Support of image export in WebP format - Support of portrait sprites
- Support of Live2D Cubism 3 model export - Correct support of avg character sprites
- Ported from my fork of Perfare's [UnityLive2DExtractor](https://github.com/aelurum/UnityLive2DExtractor) - Correct support of character art sprites
- 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)
## Requirements ## Requirements
- AssetStudioMod.net472 - ArknightsStudio-net472
- GUI/CLI - [.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/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) - 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) - 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) - 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) - CLI (Linux/Mac) - [.NET Runtime 7.0](https://dotnet.microsoft.com/download/dotnet/7.0)
- ArknightsStudio-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)
## CLI Usage ## 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 ### Run
- Command-line: `AssetStudioModCLI <asset folder path>` - Command-line: `ArknightsStudioCLI <asset folder path>`
- Command-line for Portable versions (.NET 6+): `dotnet AssetStudioModCLI.dll <asset folder path>` - Command-line for Portable versions (.NET 6+): `dotnet ArknightsStudioCLI.dll <asset folder path>`
### Basic Samples ### Basic Samples
- Show a list with a number of assets of each type available for export - 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 types - Export assets of all supported for export types
``` ```
AssetStudioModCLI <asset folder path> ArknightsStudioCLI <asset folder path>
``` ```
- Export assets of specific types - 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 - Export assets grouped by type
``` ```
AssetStudioModCLI <asset folder path> -g type ArknightsStudioCLI <asset folder path> -g type
``` ```
- Export assets to a specified output folder - 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 - 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`, `--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) - 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. > 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. Any other options will be ignored.
@@ -109,50 +88,50 @@ Any other options will be ignored.
### Advanced Samples ### Advanced Samples
- Export image assets converted to webp format to a specified output folder - 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 - 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 - 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 ArknightsStudioCLI <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
``` ```
- Load assets of all types and show them (similar to "Display all assets" option in the GUI) - 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 - 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 ## GUI Usage
@@ -161,13 +140,13 @@ AssetStudioModCLI <asset folder path> -m dump -t material --load-all
Use **File->Load file** or **File->Load folder**. 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 ### Extract/Decompress AssetBundles
Use **File->Extract file** or **File->Extract folder**. Use **File->Extract file** or **File->Extract folder**.
### Export Assets, Live2D models ### Export Assets
Use **Export** menu. Use **Export** menu.
@@ -183,14 +162,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 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 ## Build
* Visual Studio 2022 or newer * Visual Studio 2022 or newer

View File

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>0.17.3.0</Version> <Version>1.2.0</Version>
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright> <Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>