Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a00c857ac3 | ||
|
|
5333757843 | ||
|
|
4747687bec | ||
|
|
5c2ac1a5e8 | ||
|
|
e2eae53ac0 | ||
|
|
87347e8b60 | ||
|
|
0fdbddea55 | ||
|
|
823190abb7 | ||
|
|
60aef1b8ed | ||
|
|
f82a73f018 | ||
|
|
d42a1879ab | ||
|
|
632e5f8d08 | ||
|
|
51d259464b | ||
|
|
efd06648ad | ||
|
|
b27482e22b | ||
|
|
d572bd0e64 | ||
|
|
e415740373 | ||
|
|
45bf9251c9 | ||
|
|
75ebe67713 | ||
|
|
ed7b0a2415 | ||
|
|
22ab5c0633 | ||
|
|
a2bc935850 | ||
|
|
cb84c137e5 | ||
|
|
25c611fb9e | ||
|
|
6d3875cb2c | ||
|
|
be4ced77ea | ||
|
|
2bd762e8f4 | ||
|
|
a926644ff6 | ||
|
|
a4cdff5934 | ||
|
|
2e10e627b0 | ||
|
|
e216abd6be | ||
|
|
93c7e617d8 | ||
|
|
000916913e | ||
|
|
28f9744497 | ||
|
|
c3f99216b6 | ||
|
|
19c4835ea3 | ||
|
|
6b321da695 | ||
|
|
7cca301f7a | ||
|
|
5ac597c935 | ||
|
|
171962e61f | ||
|
|
c8a21838c9 | ||
|
|
cf67815d53 | ||
|
|
94c8b355fe | ||
|
|
4e41caf203 | ||
|
|
74a8555514 | ||
|
|
e1d883adf6 | ||
|
|
9784df0e16 | ||
|
|
4d919a2bfe | ||
|
|
6701f467b7 | ||
|
|
50f5da5554 |
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<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>
|
||||
<Version>0.17.1.0</Version>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks>
|
||||
<Version>0.17.1.0</Version>
|
||||
<Copyright>Copyright © Perfare 2018-2022</Copyright>
|
||||
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2023</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
|
||||
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.5" />
|
||||
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace AssetStudio
|
||||
{
|
||||
public string SpecifyUnityVersion;
|
||||
public List<SerializedFile> assetsFileList = new List<SerializedFile>();
|
||||
private List<ClassIDType> filteredAssetTypesList = new List<ClassIDType>();
|
||||
private HashSet<ClassIDType> filteredAssetTypesList = new HashSet<ClassIDType>();
|
||||
|
||||
internal Dictionary<string, int> assetsFileIndexCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
internal Dictionary<string, BinaryReader> resourceFileReaders = new Dictionary<string, BinaryReader>(StringComparer.OrdinalIgnoreCase);
|
||||
@@ -22,35 +22,33 @@ namespace AssetStudio
|
||||
private HashSet<string> noexistFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
private HashSet<string> assetsFileListHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public void SetAssetFilter(ClassIDType classIDType)
|
||||
public void SetAssetFilter(params ClassIDType[] classIDTypes)
|
||||
{
|
||||
if (filteredAssetTypesList.Count == 0)
|
||||
{
|
||||
filteredAssetTypesList.AddRange(new List<ClassIDType>
|
||||
filteredAssetTypesList.UnionWith(new HashSet<ClassIDType>
|
||||
{
|
||||
ClassIDType.AssetBundle,
|
||||
ClassIDType.ResourceManager,
|
||||
});
|
||||
}
|
||||
|
||||
if (classIDType == ClassIDType.MonoBehaviour)
|
||||
if (classIDTypes.Contains(ClassIDType.MonoBehaviour))
|
||||
{
|
||||
filteredAssetTypesList.AddRange(new List<ClassIDType>
|
||||
{
|
||||
ClassIDType.MonoScript,
|
||||
ClassIDType.MonoBehaviour
|
||||
});
|
||||
filteredAssetTypesList.Add(ClassIDType.MonoScript);
|
||||
}
|
||||
else
|
||||
if (classIDTypes.Contains(ClassIDType.Sprite))
|
||||
{
|
||||
filteredAssetTypesList.Add(classIDType);
|
||||
filteredAssetTypesList.Add(ClassIDType.Texture2D);
|
||||
filteredAssetTypesList.Add(ClassIDType.SpriteAtlas);
|
||||
}
|
||||
|
||||
filteredAssetTypesList.UnionWith(classIDTypes);
|
||||
}
|
||||
|
||||
public void SetAssetFilter(List<ClassIDType> classIDTypeList)
|
||||
{
|
||||
foreach (ClassIDType classIDType in classIDTypeList)
|
||||
SetAssetFilter(classIDType);
|
||||
SetAssetFilter(classIDTypeList.ToArray());
|
||||
}
|
||||
|
||||
public void LoadFilesAndFolders(params string[] path)
|
||||
@@ -85,7 +83,7 @@ namespace AssetStudio
|
||||
MergeSplitAssets(fullPath, true);
|
||||
fileList.AddRange(Directory.GetFiles(fullPath, "*.*", SearchOption.AllDirectories));
|
||||
}
|
||||
else
|
||||
else if (File.Exists(fullPath))
|
||||
{
|
||||
parentPath = Path.GetDirectoryName(fullPath);
|
||||
fileList.Add(fullPath);
|
||||
@@ -136,7 +134,7 @@ namespace AssetStudio
|
||||
|
||||
private void LoadFile(FileReader reader)
|
||||
{
|
||||
switch (reader.FileType)
|
||||
switch (reader?.FileType)
|
||||
{
|
||||
case FileType.AssetsFile:
|
||||
LoadAssetsFile(reader);
|
||||
@@ -535,6 +533,9 @@ namespace AssetStudio
|
||||
case ClassIDType.PlayerSettings:
|
||||
obj = new PlayerSettings(objectReader);
|
||||
break;
|
||||
case ClassIDType.PreloadData:
|
||||
obj = new PreloadData(objectReader);
|
||||
break;
|
||||
case ClassIDType.RectTransform:
|
||||
obj = new RectTransform(objectReader);
|
||||
break;
|
||||
@@ -638,14 +639,17 @@ namespace AssetStudio
|
||||
{
|
||||
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)
|
||||
{
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace AssetStudio
|
||||
public T inWeight;
|
||||
public T outWeight;
|
||||
|
||||
|
||||
public Keyframe(ObjectReader reader, Func<T> readerFunc)
|
||||
{
|
||||
time = reader.ReadSingle();
|
||||
@@ -294,15 +293,20 @@ namespace AssetStudio
|
||||
public string path;
|
||||
public ClassIDType classID;
|
||||
public PPtr<MonoScript> script;
|
||||
|
||||
public int flags;
|
||||
|
||||
public FloatCurve(ObjectReader reader)
|
||||
{
|
||||
var version = reader.version;
|
||||
curve = new AnimationCurve<float>(reader, reader.ReadSingle);
|
||||
attribute = reader.ReadAlignedString();
|
||||
path = reader.ReadAlignedString();
|
||||
classID = (ClassIDType)reader.ReadInt32();
|
||||
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 PPtr<Object> value;
|
||||
|
||||
|
||||
public PPtrKeyframe(ObjectReader reader)
|
||||
{
|
||||
time = reader.ReadSingle();
|
||||
@@ -326,10 +329,11 @@ namespace AssetStudio
|
||||
public string path;
|
||||
public int classID;
|
||||
public PPtr<MonoScript> script;
|
||||
|
||||
public int flags;
|
||||
|
||||
public PPtrCurve(ObjectReader reader)
|
||||
{
|
||||
var version = reader.version;
|
||||
int numCurves = reader.ReadInt32();
|
||||
curve = new PPtrKeyframe[numCurves];
|
||||
for (int i = 0; i < numCurves; i++)
|
||||
@@ -341,6 +345,10 @@ namespace AssetStudio
|
||||
path = reader.ReadAlignedString();
|
||||
classID = reader.ReadInt32();
|
||||
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 AnimationEvent[] m_Events;
|
||||
|
||||
|
||||
public AnimationClip(ObjectReader reader) : base(reader)
|
||||
{
|
||||
if (version[0] >= 5)//5.0 and up
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -23,22 +20,47 @@ namespace AssetStudio
|
||||
{
|
||||
public PPtr<Object>[] m_PreloadTable;
|
||||
public KeyValuePair<string, AssetInfo>[] m_Container;
|
||||
public string m_AssetBundleName;
|
||||
public string[] m_Dependencies;
|
||||
public bool m_IsStreamedSceneAssetBundle;
|
||||
|
||||
public AssetBundle(ObjectReader reader) : base(reader)
|
||||
{
|
||||
var m_PreloadTableSize = reader.ReadInt32();
|
||||
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);
|
||||
}
|
||||
|
||||
var m_ContainerSize = reader.ReadInt32();
|
||||
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));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
AssetStudio/Classes/PreloadData.cs
Normal file
35
AssetStudio/Classes/PreloadData.cs
Normal 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();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace AssetStudio
|
||||
public SecondarySpriteTexture(ObjectReader reader)
|
||||
{
|
||||
texture = new PPtr<Texture2D>(reader);
|
||||
name = reader.ReadStringToNull();
|
||||
name = reader.ReadAlignedString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,14 @@ namespace AssetStudio
|
||||
}
|
||||
if (version[0] > 2019 || (version[0] == 2019 && version[1] >= 3)) //2019.3 and up
|
||||
{
|
||||
var m_IgnoreMasterTextureLimit = reader.ReadBoolean();
|
||||
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 2)) //2022.2 and up
|
||||
{
|
||||
var m_IgnoreMipmapLimit = reader.ReadBoolean();
|
||||
}
|
||||
else
|
||||
{
|
||||
var m_IgnoreMasterTextureLimit = reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
if (version[0] >= 3) //3.0.0 - 5.4
|
||||
{
|
||||
@@ -100,6 +107,11 @@ namespace AssetStudio
|
||||
var m_ReadAllowed = reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 2)) //2022.2 and up
|
||||
{
|
||||
var m_MipmapLimitGroupName = reader.ReadAlignedString();
|
||||
reader.AlignStream();
|
||||
}
|
||||
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
|
||||
{
|
||||
var m_StreamingMipmaps = reader.ReadBoolean();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace AssetStudioCLI
|
||||
namespace AssetStudio
|
||||
{
|
||||
// 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)
|
||||
public static class CLIAnsiColors
|
||||
public static class ColorConsole
|
||||
{
|
||||
public static readonly string
|
||||
Black = "\u001b[30m",
|
||||
@@ -27,7 +27,7 @@ namespace AssetStudioCLI
|
||||
|
||||
public static string Color(this string str, string ansiColor)
|
||||
{
|
||||
if (!CLIWinAnsiFix.isAnsiSupported)
|
||||
if (!ColorConsoleHelper.isAnsiCodesSupported)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
@@ -35,10 +35,10 @@ namespace AssetStudioCLI
|
||||
return $"{ansiColor}{str}{Reset}";
|
||||
}
|
||||
|
||||
public static void ANSICodesTest()
|
||||
public static void AnsiCodesTest()
|
||||
{
|
||||
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[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");
|
||||
@@ -2,11 +2,11 @@
|
||||
using System;
|
||||
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 uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
|
||||
|
||||
@@ -19,21 +19,21 @@ namespace AssetStudioCLI
|
||||
[DllImport("kernel32.dll")]
|
||||
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)
|
||||
{
|
||||
isAnsiSupported = TryEnableVTMode();
|
||||
if (!isAnsiSupported)
|
||||
isAnsiCodesSupported = TryEnableVTMode();
|
||||
if (!isAnsiCodesSupported)
|
||||
{
|
||||
//Check for bash terminal emulator. E.g., Git Bash, Cmder
|
||||
isAnsiSupported = Environment.GetEnvironmentVariable("TERM") != null;
|
||||
isAnsiCodesSupported = Environment.GetEnvironmentVariable("TERM") != null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isAnsiSupported = true;
|
||||
isAnsiCodesSupported = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,12 +51,7 @@ namespace AssetStudioCLI
|
||||
|
||||
outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
|
||||
if (!SetConsoleMode(iStdOut, outConsoleMode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return SetConsoleMode(iStdOut, outConsoleMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,10 +35,13 @@ namespace AssetStudio
|
||||
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>();
|
||||
int count = 0;
|
||||
var count = 0;
|
||||
while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength)
|
||||
{
|
||||
var b = reader.ReadByte();
|
||||
@@ -49,7 +52,24 @@ namespace AssetStudio
|
||||
bytes.Add(b);
|
||||
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)
|
||||
|
||||
@@ -53,15 +53,24 @@ namespace AssetStudio
|
||||
|
||||
public static FileReader DecompressGZip(FileReader reader)
|
||||
{
|
||||
using (reader)
|
||||
try
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
using (var gs = new GZipStream(reader.BaseStream, CompressionMode.Decompress))
|
||||
using (reader)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks>
|
||||
<TargetFrameworks>net472;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle>
|
||||
<AssemblyName>AssetStudioModCLI</AssemblyName>
|
||||
<Version>0.17.1.0</Version>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Copyright>Copyright © Perfare; Copyright © aelurum 2023</Copyright>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>embedded</DebugType>
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
<Target Name="CopyExtraFilesPortable" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' ">
|
||||
<Message Text="Copying windows extra files for $(TargetFramework)... " Importance="high" />
|
||||
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\Win32\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\x64\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\win-x86\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
|
||||
@@ -32,6 +34,9 @@
|
||||
|
||||
<Target Name="CopyExtraFilesPortableNet" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' AND '$(TargetFramework)' != 'net472' ">
|
||||
<Message Text="Copying other platforms extra files for $(TargetFramework)... " Importance="high" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libAssetStudioFBXNative.so" DestinationFolder="$(TargetDir)runtimes\linux-x64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\osx-x64\libAssetStudioFBXNative.dylib" DestinationFolder="$(TargetDir)runtimes\osx-x64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\osx-arm64\libAssetStudioFBXNative.dylib" DestinationFolder="$(TargetDir)runtimes\osx-arm64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x86\libfmod.so" DestinationFolder="$(TargetDir)runtimes\linux-x86\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libfmod.so" DestinationFolder="$(TargetDir)runtimes\linux-x64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\osx-x64\libfmod.dylib" DestinationFolder="$(TargetDir)runtimes\osx-x64\native" ContinueOnError="false" />
|
||||
@@ -42,6 +47,8 @@
|
||||
The dll is cross-platform while the executable isn't -->
|
||||
<Target Name="PublishExtraFilesPortable" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' ">
|
||||
<Message Text="Publishing windows extra files for Portable build ($(TargetFramework))... " Importance="high" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\fmod.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
|
||||
@@ -50,6 +57,9 @@
|
||||
|
||||
<Target Name="PublishExtraFilesPortableNet" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' AND '$(TargetFramework)' != 'net472' ">
|
||||
<Message Text="Publishing other platforms extra files for Portable build ($(TargetFramework))... " Importance="high" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\linux-x64\native\libAssetStudioFBXNative.so" DestinationFolder="$(PublishDir)runtimes\linux-x64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\osx-x64\native\libAssetStudioFBXNative.dylib" DestinationFolder="$(PublishDir)runtimes\osx-x64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\osx-arm64\native\libAssetStudioFBXNative.dylib" DestinationFolder="$(PublishDir)runtimes\osx-arm64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\linux-x86\native\libfmod.so" DestinationFolder="$(PublishDir)runtimes\linux-x86\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\linux-x64\native\libfmod.so" DestinationFolder="$(PublishDir)runtimes\linux-x64\native" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)runtimes\osx-x64\native\libfmod.dylib" DestinationFolder="$(PublishDir)runtimes\osx-x64\native" ContinueOnError="false" />
|
||||
@@ -58,39 +68,46 @@
|
||||
|
||||
<Target Name="CopyExtraFilesWin86" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x86' ">
|
||||
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
|
||||
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\Win32\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\win-x86\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CopyExtraFilesWin64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x64' ">
|
||||
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
|
||||
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\x64\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\win-x64\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
|
||||
</Target>
|
||||
|
||||
<Target Name="PublishExtraFilesWin" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('win-x')) ">
|
||||
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
|
||||
<Copy SourceFiles="$(TargetDir)\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)\fmod.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CopyExtraFilesLinux64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' ">
|
||||
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libAssetStudioFBXNative.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libfmod.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
|
||||
</Target>
|
||||
|
||||
<Target Name="PublishExtraFilesLinux64" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' ">
|
||||
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
|
||||
<Copy SourceFiles="$(TargetDir)\libAssetStudioFBXNative.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)\libfmod.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CopyExtraFilesMac" AfterTargets="AfterBuild" Condition=" $(RuntimeIdentifier.Contains('osx-')) ">
|
||||
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\$(RuntimeIdentifier)\libAssetStudioFBXNative.dylib" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(ProjectDir)Libraries\$(RuntimeIdentifier)\libfmod.dylib" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
|
||||
</Target>
|
||||
|
||||
<Target Name="PublishExtraFilesMac" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('osx-')) ">
|
||||
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
|
||||
<Copy SourceFiles="$(TargetDir)\libAssetStudioFBXNative.dylib" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
|
||||
<Copy SourceFiles="$(TargetDir)\libfmod.dylib" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
|
||||
</Target>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using AssetStudio;
|
||||
using AssetStudioCLI.Options;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using AssetStudioCLI.Options;
|
||||
|
||||
namespace AssetStudioCLI
|
||||
{
|
||||
@@ -21,29 +21,31 @@ namespace AssetStudioCLI
|
||||
public string LogName;
|
||||
public string LogPath;
|
||||
|
||||
public CLILogger(CLIOptions options)
|
||||
public CLILogger()
|
||||
{
|
||||
logOutput = options.o_logOutput.Value;
|
||||
logMinLevel = options.o_logLevel.Value;
|
||||
LogName = $"AssetStudioCLI_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log";
|
||||
logOutput = CLIOptions.o_logOutput.Value;
|
||||
logMinLevel = CLIOptions.o_logLevel.Value;
|
||||
var appAssembly = typeof(Program).Assembly.GetName();
|
||||
LogName = $"{appAssembly.Name}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log";
|
||||
LogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogName);
|
||||
var arch = Environment.Is64BitProcess ? "x64" : "x32";
|
||||
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
||||
|
||||
var ver = typeof(Program).Assembly.GetName().Version;
|
||||
LogToFile(LoggerEvent.Verbose, $"---AssetStudioCLI v{ver} | Logger launched---\n" +
|
||||
$"CMD Args: {string.Join(" ", options.cliArgs)}");
|
||||
LogToFile(LoggerEvent.Verbose, $"---{appAssembly.Name} v{appAssembly.Version} [{arch}] | Logger launched---\n" +
|
||||
$"CMD Args: {string.Join(" ", CLIOptions.cliArgs)}");
|
||||
}
|
||||
|
||||
private static string ColorLogLevel(LoggerEvent logLevel)
|
||||
{
|
||||
string formattedLevel = $"[{logLevel}]";
|
||||
var formattedLevel = $"[{logLevel}]";
|
||||
switch (logLevel)
|
||||
{
|
||||
case LoggerEvent.Info:
|
||||
return $"{formattedLevel.Color(CLIAnsiColors.BrightCyan)}";
|
||||
return $"{formattedLevel.Color(ColorConsole.BrightCyan)}";
|
||||
case LoggerEvent.Warning:
|
||||
return $"{formattedLevel.Color(CLIAnsiColors.BrightYellow)}";
|
||||
return $"{formattedLevel.Color(ColorConsole.BrightYellow)}";
|
||||
case LoggerEvent.Error:
|
||||
return $"{formattedLevel.Color(CLIAnsiColors.BrightRed)}";
|
||||
return $"{formattedLevel.Color(ColorConsole.BrightRed)}";
|
||||
default:
|
||||
return formattedLevel;
|
||||
}
|
||||
@@ -58,7 +60,7 @@ namespace AssetStudioCLI
|
||||
string formattedMessage;
|
||||
if (consoleMode)
|
||||
{
|
||||
string colorLogLevel = ColorLogLevel(logMsgLevel);
|
||||
var colorLogLevel = ColorLogLevel(logMsgLevel);
|
||||
formattedMessage = $"{colorLogLevel} {message}";
|
||||
if (multiLine)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace AssetStudioCLI
|
||||
public ClassIDType Type;
|
||||
public string Text;
|
||||
public string UniqueID;
|
||||
public GameObjectNode Node;
|
||||
|
||||
public AssetItem(Object asset)
|
||||
{
|
||||
|
||||
16
AssetStudioCLI/Components/BaseNode.cs
Normal file
16
AssetStudioCLI/Components/BaseNode.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using AssetStudio;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AssetStudioCLI
|
||||
{
|
||||
internal class BaseNode
|
||||
{
|
||||
public List<BaseNode> nodes = new List<BaseNode>();
|
||||
|
||||
public BaseNode()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
16
AssetStudioCLI/Components/GameObjectNode.cs
Normal file
16
AssetStudioCLI/Components/GameObjectNode.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using AssetStudio;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AssetStudioCLI
|
||||
{
|
||||
internal class GameObjectNode : BaseNode
|
||||
{
|
||||
public GameObject gameObject;
|
||||
|
||||
public GameObjectNode(GameObject gameObject)
|
||||
{
|
||||
this.gameObject = gameObject;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using AssetStudio;
|
||||
using AssetStudioCLI.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -9,14 +10,38 @@ namespace AssetStudioCLI
|
||||
{
|
||||
internal static class Exporter
|
||||
{
|
||||
public static bool ExportTexture2D(AssetItem item, string exportPath, CLIOptions options)
|
||||
public static bool ExportTexture2D(AssetItem item, string exportPath)
|
||||
{
|
||||
var m_Texture2D = (Texture2D)item.Asset;
|
||||
if (options.convertTexture)
|
||||
if (CLIOptions.convertTexture)
|
||||
{
|
||||
var type = options.o_imageFormat.Value;
|
||||
var type = CLIOptions.o_imageFormat.Value;
|
||||
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
|
||||
return false;
|
||||
|
||||
if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"Converting \"{m_Texture2D.m_Name}\" to {type}..");
|
||||
sb.AppendLine($"Width: {m_Texture2D.m_Width}");
|
||||
sb.AppendLine($"Height: {m_Texture2D.m_Height}");
|
||||
sb.AppendLine($"Format: {m_Texture2D.m_TextureFormat}");
|
||||
switch (m_Texture2D.m_TextureSettings.m_FilterMode)
|
||||
{
|
||||
case 0: sb.AppendLine("Filter Mode: Point "); break;
|
||||
case 1: sb.AppendLine("Filter Mode: Bilinear "); break;
|
||||
case 2: sb.AppendLine("Filter Mode: Trilinear "); break;
|
||||
}
|
||||
sb.AppendLine($"Anisotropic level: {m_Texture2D.m_TextureSettings.m_Aniso}");
|
||||
sb.AppendLine($"Mip map bias: {m_Texture2D.m_TextureSettings.m_MipBias}");
|
||||
switch (m_Texture2D.m_TextureSettings.m_WrapMode)
|
||||
{
|
||||
case 0: sb.AppendLine($"Wrap mode: Repeat"); break;
|
||||
case 1: sb.AppendLine($"Wrap mode: Clamp"); break;
|
||||
}
|
||||
Logger.Debug(sb.ToString());
|
||||
}
|
||||
|
||||
var image = m_Texture2D.ConvertToImage(flip: true);
|
||||
if (image == null)
|
||||
{
|
||||
@@ -29,7 +54,7 @@ namespace AssetStudioCLI
|
||||
{
|
||||
image.WriteToStream(file, type);
|
||||
}
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -38,12 +63,12 @@ namespace AssetStudioCLI
|
||||
if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData());
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ExportAudioClip(AssetItem item, string exportPath, CLIOptions options)
|
||||
public static bool ExportAudioClip(AssetItem item, string exportPath)
|
||||
{
|
||||
string exportFullPath;
|
||||
var m_AudioClip = (AudioClip)item.Asset;
|
||||
@@ -54,18 +79,21 @@ namespace AssetStudioCLI
|
||||
return false;
|
||||
}
|
||||
var converter = new AudioClipConverter(m_AudioClip);
|
||||
if (options.o_audioFormat.Value != AudioFormat.None && converter.IsSupport)
|
||||
if (CLIOptions.o_audioFormat.Value != AudioFormat.None && converter.IsSupport)
|
||||
{
|
||||
if (!TryExportFile(exportPath, item, ".wav", out exportFullPath))
|
||||
return false;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"Converting \"{m_AudioClip.m_Name}\" to wav..");
|
||||
sb.AppendLine(m_AudioClip.version[0] < 5 ? $"AudioClip type: {m_AudioClip.m_Type}" : $"AudioClip compression format: {m_AudioClip.m_CompressionFormat}");
|
||||
sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}");
|
||||
sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}");
|
||||
sb.AppendLine($"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}");
|
||||
Logger.Debug(sb.ToString());
|
||||
if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"Converting \"{m_AudioClip.m_Name}\" to wav..");
|
||||
sb.AppendLine(m_AudioClip.version[0] < 5 ? $"AudioClip type: {m_AudioClip.m_Type}" : $"AudioClip compression format: {m_AudioClip.m_CompressionFormat}");
|
||||
sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}");
|
||||
sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}");
|
||||
sb.AppendLine($"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}");
|
||||
Logger.Debug(sb.ToString());
|
||||
}
|
||||
|
||||
var buffer = converter.ConvertToWav(m_AudioData);
|
||||
if (buffer == null)
|
||||
@@ -82,7 +110,7 @@ namespace AssetStudioCLI
|
||||
File.WriteAllBytes(exportFullPath, m_AudioData);
|
||||
}
|
||||
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -94,16 +122,19 @@ namespace AssetStudioCLI
|
||||
if (!TryExportFile(exportPath, item, Path.GetExtension(m_VideoClip.m_OriginalPath), out var exportFullPath))
|
||||
return false;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"VideoClip format: {m_VideoClip.m_Format}");
|
||||
sb.AppendLine($"VideoClip width: {m_VideoClip.Width}");
|
||||
sb.AppendLine($"VideoClip height: {m_VideoClip.Height}");
|
||||
sb.AppendLine($"VideoClip frame rate: {m_VideoClip.m_FrameRate}");
|
||||
sb.AppendLine($"VideoClip split alpha: {m_VideoClip.m_HasSplitAlpha}");
|
||||
Logger.Debug(sb.ToString());
|
||||
if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"VideoClip format: {m_VideoClip.m_Format}");
|
||||
sb.AppendLine($"VideoClip width: {m_VideoClip.Width}");
|
||||
sb.AppendLine($"VideoClip height: {m_VideoClip.Height}");
|
||||
sb.AppendLine($"VideoClip frame rate: {m_VideoClip.m_FrameRate:.0##}");
|
||||
sb.AppendLine($"VideoClip split alpha: {m_VideoClip.m_HasSplitAlpha}");
|
||||
Logger.Debug(sb.ToString());
|
||||
}
|
||||
|
||||
m_VideoClip.m_VideoData.WriteData(exportFullPath);
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -116,7 +147,7 @@ namespace AssetStudioCLI
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullPath, m_MovieTexture.m_MovieData);
|
||||
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -128,16 +159,16 @@ namespace AssetStudioCLI
|
||||
var str = m_Shader.Convert();
|
||||
File.WriteAllText(exportFullPath, str);
|
||||
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExportTextAsset(AssetItem item, string exportPath, CLIOptions options)
|
||||
public static bool ExportTextAsset(AssetItem item, string exportPath)
|
||||
{
|
||||
var m_TextAsset = (TextAsset)item.Asset;
|
||||
var extension = ".txt";
|
||||
var assetExtension = Path.GetExtension(m_TextAsset.m_Name);
|
||||
if (!options.f_notRestoreExtensionName.Value)
|
||||
if (!CLIOptions.f_notRestoreExtensionName.Value)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(assetExtension))
|
||||
{
|
||||
@@ -156,11 +187,11 @@ namespace AssetStudioCLI
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullPath, m_TextAsset.m_Script);
|
||||
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExportMonoBehaviour(AssetItem item, string exportPath, AssemblyLoader assemblyLoader)
|
||||
public static bool ExportMonoBehaviour(AssetItem item, string exportPath)
|
||||
{
|
||||
if (!TryExportFile(exportPath, item, ".json", out var exportFullPath))
|
||||
return false;
|
||||
@@ -168,7 +199,7 @@ namespace AssetStudioCLI
|
||||
var type = m_MonoBehaviour.ToType();
|
||||
if (type == null)
|
||||
{
|
||||
var m_Type = m_MonoBehaviour.ConvertToTypeTree(assemblyLoader);
|
||||
var m_Type = m_MonoBehaviour.ConvertToTypeTree(Studio.assemblyLoader);
|
||||
type = m_MonoBehaviour.ToType(m_Type);
|
||||
}
|
||||
if (type != null)
|
||||
@@ -176,7 +207,7 @@ namespace AssetStudioCLI
|
||||
var str = JsonConvert.SerializeObject(type, Formatting.Indented);
|
||||
File.WriteAllText(exportFullPath, str);
|
||||
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -196,15 +227,15 @@ namespace AssetStudioCLI
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullPath, m_Font.m_FontData);
|
||||
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ExportSprite(AssetItem item, string exportPath, CLIOptions options)
|
||||
public static bool ExportSprite(AssetItem item, string exportPath)
|
||||
{
|
||||
var type = options.o_imageFormat.Value;
|
||||
var type = CLIOptions.o_imageFormat.Value;
|
||||
var alphaMask = SpriteMaskMode.On;
|
||||
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
|
||||
return false;
|
||||
@@ -217,7 +248,7 @@ namespace AssetStudioCLI
|
||||
{
|
||||
image.WriteToStream(file, type);
|
||||
}
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -230,24 +261,52 @@ namespace AssetStudioCLI
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
|
||||
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExportDumpFile(AssetItem item, string exportPath, AssemblyLoader assemblyLoader)
|
||||
public static void ExportGameObject(GameObject gameObject, string exportPath, List<AssetItem> animationList = null)
|
||||
{
|
||||
var convert = animationList != null
|
||||
? new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value, animationList.Select(x => (AnimationClip)x.Asset).ToArray())
|
||||
: new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value);
|
||||
exportPath = exportPath + FixFileName(gameObject.m_Name) + ".fbx";
|
||||
ExportFbx(convert, exportPath);
|
||||
}
|
||||
|
||||
private static void ExportFbx(IImported convert, string exportPath)
|
||||
{
|
||||
var eulerFilter = true;
|
||||
var filterPrecision = (float)0.25f;
|
||||
var exportAllNodes = true;
|
||||
var exportSkins = true;
|
||||
var exportAnimations = true;
|
||||
var exportBlendShape = true;
|
||||
var castToBone = false;
|
||||
var boneSize = CLIOptions.o_fbxBoneSize.Value;
|
||||
var exportAllUvsAsDiffuseMaps = false;
|
||||
var scaleFactor = CLIOptions.o_fbxScaleFactor.Value;
|
||||
var fbxVersion = 3;
|
||||
var fbxFormat = 0;
|
||||
ModelExporter.ExportFbx(exportPath, convert, eulerFilter, filterPrecision,
|
||||
exportAllNodes, exportSkins, exportAnimations, exportBlendShape, castToBone, boneSize, exportAllUvsAsDiffuseMaps, scaleFactor, fbxVersion, fbxFormat == 1);
|
||||
}
|
||||
|
||||
|
||||
public static bool ExportDumpFile(AssetItem item, string exportPath)
|
||||
{
|
||||
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath))
|
||||
return false;
|
||||
var str = item.Asset.Dump();
|
||||
if (str == null && item.Asset is MonoBehaviour m_MonoBehaviour)
|
||||
{
|
||||
var m_Type = m_MonoBehaviour.ConvertToTypeTree(assemblyLoader);
|
||||
var m_Type = m_MonoBehaviour.ConvertToTypeTree(Studio.assemblyLoader);
|
||||
str = m_MonoBehaviour.Dump(m_Type);
|
||||
}
|
||||
if (str != null)
|
||||
{
|
||||
File.WriteAllText(exportFullPath, str);
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" saved to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" saved to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -268,7 +327,7 @@ namespace AssetStudioCLI
|
||||
Directory.CreateDirectory(dir);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -365,18 +424,18 @@ namespace AssetStudioCLI
|
||||
|
||||
sb.Replace("NaN", "0");
|
||||
File.WriteAllText(exportFullPath, sb.ToString());
|
||||
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ExportConvertFile(AssetItem item, string exportPath, CLIOptions options, AssemblyLoader assemblyLoader)
|
||||
public static bool ExportConvertFile(AssetItem item, string exportPath)
|
||||
{
|
||||
switch (item.Type)
|
||||
{
|
||||
case ClassIDType.Texture2D:
|
||||
return ExportTexture2D(item, exportPath, options);
|
||||
return ExportTexture2D(item, exportPath);
|
||||
case ClassIDType.AudioClip:
|
||||
return ExportAudioClip(item, exportPath, options);
|
||||
return ExportAudioClip(item, exportPath);
|
||||
case ClassIDType.VideoClip:
|
||||
return ExportVideoClip(item, exportPath);
|
||||
case ClassIDType.MovieTexture:
|
||||
@@ -384,13 +443,13 @@ namespace AssetStudioCLI
|
||||
case ClassIDType.Shader:
|
||||
return ExportShader(item, exportPath);
|
||||
case ClassIDType.TextAsset:
|
||||
return ExportTextAsset(item, exportPath, options);
|
||||
return ExportTextAsset(item, exportPath);
|
||||
case ClassIDType.MonoBehaviour:
|
||||
return ExportMonoBehaviour(item, exportPath, assemblyLoader);
|
||||
return ExportMonoBehaviour(item, exportPath);
|
||||
case ClassIDType.Font:
|
||||
return ExportFont(item, exportPath);
|
||||
case ClassIDType.Sprite:
|
||||
return ExportSprite(item, exportPath, options);
|
||||
return ExportSprite(item, exportPath);
|
||||
case ClassIDType.Mesh:
|
||||
return ExportMesh(item, exportPath);
|
||||
default:
|
||||
|
||||
BIN
AssetStudioCLI/Libraries/linux-x64/libAssetStudioFBXNative.so
Normal file
BIN
AssetStudioCLI/Libraries/linux-x64/libAssetStudioFBXNative.so
Normal file
Binary file not shown.
BIN
AssetStudioCLI/Libraries/osx-arm64/libAssetStudioFBXNative.dylib
Normal file
BIN
AssetStudioCLI/Libraries/osx-arm64/libAssetStudioFBXNative.dylib
Normal file
Binary file not shown.
BIN
AssetStudioCLI/Libraries/osx-x64/libAssetStudioFBXNative.dylib
Normal file
BIN
AssetStudioCLI/Libraries/osx-x64/libAssetStudioFBXNative.dylib
Normal file
Binary file not shown.
@@ -12,6 +12,9 @@ namespace AssetStudioCLI.Options
|
||||
General,
|
||||
Convert,
|
||||
Logger,
|
||||
Live2D,
|
||||
FBX,
|
||||
Filter,
|
||||
Advanced,
|
||||
}
|
||||
|
||||
@@ -22,6 +25,7 @@ namespace AssetStudioCLI.Options
|
||||
Dump,
|
||||
Info,
|
||||
ExportLive2D,
|
||||
SplitObjects,
|
||||
}
|
||||
|
||||
internal enum AssetGroupOption
|
||||
@@ -55,65 +59,93 @@ namespace AssetStudioCLI.Options
|
||||
NameAndContainer,
|
||||
}
|
||||
|
||||
internal class GroupedOption<T> : Option<T>
|
||||
internal static class CLIOptions
|
||||
{
|
||||
public GroupedOption(T optionDefaultValue, string optionName, string optionDescription, HelpGroups optionHelpGroup, bool isFlag = false) : base(optionDefaultValue, optionName, optionDescription, optionHelpGroup, isFlag)
|
||||
{
|
||||
CLIOptions.OptionGrouping(optionName, optionDescription, optionHelpGroup, isFlag);
|
||||
}
|
||||
}
|
||||
|
||||
internal class CLIOptions
|
||||
{
|
||||
public bool isParsed;
|
||||
public bool showHelp;
|
||||
public string[] cliArgs;
|
||||
public string inputPath;
|
||||
public FilterBy filterBy;
|
||||
public static bool isParsed;
|
||||
public static bool showHelp;
|
||||
public static string[] cliArgs;
|
||||
public static string inputPath;
|
||||
public static FilterBy filterBy;
|
||||
private static Dictionary<string, string> optionsDict;
|
||||
private static Dictionary<string, string> flagsDict;
|
||||
private static Dictionary<HelpGroups, Dictionary<string, string>> optionGroups;
|
||||
private List<ClassIDType> supportedAssetTypes;
|
||||
private static List<ClassIDType> exportableAssetTypes;
|
||||
private static Dictionary<string, ClassIDType> knownAssetTypesDict;
|
||||
//general
|
||||
public Option<WorkMode> o_workMode;
|
||||
public Option<List<ClassIDType>> o_exportAssetTypes;
|
||||
public Option<AssetGroupOption> o_groupAssetsBy;
|
||||
public Option<string> o_outputFolder;
|
||||
public Option<bool> o_displayHelp;
|
||||
public static Option<WorkMode> o_workMode;
|
||||
public static Option<List<ClassIDType>> o_exportAssetTypes;
|
||||
public static Option<AssetGroupOption> o_groupAssetsBy;
|
||||
public static Option<string> o_outputFolder;
|
||||
public static Option<bool> o_displayHelp;
|
||||
//logger
|
||||
public Option<LoggerEvent> o_logLevel;
|
||||
public Option<LogOutputMode> o_logOutput;
|
||||
public static Option<LoggerEvent> o_logLevel;
|
||||
public static Option<LogOutputMode> o_logOutput;
|
||||
//convert
|
||||
public bool convertTexture;
|
||||
public Option<ImageFormat> o_imageFormat;
|
||||
public Option<AudioFormat> o_audioFormat;
|
||||
public static bool convertTexture;
|
||||
public static Option<ImageFormat> o_imageFormat;
|
||||
public static Option<AudioFormat> o_audioFormat;
|
||||
//live2d
|
||||
public static Option<CubismLive2DExtractor.Live2DMotionMode> o_l2dMotionMode;
|
||||
public static Option<bool> f_l2dForceBezier;
|
||||
//fbx
|
||||
public static Option<float> o_fbxScaleFactor;
|
||||
public static Option<int> o_fbxBoneSize;
|
||||
//filter
|
||||
public static Option<List<string>> o_filterByName;
|
||||
public static Option<List<string>> o_filterByContainer;
|
||||
public static Option<List<string>> o_filterByPathID;
|
||||
public static Option<List<string>> o_filterByText;
|
||||
//advanced
|
||||
public Option<ExportListType> o_exportAssetList;
|
||||
public Option<List<string>> o_filterByName;
|
||||
public Option<List<string>> o_filterByContainer;
|
||||
public Option<List<string>> o_filterByPathID;
|
||||
public Option<List<string>> o_filterByText;
|
||||
public Option<string> o_assemblyPath;
|
||||
public Option<string> o_unityVersion;
|
||||
public Option<bool> f_notRestoreExtensionName;
|
||||
public static Option<ExportListType> o_exportAssetList;
|
||||
public static Option<string> o_assemblyPath;
|
||||
public static Option<string> o_unityVersion;
|
||||
public static Option<bool> f_notRestoreExtensionName;
|
||||
public static Option<bool> f_loadAllAssets;
|
||||
|
||||
public CLIOptions(string[] args)
|
||||
static CLIOptions()
|
||||
{
|
||||
cliArgs = args;
|
||||
OptionExtensions.OptionGrouping = OptionGrouping;
|
||||
InitOptions();
|
||||
ParseArgs(args);
|
||||
}
|
||||
|
||||
private void InitOptions()
|
||||
private static void OptionGrouping(string name, string desc, HelpGroups group, bool isFlag)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var optionDict = new Dictionary<string, string>() { { name, desc } };
|
||||
if (!optionGroups.ContainsKey(group))
|
||||
{
|
||||
optionGroups.Add(group, optionDict);
|
||||
}
|
||||
else
|
||||
{
|
||||
optionGroups[group].Add(name, desc);
|
||||
}
|
||||
|
||||
if (isFlag)
|
||||
{
|
||||
flagsDict.Add(name, desc);
|
||||
}
|
||||
else
|
||||
{
|
||||
optionsDict.Add(name, desc);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitOptions()
|
||||
{
|
||||
isParsed = false;
|
||||
showHelp = false;
|
||||
cliArgs = null;
|
||||
inputPath = "";
|
||||
filterBy = FilterBy.None;
|
||||
optionsDict = new Dictionary<string, string>();
|
||||
flagsDict = new Dictionary<string, string>();
|
||||
optionGroups = new Dictionary<HelpGroups, Dictionary<string, string>>();
|
||||
supportedAssetTypes = new List<ClassIDType>
|
||||
exportableAssetTypes = new List<ClassIDType>
|
||||
{
|
||||
ClassIDType.Texture2D,
|
||||
ClassIDType.Sprite,
|
||||
@@ -126,6 +158,7 @@ namespace AssetStudioCLI.Options
|
||||
ClassIDType.MovieTexture,
|
||||
ClassIDType.Mesh,
|
||||
};
|
||||
knownAssetTypesDict = ((ClassIDType[])Enum.GetValues(typeof(ClassIDType))).ToHashSet().ToDictionary(x => x.ToString().ToLower(), y => y);
|
||||
|
||||
#region Init General Options
|
||||
o_workMode = new GroupedOption<WorkMode>
|
||||
@@ -133,18 +166,19 @@ namespace AssetStudioCLI.Options
|
||||
optionDefaultValue: WorkMode.Export,
|
||||
optionName: "-m, --mode <value>",
|
||||
optionDescription: "Specify working mode\n" +
|
||||
"<Value: export(default) | exportRaw | dump | info | live2d>\n" +
|
||||
"<Value: export(default) | exportRaw | dump | info | live2d | splitObjects>\n" +
|
||||
"Export - Exports converted assets\n" +
|
||||
"ExportRaw - Exports raw data\n" +
|
||||
"Dump - Makes asset dumps\n" +
|
||||
"Info - Loads file(s), shows the number of available for export assets and exits\n" +
|
||||
"Live2D - Exports Live2D Cubism 3 models\n" +
|
||||
"SplitObjects - Exports split objects (fbx)\n" +
|
||||
"Example: \"-m info\"\n",
|
||||
optionHelpGroup: HelpGroups.General
|
||||
);
|
||||
o_exportAssetTypes = new GroupedOption<List<ClassIDType>>
|
||||
(
|
||||
optionDefaultValue: supportedAssetTypes,
|
||||
optionDefaultValue: exportableAssetTypes,
|
||||
optionName: "-t, --asset-type <value(s)>",
|
||||
optionDescription: "Specify asset type(s) to export\n" +
|
||||
"<Value(s): tex2d, sprite, textAsset, monoBehaviour, font, shader, movieTexture,\n" +
|
||||
@@ -170,10 +204,10 @@ namespace AssetStudioCLI.Options
|
||||
);
|
||||
o_outputFolder = new GroupedOption<string>
|
||||
(
|
||||
optionDefaultValue: "",
|
||||
optionDefaultValue: "ASExport",
|
||||
optionName: "-o, --output <path>",
|
||||
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
|
||||
);
|
||||
o_displayHelp = new GroupedOption<bool>
|
||||
@@ -230,6 +264,91 @@ namespace AssetStudioCLI.Options
|
||||
);
|
||||
#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
|
||||
o_fbxScaleFactor = new GroupedOption<float>
|
||||
(
|
||||
optionDefaultValue: 1f,
|
||||
optionName: "--fbx-scale-factor <value>",
|
||||
optionDescription: "Specify the FBX Scale Factor\n" +
|
||||
"<Value: float number from 0 to 100 (default=1)\n" +
|
||||
"Example: \"--fbx-scale-factor 50\"\n",
|
||||
optionHelpGroup: HelpGroups.FBX
|
||||
);
|
||||
o_fbxBoneSize = new GroupedOption<int>
|
||||
(
|
||||
optionDefaultValue: 10,
|
||||
optionName: "--fbx-bone-size <value>",
|
||||
optionDescription: "Specify the FBX Bone Size\n" +
|
||||
"<Value: integer number from 0 to 100 (default=10)\n" +
|
||||
"Example: \"--fbx-bone-size 10\"",
|
||||
optionHelpGroup: HelpGroups.FBX
|
||||
);
|
||||
#endregion
|
||||
|
||||
#region Init Filter Options
|
||||
o_filterByName = new GroupedOption<List<string>>
|
||||
(
|
||||
optionDefaultValue: new List<string>(),
|
||||
optionName: "--filter-by-name <text>",
|
||||
optionDescription: "Specify the name by which assets should be filtered\n" +
|
||||
"*To specify multiple names write them separated by ',' or ';' without spaces\n" +
|
||||
"Example: \"--filter-by-name char\" or \"--filter-by-name char,bg\"\n",
|
||||
optionHelpGroup: HelpGroups.Filter
|
||||
);
|
||||
o_filterByContainer = new GroupedOption<List<string>>
|
||||
(
|
||||
optionDefaultValue: new List<string>(),
|
||||
optionName: "--filter-by-container <text>",
|
||||
optionDescription: "Specify the container by which assets should be filtered\n" +
|
||||
"*To specify multiple containers write them separated by ',' or ';' without spaces\n" +
|
||||
"Example: \"--filter-by-container arts\" or \"--filter-by-container arts,icons\"\n",
|
||||
optionHelpGroup: HelpGroups.Filter
|
||||
);
|
||||
o_filterByPathID = new GroupedOption<List<string>>
|
||||
(
|
||||
optionDefaultValue: new List<string>(),
|
||||
optionName: "--filter-by-pathid <text>",
|
||||
optionDescription: "Specify the PathID by which assets should be filtered\n" +
|
||||
"*To specify multiple PathIDs write them separated by ',' or ';' without spaces\n" +
|
||||
"Example: \"--filter-by-pathid 7238605633795851352,-2430306240205277265\"\n",
|
||||
optionHelpGroup: HelpGroups.Filter
|
||||
);
|
||||
o_filterByText = new GroupedOption<List<string>>
|
||||
(
|
||||
optionDefaultValue: new List<string>(),
|
||||
optionName: "--filter-by-text <text>",
|
||||
optionDescription: "Specify the text by which assets should be filtered\n" +
|
||||
"Looks for assets that contain the specified text in their names or containers\n" +
|
||||
"*To specify multiple values write them separated by ',' or ';' without spaces\n" +
|
||||
"Example: \"--filter-by-text portrait\" or \"--filter-by-text portrait,art\"\n",
|
||||
optionHelpGroup: HelpGroups.Filter
|
||||
);
|
||||
#endregion
|
||||
|
||||
#region Init Advanced Options
|
||||
o_exportAssetList = new GroupedOption<ExportListType>
|
||||
(
|
||||
@@ -241,101 +360,47 @@ namespace AssetStudioCLI.Options
|
||||
"Example: \"--export-asset-list xml\"\n",
|
||||
optionHelpGroup: HelpGroups.Advanced
|
||||
);
|
||||
o_filterByName = new GroupedOption<List<string>>
|
||||
(
|
||||
optionDefaultValue: new List<string>(),
|
||||
optionName: "--filter-by-name <text>",
|
||||
optionDescription: "Specify the name by which assets should be filtered\n" +
|
||||
"*To specify multiple names write them separated by ',' or ';' without spaces\n" +
|
||||
"Example: \"--filter-by-name char\" or \"--filter-by-name char,bg\"\n",
|
||||
optionHelpGroup: HelpGroups.Advanced
|
||||
);
|
||||
o_filterByContainer = new GroupedOption<List<string>>
|
||||
(
|
||||
optionDefaultValue: new List<string>(),
|
||||
optionName: "--filter-by-container <text>",
|
||||
optionDescription: "Specify the container by which assets should be filtered\n" +
|
||||
"*To specify multiple containers write them separated by ',' or ';' without spaces\n" +
|
||||
"Example: \"--filter-by-container arts\" or \"--filter-by-container arts,icons\"\n",
|
||||
optionHelpGroup: HelpGroups.Advanced
|
||||
);
|
||||
o_filterByPathID = new GroupedOption<List<string>>
|
||||
(
|
||||
optionDefaultValue: new List<string>(),
|
||||
optionName: "--filter-by-pathid <text>",
|
||||
optionDescription: "Specify the PathID by which assets should be filtered\n" +
|
||||
"*To specify multiple PathIDs write them separated by ',' or ';' without spaces\n" +
|
||||
"Example: \"--filter-by-pathid 7238605633795851352,-2430306240205277265\"\n",
|
||||
optionHelpGroup: HelpGroups.Advanced
|
||||
);
|
||||
o_filterByText = new GroupedOption<List<string>>
|
||||
(
|
||||
optionDefaultValue: new List<string>(),
|
||||
optionName: "--filter-by-text <text>",
|
||||
optionDescription: "Specify the text by which assets should be filtered\n" +
|
||||
"Looks for assets that contain the specified text in their names or containers\n" +
|
||||
"*To specify multiple values write them separated by ',' or ';' without spaces\n" +
|
||||
"Example: \"--filter-by-text portrait\" or \"--filter-by-text portrait,art\"\n",
|
||||
optionHelpGroup: HelpGroups.Advanced
|
||||
);
|
||||
o_assemblyPath = new GroupedOption<string>
|
||||
(
|
||||
optionDefaultValue: "",
|
||||
optionName: "--assembly-folder <path>",
|
||||
optionDescription: "Specify the path to the assembly folder",
|
||||
optionDescription: "Specify the path to the assembly folder\n",
|
||||
optionHelpGroup: HelpGroups.Advanced
|
||||
);
|
||||
o_unityVersion = new GroupedOption<string>
|
||||
(
|
||||
optionDefaultValue: "",
|
||||
optionName: "--unity-version <text>",
|
||||
optionDescription: "Specify Unity version. Example: \"--unity-version 2017.4.39f1\"",
|
||||
optionDescription: "Specify Unity version\nExample: \"--unity-version 2017.4.39f1\"\n",
|
||||
optionHelpGroup: HelpGroups.Advanced
|
||||
);
|
||||
f_notRestoreExtensionName = new GroupedOption<bool>
|
||||
(
|
||||
optionDefaultValue: false,
|
||||
optionName: "--not-restore-extension",
|
||||
optionDescription: "(Flag) If specified, AssetStudio will not try to use/restore original TextAsset\nextension name, and will just export all TextAssets with the \".txt\" extension",
|
||||
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",
|
||||
optionHelpGroup: HelpGroups.Advanced,
|
||||
isFlag: true
|
||||
);
|
||||
f_loadAllAssets = new GroupedOption<bool>
|
||||
(
|
||||
optionDefaultValue: false,
|
||||
optionName: "--load-all",
|
||||
optionDescription: "(Flag) If specified, AssetStudio will load assets of all types\n(Only for Dump, Info and ExportRaw modes)",
|
||||
optionHelpGroup: HelpGroups.Advanced,
|
||||
isFlag: true
|
||||
);
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal static void OptionGrouping(string name, string desc, HelpGroups group, bool isFlag)
|
||||
public static void ParseArgs(string[] args)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
cliArgs = args;
|
||||
|
||||
var optionDict = new Dictionary<string, string>() { { name, desc } };
|
||||
if (!optionGroups.ContainsKey(group))
|
||||
{
|
||||
optionGroups.Add(group, optionDict);
|
||||
}
|
||||
else
|
||||
{
|
||||
optionGroups[group].Add(name, desc);
|
||||
}
|
||||
var brightYellow = ColorConsole.BrightYellow;
|
||||
var brightRed = ColorConsole.BrightRed;
|
||||
|
||||
if (isFlag)
|
||||
{
|
||||
flagsDict.Add(name, desc);
|
||||
}
|
||||
else
|
||||
{
|
||||
optionsDict.Add(name, desc);
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseArgs(string[] args)
|
||||
{
|
||||
var brightYellow = CLIAnsiColors.BrightYellow;
|
||||
var brightRed = CLIAnsiColors.BrightRed;
|
||||
|
||||
if (args.Length == 0 || args.Any(x => x == "-h" || x == "--help"))
|
||||
if (args.Length == 0 || args.Any(x => x.ToLower() == "-h" || x.ToLower() == "--help" || x.ToLower() == "-?"))
|
||||
{
|
||||
showHelp = true;
|
||||
return;
|
||||
@@ -350,7 +415,6 @@ namespace AssetStudioCLI.Options
|
||||
$"Specified file or folder was not found. The input path must be specified as the first argument.");
|
||||
return;
|
||||
}
|
||||
o_outputFolder.Value = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ASExport");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -375,6 +439,67 @@ namespace AssetStudioCLI.Options
|
||||
}
|
||||
};
|
||||
|
||||
#region Parse "Working Mode" Option
|
||||
var workModeOptionIndex = resplittedArgs.FindIndex(x => x.ToLower() == "-m" || x.ToLower() == "--mode");
|
||||
if (workModeOptionIndex >= 0)
|
||||
{
|
||||
var option = resplittedArgs[workModeOptionIndex];
|
||||
if (workModeOptionIndex + 1 >= resplittedArgs.Count)
|
||||
{
|
||||
Console.WriteLine($"{"Error during parsing options:".Color(brightRed)} Value for [{option.Color(brightRed)}] option was not found.\n");
|
||||
TryFindOptionDescription(option, optionsDict);
|
||||
return;
|
||||
}
|
||||
var value = resplittedArgs[workModeOptionIndex + 1];
|
||||
switch (value.ToLower())
|
||||
{
|
||||
case "export":
|
||||
o_workMode.Value = WorkMode.Export;
|
||||
break;
|
||||
case "raw":
|
||||
case "exportraw":
|
||||
o_workMode.Value = WorkMode.ExportRaw;
|
||||
break;
|
||||
case "dump":
|
||||
o_workMode.Value = WorkMode.Dump;
|
||||
break;
|
||||
case "info":
|
||||
o_workMode.Value = WorkMode.Info;
|
||||
break;
|
||||
case "l2d":
|
||||
case "live2d":
|
||||
o_workMode.Value = WorkMode.ExportLive2D;
|
||||
o_exportAssetTypes.Value = new List<ClassIDType>()
|
||||
{
|
||||
ClassIDType.AnimationClip,
|
||||
ClassIDType.GameObject,
|
||||
ClassIDType.MonoBehaviour,
|
||||
ClassIDType.Texture2D,
|
||||
ClassIDType.Transform,
|
||||
};
|
||||
break;
|
||||
case "splitobjects":
|
||||
o_workMode.Value = WorkMode.SplitObjects;
|
||||
o_exportAssetTypes.Value = new List<ClassIDType>()
|
||||
{
|
||||
ClassIDType.GameObject,
|
||||
ClassIDType.Texture2D,
|
||||
ClassIDType.Material,
|
||||
ClassIDType.Transform,
|
||||
ClassIDType.Mesh,
|
||||
ClassIDType.MeshRenderer,
|
||||
ClassIDType.MeshFilter,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported working mode: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_workMode.Description);
|
||||
return;
|
||||
}
|
||||
resplittedArgs.RemoveRange(workModeOptionIndex, 2);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Parse Flags
|
||||
for (int i = 0; i < resplittedArgs.Count; i++)
|
||||
{
|
||||
@@ -386,6 +511,31 @@ namespace AssetStudioCLI.Options
|
||||
f_notRestoreExtensionName.Value = true;
|
||||
resplittedArgs.RemoveAt(i);
|
||||
break;
|
||||
case "--load-all":
|
||||
switch (o_workMode.Value)
|
||||
{
|
||||
case WorkMode.ExportRaw:
|
||||
case WorkMode.Dump:
|
||||
case WorkMode.Info:
|
||||
f_loadAllAssets.Value = true;
|
||||
resplittedArgs.RemoveAt(i);
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{flag}] flag. This flag is not suitable for the current working mode [{o_workMode.Value}].\n");
|
||||
ShowOptionDescription(f_loadAllAssets.Description, isFlag: true);
|
||||
return;
|
||||
}
|
||||
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
|
||||
@@ -399,43 +549,9 @@ namespace AssetStudioCLI.Options
|
||||
var value = resplittedArgs[i + 1].Replace("\"", "");
|
||||
switch (option)
|
||||
{
|
||||
case "-m":
|
||||
case "--mode":
|
||||
switch (value.ToLower())
|
||||
{
|
||||
case "export":
|
||||
o_workMode.Value = WorkMode.Export;
|
||||
break;
|
||||
case "raw":
|
||||
case "exportraw":
|
||||
o_workMode.Value = WorkMode.ExportRaw;
|
||||
break;
|
||||
case "dump":
|
||||
o_workMode.Value = WorkMode.Dump;
|
||||
break;
|
||||
case "info":
|
||||
o_workMode.Value = WorkMode.Info;
|
||||
break;
|
||||
case "live2d":
|
||||
o_workMode.Value = WorkMode.ExportLive2D;
|
||||
o_exportAssetTypes.Value = new List<ClassIDType>()
|
||||
{
|
||||
ClassIDType.AnimationClip,
|
||||
ClassIDType.GameObject,
|
||||
ClassIDType.MonoBehaviour,
|
||||
ClassIDType.Texture2D,
|
||||
ClassIDType.Transform,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported working mode: [{value.Color(brightRed)}].\n");
|
||||
Console.WriteLine(o_workMode.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "-t":
|
||||
case "--asset-type":
|
||||
if (o_workMode.Value == WorkMode.ExportLive2D)
|
||||
if (o_workMode.Value == WorkMode.ExportLive2D || o_workMode.Value == WorkMode.SplitObjects)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
@@ -447,44 +563,35 @@ namespace AssetStudioCLI.Options
|
||||
switch (type.ToLower())
|
||||
{
|
||||
case "tex2d":
|
||||
case "texture2d":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.Texture2D);
|
||||
break;
|
||||
case "sprite":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.Sprite);
|
||||
break;
|
||||
case "textasset":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.TextAsset);
|
||||
break;
|
||||
case "monobehaviour":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.MonoBehaviour);
|
||||
break;
|
||||
case "font":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.Font);
|
||||
break;
|
||||
case "shader":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.Shader);
|
||||
break;
|
||||
case "audio":
|
||||
case "audioclip":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.AudioClip);
|
||||
break;
|
||||
case "video":
|
||||
case "videoclip":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.VideoClip);
|
||||
break;
|
||||
case "movietexture":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.MovieTexture);
|
||||
break;
|
||||
case "mesh":
|
||||
o_exportAssetTypes.Value.Add(ClassIDType.Mesh);
|
||||
break;
|
||||
case "all":
|
||||
o_exportAssetTypes.Value = supportedAssetTypes;
|
||||
o_exportAssetTypes.Value = exportableAssetTypes;
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported asset type: [{value.Color(brightRed)}].\n");
|
||||
Console.WriteLine(o_exportAssetTypes.Description);
|
||||
var isKnownType = knownAssetTypesDict.TryGetValue(type.ToLower(), out var assetType);
|
||||
if (isKnownType)
|
||||
{
|
||||
if (f_loadAllAssets.Value || exportableAssetTypes.Contains(assetType))
|
||||
{
|
||||
o_exportAssetTypes.Value.Add(assetType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unknown asset type specified [{type.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_exportAssetTypes.Description);
|
||||
return;
|
||||
}
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Asset type [{type.Color(brightRed)}] is not supported for exporting.\n");
|
||||
ShowOptionDescription(o_exportAssetTypes.Description);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -510,7 +617,7 @@ namespace AssetStudioCLI.Options
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
@@ -523,6 +630,10 @@ namespace AssetStudioCLI.Options
|
||||
{
|
||||
Directory.CreateDirectory(value);
|
||||
}
|
||||
if (!value.EndsWith($"{Path.DirectorySeparatorChar}"))
|
||||
{
|
||||
value += Path.DirectorySeparatorChar;
|
||||
}
|
||||
o_outputFolder.Value = value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -557,7 +668,7 @@ namespace AssetStudioCLI.Options
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
@@ -575,7 +686,7 @@ namespace AssetStudioCLI.Options
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
@@ -603,7 +714,7 @@ namespace AssetStudioCLI.Options
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
@@ -619,10 +730,58 @@ namespace AssetStudioCLI.Options
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case "--fbx-scale-factor":
|
||||
var isFloat = float.TryParse(value, out float floatValue);
|
||||
if (isFloat && floatValue >= 0 && floatValue <= 100)
|
||||
{
|
||||
o_fbxScaleFactor.Value = floatValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported scale factor value: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_fbxScaleFactor.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "--fbx-bone-size":
|
||||
var isInt = int.TryParse(value, out int intValue);
|
||||
if (isInt && intValue >= 0 && intValue <= 100)
|
||||
{
|
||||
o_fbxBoneSize.Value = intValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported bone size value: [{value.Color(brightRed)}].\n");
|
||||
ShowOptionDescription(o_fbxBoneSize.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "--export-asset-list":
|
||||
switch (value.ToLower())
|
||||
{
|
||||
@@ -634,7 +793,7 @@ namespace AssetStudioCLI.Options
|
||||
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);
|
||||
ShowOptionDescription(o_exportAssetList.Description);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -658,6 +817,7 @@ namespace AssetStudioCLI.Options
|
||||
if (Directory.Exists(value))
|
||||
{
|
||||
o_assemblyPath.Value = value;
|
||||
Studio.assemblyLoader.Load(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -670,9 +830,9 @@ namespace AssetStudioCLI.Options
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
@@ -683,12 +843,12 @@ namespace AssetStudioCLI.Options
|
||||
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");
|
||||
TryShowOptionDescription(option, optionsDict);
|
||||
TryFindOptionDescription(option, optionsDict);
|
||||
}
|
||||
else if (flagsDict.Any(x => x.Key.Contains(option)))
|
||||
{
|
||||
Console.WriteLine($"{"Error:".Color(brightRed)} Unknown flag [{option.Color(brightRed)}].\n");
|
||||
TryShowOptionDescription(option, flagsDict);
|
||||
TryFindOptionDescription(option, flagsDict, isFlag: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -698,13 +858,27 @@ namespace AssetStudioCLI.Options
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Unknown Error.".Color(CLIAnsiColors.Red));
|
||||
Console.WriteLine("Unknown Error.".Color(ColorConsole.Red));
|
||||
Console.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
isParsed = true;
|
||||
#endregion
|
||||
|
||||
if (!Studio.assemblyLoader.Loaded)
|
||||
{
|
||||
Studio.assemblyLoader.Loaded = true;
|
||||
}
|
||||
if (o_outputFolder.Value == o_outputFolder.DefaultValue)
|
||||
{
|
||||
var fullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, o_outputFolder.DefaultValue + Path.DirectorySeparatorChar);
|
||||
if (!Directory.Exists(fullPath))
|
||||
{
|
||||
Directory.CreateDirectory(fullPath);
|
||||
}
|
||||
o_outputFolder.Value = fullPath;
|
||||
}
|
||||
isParsed = true;
|
||||
}
|
||||
|
||||
private static string[] ValueSplitter(string value)
|
||||
@@ -713,14 +887,21 @@ namespace AssetStudioCLI.Options
|
||||
return value.Split(separator);
|
||||
}
|
||||
|
||||
private 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())
|
||||
{
|
||||
var arg = isFlag ? "flag" : "option";
|
||||
var rand = new Random();
|
||||
var rndOption = optionDesc.ElementAt(rand.Next(0, optionDesc.Count()));
|
||||
Console.WriteLine($"Did you mean [{ $"{rndOption.Key}".Color(CLIAnsiColors.BrightYellow) }] option?");
|
||||
var rndOption = optionDesc.ElementAt(rand.Next(0, optionDesc.Length));
|
||||
Console.WriteLine($"Did you mean [{$"{rndOption.Key}".Color(ColorConsole.BrightYellow)}] {arg}?");
|
||||
Console.WriteLine($"Here's a description of it: \n\n{rndOption.Value}");
|
||||
|
||||
return true;
|
||||
@@ -728,7 +909,7 @@ namespace AssetStudioCLI.Options
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ShowHelp(bool showUsageOnly = false)
|
||||
public static void ShowHelp(bool showUsageOnly = false)
|
||||
{
|
||||
const int indent = 22;
|
||||
var helpMessage = new StringBuilder();
|
||||
@@ -761,12 +942,13 @@ namespace AssetStudioCLI.Options
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"# {appAssembly.Name}\n# Based on AssetStudioMod v{appAssembly.Version}\n");
|
||||
var arch = Environment.Is64BitProcess ? "x64" : "x32";
|
||||
Console.WriteLine($"# {appAssembly.Name} [{arch}]\n# Based on AssetStudioMod v{appAssembly.Version}\n");
|
||||
Console.WriteLine($"{usage}\n\n{helpMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
private string ShowCurrentFilter()
|
||||
private static string ShowCurrentFilter()
|
||||
{
|
||||
switch (filterBy)
|
||||
{
|
||||
@@ -785,7 +967,22 @@ namespace AssetStudioCLI.Options
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowCurrentOptions()
|
||||
private static string ShowExportTypes()
|
||||
{
|
||||
switch (o_workMode.Value)
|
||||
{
|
||||
case WorkMode.ExportRaw:
|
||||
case WorkMode.Dump:
|
||||
case WorkMode.Info:
|
||||
return f_loadAllAssets.Value && o_exportAssetTypes.Value == o_exportAssetTypes.DefaultValue
|
||||
? $"# Export Asset Type(s): All"
|
||||
: $"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}";
|
||||
default:
|
||||
return $"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}";
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShowCurrentOptions()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("[Current Options]");
|
||||
@@ -793,8 +990,35 @@ namespace AssetStudioCLI.Options
|
||||
sb.AppendLine($"# Input Path: \"{inputPath}\"");
|
||||
switch (o_workMode.Value)
|
||||
{
|
||||
case WorkMode.Export:
|
||||
case WorkMode.ExportRaw:
|
||||
case WorkMode.Dump:
|
||||
sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
|
||||
if (o_workMode.Value != WorkMode.Export)
|
||||
{
|
||||
sb.AppendLine($"# Load All Assets: {f_loadAllAssets}");
|
||||
}
|
||||
sb.AppendLine(ShowExportTypes());
|
||||
sb.AppendLine($"# Asset Group Option: {o_groupAssetsBy}");
|
||||
if (o_workMode.Value == WorkMode.Export)
|
||||
{
|
||||
sb.AppendLine($"# Export Image Format: {o_imageFormat}");
|
||||
sb.AppendLine($"# Export Audio Format: {o_audioFormat}");
|
||||
}
|
||||
sb.AppendLine($"# Log Level: {o_logLevel}");
|
||||
sb.AppendLine($"# Log Output: {o_logOutput}");
|
||||
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
|
||||
sb.AppendLine(ShowCurrentFilter());
|
||||
sb.AppendLine($"# Assembly Path: \"{o_assemblyPath}\"");
|
||||
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
|
||||
if (o_workMode.Value == WorkMode.Export)
|
||||
{
|
||||
sb.AppendLine($"# Restore TextAsset Extension: {!f_notRestoreExtensionName.Value}");
|
||||
}
|
||||
break;
|
||||
case WorkMode.Info:
|
||||
sb.AppendLine($"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}");
|
||||
sb.AppendLine($"# Load All Assets: {f_loadAllAssets}");
|
||||
sb.AppendLine(ShowExportTypes());
|
||||
sb.AppendLine($"# Log Level: {o_logLevel}");
|
||||
sb.AppendLine($"# Log Output: {o_logOutput}");
|
||||
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
|
||||
@@ -802,27 +1026,24 @@ namespace AssetStudioCLI.Options
|
||||
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
|
||||
break;
|
||||
case WorkMode.ExportLive2D:
|
||||
case WorkMode.SplitObjects:
|
||||
sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
|
||||
sb.AppendLine($"# Log Level: {o_logLevel}");
|
||||
sb.AppendLine($"# Log Output: {o_logOutput}");
|
||||
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
|
||||
sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\"");
|
||||
if (o_workMode.Value == WorkMode.SplitObjects)
|
||||
{
|
||||
sb.AppendLine($"# Export Image Format: {o_imageFormat}");
|
||||
sb.AppendLine($"# Filter by Name(s): \"{string.Join("\", \"", o_filterByName.Value)}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
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}\"");
|
||||
break;
|
||||
default:
|
||||
sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
|
||||
sb.AppendLine($"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}");
|
||||
sb.AppendLine($"# Asset Group Option: {o_groupAssetsBy}");
|
||||
sb.AppendLine($"# Export Image Format: {o_imageFormat}");
|
||||
sb.AppendLine($"# Export Audio Format: {o_audioFormat}");
|
||||
sb.AppendLine($"# Log Level: {o_logLevel}");
|
||||
sb.AppendLine($"# Log Output: {o_logOutput}");
|
||||
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
|
||||
sb.AppendLine(ShowCurrentFilter());
|
||||
sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\"");
|
||||
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
|
||||
sb.AppendLine($"# Restore TextAsset extension: {!f_notRestoreExtensionName.Value}");
|
||||
break;
|
||||
}
|
||||
sb.AppendLine("======");
|
||||
Logger.Info(sb.ToString());
|
||||
|
||||
17
AssetStudioCLI/Options/OptionExtensions.cs
Normal file
17
AssetStudioCLI/Options/OptionExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace AssetStudioCLI.Options
|
||||
{
|
||||
internal static class OptionExtensions
|
||||
{
|
||||
public static Action<string, string, HelpGroups, bool> OptionGrouping = (name, desc, group, isFlag) => { };
|
||||
}
|
||||
|
||||
internal class GroupedOption<T> : Option<T>
|
||||
{
|
||||
public GroupedOption(T optionDefaultValue, string optionName, string optionDescription, HelpGroups optionHelpGroup, bool isFlag = false) : base(optionDefaultValue, optionName, optionDescription, optionHelpGroup, isFlag)
|
||||
{
|
||||
OptionExtensions.OptionGrouping(optionName, optionDescription, optionHelpGroup, isFlag);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,52 +8,54 @@ namespace AssetStudioCLI
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var options = new CLIOptions(args);
|
||||
if (options.isParsed)
|
||||
CLIOptions.ParseArgs(args);
|
||||
if (CLIOptions.isParsed)
|
||||
{
|
||||
CLIRun(options);
|
||||
CLIRun();
|
||||
}
|
||||
else if (options.showHelp)
|
||||
else if (CLIOptions.showHelp)
|
||||
{
|
||||
options.ShowHelp();
|
||||
CLIOptions.ShowHelp();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine();
|
||||
options.ShowHelp(showUsageOnly: true);
|
||||
CLIOptions.ShowHelp(showUsageOnly: true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CLIRun(CLIOptions options)
|
||||
private static void CLIRun()
|
||||
{
|
||||
var cliLogger = new CLILogger(options);
|
||||
var cliLogger = new CLILogger();
|
||||
Logger.Default = cliLogger;
|
||||
var studio = new Studio(options);
|
||||
options.ShowCurrentOptions();
|
||||
CLIOptions.ShowCurrentOptions();
|
||||
|
||||
try
|
||||
{
|
||||
if (studio.LoadAssets())
|
||||
if (Studio.LoadAssets())
|
||||
{
|
||||
studio.ParseAssets();
|
||||
if (options.filterBy != FilterBy.None && options.o_workMode.Value != WorkMode.ExportLive2D)
|
||||
Studio.ParseAssets();
|
||||
if (CLIOptions.filterBy != FilterBy.None)
|
||||
{
|
||||
studio.FilterAssets();
|
||||
Studio.Filter();
|
||||
}
|
||||
if (options.o_exportAssetList.Value != ExportListType.None)
|
||||
if (CLIOptions.o_exportAssetList.Value != ExportListType.None)
|
||||
{
|
||||
studio.ExportAssetList();
|
||||
Studio.ExportAssetList();
|
||||
}
|
||||
switch (options.o_workMode.Value)
|
||||
switch (CLIOptions.o_workMode.Value)
|
||||
{
|
||||
case WorkMode.Info:
|
||||
studio.ShowExportableAssetsInfo();
|
||||
Studio.ShowExportableAssetsInfo();
|
||||
break;
|
||||
case WorkMode.ExportLive2D:
|
||||
studio.ExportLive2D();
|
||||
Studio.ExportLive2D();
|
||||
break;
|
||||
case WorkMode.SplitObjects:
|
||||
Studio.ExportSplitObjects();
|
||||
break;
|
||||
default:
|
||||
studio.ExportAssets();
|
||||
Studio.ExportAssets();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## AssetStudioModCLI
|
||||
CLI version of AssetStudioMod.
|
||||
- Supported asset types: `Texture2D`, `Sprite`, `TextAsset`, `MonoBehaviour`, `Font`, `Shader`, `MovieTexture`, `AudioClip`, `VideoClip`, `Mesh`.
|
||||
- Supported asset types for export: `Texture2D`, `Sprite`, `TextAsset`, `MonoBehaviour`, `Font`, `Shader`, `MovieTexture`, `AudioClip`, `VideoClip`, `Mesh`.
|
||||
- *There are no plans to add support for `AnimationClip`, `Animator` for now.*
|
||||
|
||||
### Usage
|
||||
@@ -10,20 +10,23 @@ AssetStudioModCLI <input path to asset file/folder> [-m, --mode <value>]
|
||||
[-o, --output <path>] [-h, --help]
|
||||
[--log-level <value>] [--log-output <value>]
|
||||
[--image-format <value>] [--audio-format <value>]
|
||||
[--export-asset-list <value>] [--filter-by-name <text>]
|
||||
[--filter-by-container <text>] [--filter-by-pathid <text>]
|
||||
[--filter-by-text <text>] [--assembly-folder <path>]
|
||||
[--l2d-motion-mode <value>] [--l2d-force-bezier]
|
||||
[--fbx-scale-factor <value>] [--fbx-bone-size <value>]
|
||||
[--filter-by-name <text>] [--filter-by-container <text>]
|
||||
[--filter-by-pathid <text>] [--filter-by-text <text>]
|
||||
[--export-asset-list <value>] [--assembly-folder <path>]
|
||||
[--unity-version <text>] [--not-restore-extension]
|
||||
|
||||
[--load-all]
|
||||
|
||||
General Options:
|
||||
-m, --mode <value> Specify working mode
|
||||
<Value: export(default) | exportRaw | dump | info | live2d>
|
||||
<Value: export(default) | exportRaw | dump | info | live2d | splitObjects>
|
||||
Export - Exports converted assets
|
||||
ExportRaw - Exports raw data
|
||||
Dump - Makes asset dumps
|
||||
Info - Loads file(s), shows the number of available for export assets and exits
|
||||
Live2D - Exports Live2D Cubism 3 models
|
||||
SplitObjects - Exports split objects (fbx)
|
||||
Example: "-m info"
|
||||
|
||||
-t, --asset-type <value(s)> Specify asset type(s) to export
|
||||
@@ -43,7 +46,7 @@ General Options:
|
||||
Example: "-g container"
|
||||
|
||||
-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
|
||||
|
||||
@@ -67,12 +70,27 @@ Convert Options:
|
||||
None - Do not convert audios and export them in their own format
|
||||
Example: "--audio-format wav"
|
||||
|
||||
Advanced Options:
|
||||
--export-asset-list <value> Specify the format in which you want to export asset list
|
||||
<Value: none(default) | xml>
|
||||
None - Do not export asset list
|
||||
Example: "--export-asset-list xml"
|
||||
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-scale-factor <value> Specify the FBX Scale Factor
|
||||
<Value: float number from 0 to 100 (default=1)
|
||||
Example: "--fbx-scale-factor 50"
|
||||
|
||||
--fbx-bone-size <value> Specify the FBX Bone Size
|
||||
<Value: integer number from 0 to 100 (default=10)
|
||||
Example: "--fbx-bone-size 10"
|
||||
|
||||
Filter Options:
|
||||
--filter-by-name <text> Specify the name by which assets should be filtered
|
||||
*To specify multiple names write them separated by ',' or ';' without spaces
|
||||
Example: "--filter-by-name char" or "--filter-by-name char,bg"
|
||||
@@ -90,8 +108,21 @@ Advanced Options:
|
||||
*To specify multiple values write them separated by ',' or ';' without spaces
|
||||
Example: "--filter-by-text portrait" or "--filter-by-text portrait,art"
|
||||
|
||||
|
||||
Advanced Options:
|
||||
--export-asset-list <value> Specify the format in which you want to export asset list
|
||||
<Value: none(default) | xml>
|
||||
None - Do not export asset list
|
||||
Example: "--export-asset-list xml"
|
||||
|
||||
--assembly-folder <path> Specify the path to the assembly folder
|
||||
--unity-version <text> Specify Unity version. Example: "--unity-version 2017.4.39f1"
|
||||
|
||||
--unity-version <text> Specify Unity version
|
||||
Example: "--unity-version 2017.4.39f1"
|
||||
|
||||
--not-restore-extension (Flag) If specified, AssetStudio will not try to use/restore original TextAsset
|
||||
extension name, and will just export all TextAssets with the ".txt" extension
|
||||
|
||||
--load-all (Flag) If specified, AssetStudio will load assets of all types
|
||||
(Only for Dump, Info and ExportRaw modes)
|
||||
```
|
||||
|
||||
@@ -7,44 +7,37 @@ using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using static AssetStudioCLI.Exporter;
|
||||
using static CubismLive2DExtractor.Live2DExtractor;
|
||||
using Ansi = AssetStudioCLI.CLIAnsiColors;
|
||||
using Ansi = AssetStudio.ColorConsole;
|
||||
|
||||
namespace AssetStudioCLI
|
||||
{
|
||||
internal class Studio
|
||||
internal static class Studio
|
||||
{
|
||||
public AssetsManager assetsManager = new AssetsManager();
|
||||
public List<AssetItem> parsedAssetsList = new List<AssetItem>();
|
||||
public static AssetsManager assetsManager = new AssetsManager();
|
||||
public static List<AssetItem> parsedAssetsList = new List<AssetItem>();
|
||||
public static List<BaseNode> gameObjectTree = new List<BaseNode>();
|
||||
public static AssemblyLoader assemblyLoader = new AssemblyLoader();
|
||||
private static Dictionary<AssetStudio.Object, string> containers = new Dictionary<AssetStudio.Object, string>();
|
||||
private static AssemblyLoader assemblyLoader = new AssemblyLoader();
|
||||
private readonly CLIOptions options;
|
||||
|
||||
public Studio(CLIOptions cliOptions)
|
||||
static Studio()
|
||||
{
|
||||
Progress.Default = new Progress<int>(ShowCurProgressValue);
|
||||
options = cliOptions;
|
||||
if (options.o_assemblyPath.Value != "")
|
||||
{
|
||||
assemblyLoader.Load(options.o_assemblyPath.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
assemblyLoader.Loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowCurProgressValue(int value)
|
||||
private static void ShowCurProgressValue(int value)
|
||||
{
|
||||
Console.Write($"[{value:000}%]\r");
|
||||
}
|
||||
|
||||
public bool LoadAssets()
|
||||
public static bool LoadAssets()
|
||||
{
|
||||
var isLoaded = false;
|
||||
assetsManager.SpecifyUnityVersion = options.o_unityVersion.Value;
|
||||
assetsManager.SetAssetFilter(options.o_exportAssetTypes.Value);
|
||||
|
||||
assetsManager.LoadFilesAndFolders(options.inputPath);
|
||||
assetsManager.SpecifyUnityVersion = CLIOptions.o_unityVersion.Value;
|
||||
if (!CLIOptions.f_loadAllAssets.Value)
|
||||
{
|
||||
assetsManager.SetAssetFilter(CLIOptions.o_exportAssetTypes.Value);
|
||||
}
|
||||
assetsManager.LoadFilesAndFolders(CLIOptions.inputPath);
|
||||
if (assetsManager.assetsFileList.Count == 0)
|
||||
{
|
||||
Logger.Warning("No Unity file can be loaded.");
|
||||
@@ -57,33 +50,46 @@ namespace AssetStudioCLI
|
||||
return isLoaded;
|
||||
}
|
||||
|
||||
public void ParseAssets()
|
||||
public static void ParseAssets()
|
||||
{
|
||||
Logger.Info("Parse assets...");
|
||||
|
||||
var fileAssetsList = new List<AssetItem>();
|
||||
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
|
||||
var objectAssetItemDic = new Dictionary<AssetStudio.Object, AssetItem>(objectCount);
|
||||
|
||||
Progress.Reset();
|
||||
var i = 0;
|
||||
foreach (var assetsFile in assetsManager.assetsFileList)
|
||||
{
|
||||
var preloadTable = Array.Empty<PPtr<AssetStudio.Object>>();
|
||||
foreach (var asset in assetsFile.Objects)
|
||||
{
|
||||
var assetItem = new AssetItem(asset);
|
||||
objectAssetItemDic.Add(asset, assetItem);
|
||||
assetItem.UniqueID = "_#" + i;
|
||||
var isExportable = false;
|
||||
switch (asset)
|
||||
{
|
||||
case PreloadData m_PreloadData:
|
||||
preloadTable = m_PreloadData.m_Assets;
|
||||
break;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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))
|
||||
{
|
||||
containers[obj] = m_Container.Key;
|
||||
@@ -115,13 +121,6 @@ namespace AssetStudioCLI
|
||||
assetItem.FullSize = asset.byteSize + m_VideoClip.m_ExternalResources.m_Size;
|
||||
assetItem.Text = m_VideoClip.m_Name;
|
||||
break;
|
||||
case Mesh _:
|
||||
case MovieTexture _:
|
||||
case TextAsset _:
|
||||
case Font _:
|
||||
case Sprite _:
|
||||
assetItem.Text = ((NamedObject)asset).m_Name;
|
||||
break;
|
||||
case Shader m_Shader:
|
||||
assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name;
|
||||
break;
|
||||
@@ -135,14 +134,26 @@ namespace AssetStudioCLI
|
||||
assetItem.Text = m_MonoBehaviour.m_Name;
|
||||
}
|
||||
break;
|
||||
case GameObject m_GameObject:
|
||||
assetItem.Text = m_GameObject.m_Name;
|
||||
break;
|
||||
case Animator m_Animator:
|
||||
if (m_Animator.m_GameObject.TryGet(out var gameObject))
|
||||
{
|
||||
assetItem.Text = gameObject.m_Name;
|
||||
}
|
||||
break;
|
||||
case NamedObject m_NamedObject:
|
||||
assetItem.Text = m_NamedObject.m_Name;
|
||||
break;
|
||||
}
|
||||
if (assetItem.Text == "")
|
||||
if (string.IsNullOrEmpty(assetItem.Text))
|
||||
{
|
||||
assetItem.Text = assetItem.TypeString + assetItem.UniqueID;
|
||||
}
|
||||
|
||||
isExportable = options.o_exportAssetTypes.Value.Contains(asset.type);
|
||||
if (isExportable)
|
||||
isExportable = CLIOptions.o_exportAssetTypes.Value.Contains(asset.type);
|
||||
if (isExportable || (CLIOptions.f_loadAllAssets.Value && CLIOptions.o_exportAssetTypes.Value == CLIOptions.o_exportAssetTypes.DefaultValue))
|
||||
{
|
||||
fileAssetsList.Add(assetItem);
|
||||
}
|
||||
@@ -151,21 +162,126 @@ namespace AssetStudioCLI
|
||||
}
|
||||
foreach (var asset in fileAssetsList)
|
||||
{
|
||||
if (containers.ContainsKey(asset.Asset))
|
||||
if (containers.TryGetValue(asset.Asset, out var container))
|
||||
{
|
||||
asset.Container = containers[asset.Asset];
|
||||
asset.Container = container;
|
||||
}
|
||||
}
|
||||
parsedAssetsList.AddRange(fileAssetsList);
|
||||
fileAssetsList.Clear();
|
||||
if (options.o_workMode.Value != WorkMode.ExportLive2D)
|
||||
if (CLIOptions.o_workMode.Value != WorkMode.ExportLive2D)
|
||||
{
|
||||
containers.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (CLIOptions.o_workMode.Value == WorkMode.SplitObjects)
|
||||
{
|
||||
BuildTreeStructure(objectAssetItemDic);
|
||||
}
|
||||
|
||||
var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {parsedAssetsList.Count} exportable assets";
|
||||
var unityVer = assetsManager.assetsFileList[0].version;
|
||||
long m_ObjectsCount;
|
||||
if (unityVer[0] > 2020)
|
||||
{
|
||||
m_ObjectsCount = assetsManager.assetsFileList.Sum(x => x.m_Objects.LongCount(y =>
|
||||
y.classID != (int)ClassIDType.Shader
|
||||
&& CLIOptions.o_exportAssetTypes.Value.Any(k => (int)k == y.classID))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ObjectsCount = assetsManager.assetsFileList.Sum(x => x.m_Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => (int)k == y.classID)));
|
||||
}
|
||||
var objectsCount = assetsManager.assetsFileList.Sum(x => x.Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => k == y.type)));
|
||||
if (m_ObjectsCount != objectsCount)
|
||||
{
|
||||
log += $" and {m_ObjectsCount - objectsCount} assets failed to read";
|
||||
}
|
||||
Logger.Info(log);
|
||||
}
|
||||
|
||||
public void ShowExportableAssetsInfo()
|
||||
public static void BuildTreeStructure(Dictionary<AssetStudio.Object, AssetItem> objectAssetItemDic)
|
||||
{
|
||||
Logger.Info("Building tree structure...");
|
||||
|
||||
var treeNodeDictionary = new Dictionary<GameObject, GameObjectNode>();
|
||||
var assetsFileCount = assetsManager.assetsFileList.Count;
|
||||
int j = 0;
|
||||
Progress.Reset();
|
||||
foreach (var assetsFile in assetsManager.assetsFileList)
|
||||
{
|
||||
var fileNode = new BaseNode(); //RootNode
|
||||
|
||||
foreach (var obj in assetsFile.Objects)
|
||||
{
|
||||
if (obj is GameObject m_GameObject)
|
||||
{
|
||||
if (!treeNodeDictionary.TryGetValue(m_GameObject, out var currentNode))
|
||||
{
|
||||
currentNode = new GameObjectNode(m_GameObject);
|
||||
treeNodeDictionary.Add(m_GameObject, currentNode);
|
||||
}
|
||||
|
||||
foreach (var pptr in m_GameObject.m_Components)
|
||||
{
|
||||
if (pptr.TryGet(out var m_Component))
|
||||
{
|
||||
objectAssetItemDic[m_Component].Node = currentNode;
|
||||
if (m_Component is MeshFilter m_MeshFilter)
|
||||
{
|
||||
if (m_MeshFilter.m_Mesh.TryGet(out var m_Mesh))
|
||||
{
|
||||
objectAssetItemDic[m_Mesh].Node = currentNode;
|
||||
}
|
||||
}
|
||||
else if (m_Component is SkinnedMeshRenderer m_SkinnedMeshRenderer)
|
||||
{
|
||||
if (m_SkinnedMeshRenderer.m_Mesh.TryGet(out var m_Mesh))
|
||||
{
|
||||
objectAssetItemDic[m_Mesh].Node = currentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var parentNode = fileNode;
|
||||
|
||||
if (m_GameObject.m_Transform != null)
|
||||
{
|
||||
if (m_GameObject.m_Transform.m_Father.TryGet(out var m_Father))
|
||||
{
|
||||
if (m_Father.m_GameObject.TryGet(out var parentGameObject))
|
||||
{
|
||||
if (!treeNodeDictionary.TryGetValue(parentGameObject, out var parentGameObjectNode))
|
||||
{
|
||||
parentGameObjectNode = new GameObjectNode(parentGameObject);
|
||||
treeNodeDictionary.Add(parentGameObject, parentGameObjectNode);
|
||||
}
|
||||
parentNode = parentGameObjectNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parentNode.nodes.Add(currentNode);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (fileNode.nodes.Count > 0)
|
||||
{
|
||||
gameObjectTree.Add(fileNode);
|
||||
}
|
||||
|
||||
Progress.Report(++j, assetsFileCount);
|
||||
}
|
||||
|
||||
treeNodeDictionary.Clear();
|
||||
objectAssetItemDic.Clear();
|
||||
}
|
||||
|
||||
public static void ShowExportableAssetsInfo()
|
||||
{
|
||||
var exportableAssetsCountDict = new Dictionary<ClassIDType, int>();
|
||||
string info = "";
|
||||
@@ -198,7 +314,7 @@ namespace AssetStudioCLI
|
||||
info += "No exportable assets found.";
|
||||
}
|
||||
|
||||
if (options.o_logLevel.Value > LoggerEvent.Info)
|
||||
if (CLIOptions.o_logLevel.Value > LoggerEvent.Info)
|
||||
{
|
||||
Console.WriteLine(info);
|
||||
}
|
||||
@@ -208,53 +324,66 @@ namespace AssetStudioCLI
|
||||
}
|
||||
}
|
||||
|
||||
public void FilterAssets()
|
||||
public static void Filter()
|
||||
{
|
||||
switch (CLIOptions.o_workMode.Value)
|
||||
{
|
||||
case WorkMode.ExportLive2D:
|
||||
case WorkMode.SplitObjects:
|
||||
break;
|
||||
default:
|
||||
FilterAssets();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FilterAssets()
|
||||
{
|
||||
var assetsCount = parsedAssetsList.Count;
|
||||
var filteredAssets = new List<AssetItem>();
|
||||
|
||||
switch(options.filterBy)
|
||||
switch(CLIOptions.filterBy)
|
||||
{
|
||||
case FilterBy.Name:
|
||||
filteredAssets = parsedAssetsList.FindAll(x => options.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
filteredAssets = parsedAssetsList.FindAll(x => CLIOptions.o_filterByName.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
Logger.Info(
|
||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||
$"that contain {$"\"{string.Join("\", \"", options.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
|
||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
|
||||
);
|
||||
break;
|
||||
case FilterBy.Container:
|
||||
filteredAssets = parsedAssetsList.FindAll(x => options.o_filterByContainer.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
filteredAssets = parsedAssetsList.FindAll(x => CLIOptions.o_filterByContainer.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
Logger.Info(
|
||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||
$"that contain {$"\"{string.Join("\", \"", options.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers."
|
||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers."
|
||||
);
|
||||
break;
|
||||
case FilterBy.PathID:
|
||||
filteredAssets = parsedAssetsList.FindAll(x => options.o_filterByPathID.Value.Any(y => x.m_PathID.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
filteredAssets = parsedAssetsList.FindAll(x => CLIOptions.o_filterByPathID.Value.Any(y => x.m_PathID.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
|
||||
Logger.Info(
|
||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||
$"that contain {$"\"{string.Join("\", \"", options.o_filterByPathID.Value)}\"".Color(Ansi.BrightYellow)} in their PathIDs."
|
||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByPathID.Value)}\"".Color(Ansi.BrightYellow)} in their PathIDs."
|
||||
);
|
||||
break;
|
||||
case FilterBy.NameOrContainer:
|
||||
filteredAssets = parsedAssetsList.FindAll(x =>
|
||||
options.o_filterByText.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) ||
|
||||
options.o_filterByText.Value.Any(y => x.Container.ToString().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)
|
||||
);
|
||||
Logger.Info(
|
||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||
$"that contain {$"\"{string.Join("\", \"", options.o_filterByText.Value)}\"".Color(Ansi.BrightYellow)} in their Names or Contaniers."
|
||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByText.Value)}\"".Color(Ansi.BrightYellow)} in their Names or Contaniers."
|
||||
);
|
||||
break;
|
||||
case FilterBy.NameAndContainer:
|
||||
filteredAssets = parsedAssetsList.FindAll(x =>
|
||||
options.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) &&
|
||||
options.o_filterByContainer.Value.Any(y => x.Container.ToString().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)
|
||||
);
|
||||
Logger.Info(
|
||||
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
|
||||
$"that contain {$"\"{string.Join("\", \"", options.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers " +
|
||||
$"and {$"\"{string.Join("\", \"", options.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
|
||||
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers " +
|
||||
$"and {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -262,13 +391,13 @@ namespace AssetStudioCLI
|
||||
parsedAssetsList = filteredAssets;
|
||||
}
|
||||
|
||||
public void ExportAssets()
|
||||
public static void ExportAssets()
|
||||
{
|
||||
var savePath = options.o_outputFolder.Value;
|
||||
var savePath = CLIOptions.o_outputFolder.Value;
|
||||
var toExportCount = parsedAssetsList.Count;
|
||||
var exportedCount = 0;
|
||||
|
||||
var groupOption = options.o_groupAssetsBy.Value;
|
||||
var groupOption = CLIOptions.o_groupAssetsBy.Value;
|
||||
foreach (var asset in parsedAssetsList)
|
||||
{
|
||||
string exportPath;
|
||||
@@ -310,25 +439,25 @@ namespace AssetStudioCLI
|
||||
exportPath += Path.DirectorySeparatorChar;
|
||||
try
|
||||
{
|
||||
switch (options.o_workMode.Value)
|
||||
switch (CLIOptions.o_workMode.Value)
|
||||
{
|
||||
case WorkMode.ExportRaw:
|
||||
Logger.Debug($"{options.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
|
||||
Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
|
||||
if (ExportRawFile(asset, exportPath))
|
||||
{
|
||||
exportedCount++;
|
||||
}
|
||||
break;
|
||||
case WorkMode.Dump:
|
||||
Logger.Debug($"{options.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
|
||||
if (ExportDumpFile(asset, exportPath, assemblyLoader))
|
||||
Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
|
||||
if (ExportDumpFile(asset, exportPath))
|
||||
{
|
||||
exportedCount++;
|
||||
}
|
||||
break;
|
||||
case WorkMode.Export:
|
||||
Logger.Debug($"{options.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
|
||||
if (ExportConvertFile(asset, exportPath, options, assemblyLoader))
|
||||
Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
|
||||
if (ExportConvertFile(asset, exportPath))
|
||||
{
|
||||
exportedCount++;
|
||||
}
|
||||
@@ -349,11 +478,11 @@ namespace AssetStudioCLI
|
||||
}
|
||||
else if (toExportCount > exportedCount)
|
||||
{
|
||||
Logger.Default.Log(LoggerEvent.Info, $"Finished exporting {exportedCount} asset(s) to \"{options.o_outputFolder.Value.Color(Ansi.BrightYellow)}\".", ignoreLevel: true);
|
||||
Logger.Default.Log(LoggerEvent.Info, $"Finished exporting {exportedCount} asset(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightYellow)}\".", ignoreLevel: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Default.Log(LoggerEvent.Info, $"Finished exporting {exportedCount} asset(s) to \"{options.o_outputFolder.Value.Color(Ansi.BrightGreen)}\".", ignoreLevel: true);
|
||||
Logger.Default.Log(LoggerEvent.Info, $"Finished exporting {exportedCount} asset(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightGreen)}\".", ignoreLevel: true);
|
||||
}
|
||||
|
||||
if (toExportCount > exportedCount)
|
||||
@@ -362,11 +491,11 @@ namespace AssetStudioCLI
|
||||
}
|
||||
}
|
||||
|
||||
public void ExportAssetList()
|
||||
public static void ExportAssetList()
|
||||
{
|
||||
var savePath = options.o_outputFolder.Value;
|
||||
var savePath = CLIOptions.o_outputFolder.Value;
|
||||
|
||||
switch (options.o_exportAssetList.Value)
|
||||
switch (CLIOptions.o_exportAssetList.Value)
|
||||
{
|
||||
case ExportListType.XML:
|
||||
var filename = Path.Combine(savePath, "assets.xml");
|
||||
@@ -393,10 +522,107 @@ namespace AssetStudioCLI
|
||||
Logger.Info($"Finished exporting asset list with {parsedAssetsList.Count} items.");
|
||||
}
|
||||
|
||||
public void ExportLive2D()
|
||||
public static void ExportSplitObjects()
|
||||
{
|
||||
var baseDestPath = Path.Combine(options.o_outputFolder.Value, "Live2DOutput");
|
||||
var savePath = CLIOptions.o_outputFolder.Value;
|
||||
var searchList = CLIOptions.o_filterByName.Value;
|
||||
var isFiltered = CLIOptions.filterBy == FilterBy.Name;
|
||||
|
||||
var exportableObjects = new List<GameObjectNode>();
|
||||
var exportedCount = 0;
|
||||
var k = 0;
|
||||
|
||||
Logger.Info($"Searching for objects to export..");
|
||||
Progress.Reset();
|
||||
var count = gameObjectTree.Sum(x => x.nodes.Count);
|
||||
foreach (var node in gameObjectTree)
|
||||
{
|
||||
foreach (GameObjectNode j in node.nodes)
|
||||
{
|
||||
if (isFiltered)
|
||||
{
|
||||
if (!searchList.Any(searchText => j.gameObject.m_Name.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0))
|
||||
continue;
|
||||
}
|
||||
var gameObjects = new List<GameObject>();
|
||||
CollectNode(j, gameObjects);
|
||||
|
||||
if (gameObjects.All(x => x.m_SkinnedMeshRenderer == null && x.m_MeshFilter == null))
|
||||
{
|
||||
Progress.Report(++k, count);
|
||||
continue;
|
||||
}
|
||||
exportableObjects.Add(j);
|
||||
}
|
||||
}
|
||||
gameObjectTree.Clear();
|
||||
var exportableCount = exportableObjects.Count;
|
||||
var log = $"Found {exportableCount} exportable object(s) ";
|
||||
if (isFiltered)
|
||||
{
|
||||
log += $"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names";
|
||||
}
|
||||
Logger.Info(log);
|
||||
if (exportableCount > 0)
|
||||
{
|
||||
Progress.Reset();
|
||||
k = 0;
|
||||
|
||||
foreach (var gameObjectNode in exportableObjects)
|
||||
{
|
||||
var gameObject = gameObjectNode.gameObject;
|
||||
var filename = FixFileName(gameObject.m_Name);
|
||||
var targetPath = $"{savePath}{filename}{Path.DirectorySeparatorChar}";
|
||||
//重名文件处理
|
||||
for (int i = 1; ; i++)
|
||||
{
|
||||
if (Directory.Exists(targetPath))
|
||||
{
|
||||
targetPath = $"{savePath}{filename} ({i}){Path.DirectorySeparatorChar}";
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
Directory.CreateDirectory(targetPath);
|
||||
//导出FBX
|
||||
Logger.Info($"Exporting {filename}.fbx");
|
||||
Progress.Report(k, exportableCount);
|
||||
try
|
||||
{
|
||||
ExportGameObject(gameObject, targetPath);
|
||||
Logger.Debug($"{gameObject.type} \"{filename}\" saved to \"{targetPath}\"");
|
||||
exportedCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Export GameObject:{gameObject.m_Name} error", ex);
|
||||
}
|
||||
k++;
|
||||
}
|
||||
}
|
||||
var status = exportedCount > 0
|
||||
? $"Finished exporting [{exportedCount}/{exportableCount}] object(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightCyan)}\""
|
||||
: "Nothing exported";
|
||||
Logger.Default.Log(LoggerEvent.Info, status, ignoreLevel: true);
|
||||
}
|
||||
|
||||
private static void CollectNode(GameObjectNode node, List<GameObject> gameObjects)
|
||||
{
|
||||
gameObjects.Add(node.gameObject);
|
||||
foreach (GameObjectNode i in node.nodes)
|
||||
{
|
||||
CollectNode(i, gameObjects);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExportLive2D()
|
||||
{
|
||||
var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput");
|
||||
var useFullContainerPath = false;
|
||||
var motionMode = CLIOptions.o_l2dMotionMode.Value;
|
||||
var forceBezier = CLIOptions.f_l2dForceBezier.Value;
|
||||
|
||||
Progress.Reset();
|
||||
Logger.Info($"Searching for Live2D files...");
|
||||
@@ -410,6 +636,7 @@ namespace AssetStudioCLI
|
||||
}
|
||||
return false;
|
||||
}).Select(x => x.Asset).ToArray();
|
||||
|
||||
if (cubismMocs.Length == 0)
|
||||
{
|
||||
Logger.Default.Log(LoggerEvent.Info, "Live2D Cubism models were not found.", ignoreLevel: true);
|
||||
@@ -417,7 +644,18 @@ namespace AssetStudioCLI
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -425,9 +663,16 @@ namespace AssetStudioCLI
|
||||
Logger.Debug($"useFullContainerPath: {useFullContainerPath}");
|
||||
}
|
||||
}
|
||||
var basePathList = useFullContainerPath ?
|
||||
cubismMocs.Select(x => containers[x]).ToList() :
|
||||
cubismMocs.Select(x => containers[x].Substring(0, containers[x].LastIndexOf("/"))).ToList();
|
||||
|
||||
var basePathList = cubismMocs.Select(x =>
|
||||
{
|
||||
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(
|
||||
x => basePathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
|
||||
x => x.Key
|
||||
@@ -435,33 +680,33 @@ namespace AssetStudioCLI
|
||||
|
||||
var totalModelCount = lookup.LongCount(x => x.Key != null);
|
||||
Logger.Info($"Found {totalModelCount} model(s).");
|
||||
var name = "";
|
||||
var modelCounter = 0;
|
||||
foreach (var assets in lookup)
|
||||
{
|
||||
var container = assets.Key;
|
||||
if (container == null)
|
||||
var srcContainer = assets.Key;
|
||||
if (srcContainer == null)
|
||||
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
|
||||
{
|
||||
var modelName = useFullContainerPath ? Path.GetFileNameWithoutExtension(container) : container.Substring(container.LastIndexOf('/') + 1);
|
||||
container = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container;
|
||||
var destPath = Path.Combine(baseDestPath, container) + Path.DirectorySeparatorChar;
|
||||
|
||||
ExtractLive2D(assets, destPath, modelName, assemblyLoader);
|
||||
ExtractLive2D(assets, destPath, modelName, assemblyLoader, motionMode, forceBezier);
|
||||
modelCounter++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Live2D model export error: \"{name}\"", ex);
|
||||
Logger.Error($"Live2D model export error: \"{srcContainer}\"", ex);
|
||||
}
|
||||
Progress.Report(modelCounter, (int)totalModelCount);
|
||||
}
|
||||
|
||||
var status = modelCounter > 0 ?
|
||||
$"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s) to \"{options.o_outputFolder.Value.Color(Ansi.BrightCyan)}\"" :
|
||||
$"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightCyan)}\"" :
|
||||
"Nothing exported.";
|
||||
Logger.Default.Log(LoggerEvent.Info, status, ignoreLevel: true);
|
||||
}
|
||||
|
||||
22
AssetStudioFBXNative/CMakeLists.txt
Normal file
22
AssetStudioFBXNative/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
# Set the minimum version of CMake that can be used
|
||||
cmake_minimum_required (VERSION 3.8)
|
||||
|
||||
# Set the project name
|
||||
project("AssetStudioFBXNative")
|
||||
|
||||
# Set the C++ standard to C++ 14
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
# Generate the shared library from the library sources
|
||||
add_library(AssetStudioFBXNative SHARED
|
||||
asfbx_skin_context.cpp
|
||||
asfbx_morph_context.cpp
|
||||
api.cpp
|
||||
utils.cpp
|
||||
asfbx_context.cpp
|
||||
asfbx_anim_context.cpp)
|
||||
|
||||
# Add the given directories to those the compiler uses to search for include files
|
||||
target_include_directories(AssetStudioFBXNative PRIVATE .)
|
||||
|
||||
target_link_libraries(AssetStudioFBXNative PRIVATE fbxsdk xml2)
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<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>
|
||||
<Version>0.17.1.0</Version>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Copyright>Copyright © Perfare 2018-2022; Copyright © hozuki 2020</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AssetStudioGUI
|
||||
@@ -18,13 +10,15 @@ namespace AssetStudioGUI
|
||||
public AboutForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
var productName = Application.ProductName;
|
||||
var arch = Environment.Is64BitProcess ? "x64" : "x32";
|
||||
var appAssembly = typeof(Program).Assembly.GetName();
|
||||
var productName = appAssembly.Name;
|
||||
var productVer = appAssembly.Version.ToString();
|
||||
Text += " " + productName;
|
||||
productTitleLabel.Text = productName;
|
||||
productVersionLabel.Text = $"v{Application.ProductVersion} [{arch}]";
|
||||
productVersionLabel.Text = $"v{productVer} [{arch}]";
|
||||
productNamelabel.Text = productName;
|
||||
modVersionLabel.Text = Application.ProductVersion;
|
||||
modVersionLabel.Text = productVer;
|
||||
|
||||
licenseRichTextBox.Text = GetLicenseText();
|
||||
}
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<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>
|
||||
<ApplicationIcon>Resources\as.ico</ApplicationIcon>
|
||||
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle>
|
||||
<AssemblyName>AssetStudioModGUI</AssemblyName>
|
||||
<Version>0.17.1.0</Version>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2021-2023</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net472' ">
|
||||
<IsPublishable>false</IsPublishable>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -52,8 +53,15 @@
|
||||
</ContentWithTargetPath>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Core-6.0" Version="1.1.6" />
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell-6.0" Version="1.1.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
|
||||
<PackageReference Include="OpenTK" Version="4.7.7" />
|
||||
<PackageReference Include="OpenTK.Graphics" Version="4.8.2" />
|
||||
<PackageReference Include="OpenTK.Windowing.Desktop" Version="4.8.2" />
|
||||
<Reference Include="OpenTK.WinForms">
|
||||
<HintPath>Libraries\OpenTK.WinForms.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -64,12 +72,6 @@
|
||||
<PackageReference Include="OpenTK.GLControl" Version="3.3.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Core-6.0" Version="1.1.6-preview.2.24bd88f" />
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell-6.0" Version="1.1.6-preview.2.24bd88f" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Use local compiled win-x86 and win-x64 Texture2DDecoder libs, because libs from Kyaru.Texture2DDecoder.Windows were compiled with /MD flag -->
|
||||
|
||||
<Target Name="CopyExtraFilesPortable" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' OR '$(TargetFramework)' == 'net472' ">
|
||||
|
||||
68
AssetStudioGUI/AssetStudioGUIForm.Designer.cs
generated
68
AssetStudioGUI/AssetStudioGUIForm.Designer.cs
generated
@@ -41,6 +41,7 @@
|
||||
this.displayAll = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.enablePreview = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.displayInfo = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.buildTreeStructureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripMenuItem14 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.specifyUnityVersion = new System.Windows.Forms.ToolStripTextBox();
|
||||
this.showExpOpt = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@@ -77,6 +78,8 @@
|
||||
this.allToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.debugMenuItem = 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.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
||||
@@ -125,6 +128,8 @@
|
||||
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
||||
this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.contextMenuStrip2 = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.showRelatedAssetsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.selectAllToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.clearSelectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator();
|
||||
@@ -229,6 +234,7 @@
|
||||
this.displayAll,
|
||||
this.enablePreview,
|
||||
this.displayInfo,
|
||||
this.buildTreeStructureToolStripMenuItem,
|
||||
this.toolStripMenuItem14,
|
||||
this.showExpOpt});
|
||||
this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
|
||||
@@ -269,6 +275,17 @@
|
||||
"t, audio bitrate, etc.";
|
||||
this.displayInfo.CheckedChanged += new System.EventHandler(this.displayAssetInfo_Check);
|
||||
//
|
||||
// buildTreeStructureToolStripMenuItem
|
||||
//
|
||||
this.buildTreeStructureToolStripMenuItem.Checked = true;
|
||||
this.buildTreeStructureToolStripMenuItem.CheckOnClick = true;
|
||||
this.buildTreeStructureToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.buildTreeStructureToolStripMenuItem.Name = "buildTreeStructureToolStripMenuItem";
|
||||
this.buildTreeStructureToolStripMenuItem.Size = new System.Drawing.Size(207, 22);
|
||||
this.buildTreeStructureToolStripMenuItem.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
|
||||
//
|
||||
this.toolStripMenuItem14.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
@@ -526,7 +543,7 @@
|
||||
this.allToolStripMenuItem.CheckOnClick = true;
|
||||
this.allToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.allToolStripMenuItem.Name = "allToolStripMenuItem";
|
||||
this.allToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
|
||||
this.allToolStripMenuItem.Size = new System.Drawing.Size(88, 22);
|
||||
this.allToolStripMenuItem.Text = "All";
|
||||
this.allToolStripMenuItem.Click += new System.EventHandler(this.typeToolStripMenuItem_Click);
|
||||
//
|
||||
@@ -534,6 +551,8 @@
|
||||
//
|
||||
this.debugMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.toolStripMenuItem15,
|
||||
this.showConsoleToolStripMenuItem,
|
||||
this.writeLogToFileToolStripMenuItem,
|
||||
this.exportClassStructuresMenuItem});
|
||||
this.debugMenuItem.Name = "debugMenuItem";
|
||||
this.debugMenuItem.Size = new System.Drawing.Size(54, 20);
|
||||
@@ -547,6 +566,24 @@
|
||||
this.toolStripMenuItem15.Text = "Show all error messages";
|
||||
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
|
||||
//
|
||||
this.exportClassStructuresMenuItem.Name = "exportClassStructuresMenuItem";
|
||||
@@ -620,7 +657,7 @@
|
||||
this.sceneTreeView.Size = new System.Drawing.Size(472, 587);
|
||||
this.sceneTreeView.TabIndex = 1;
|
||||
this.sceneTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.sceneTreeView_AfterCheck);
|
||||
this.sceneTreeView.MouseClick += new System.Windows.Forms.MouseEventHandler(this.sceneTreeView_MouseClick);
|
||||
this.sceneTreeView.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.sceneTreeView_NodeMouseClick);
|
||||
//
|
||||
// treeSearch
|
||||
//
|
||||
@@ -629,7 +666,7 @@
|
||||
this.treeSearch.Location = new System.Drawing.Point(0, 0);
|
||||
this.treeSearch.Name = "treeSearch";
|
||||
this.treeSearch.Size = new System.Drawing.Size(472, 20);
|
||||
this.treeSearch.TabIndex = 0;
|
||||
this.treeSearch.TabIndex = 2;
|
||||
this.treeSearch.Text = " Search ";
|
||||
this.treeSearch.TextChanged += new System.EventHandler(this.treeSearch_TextChanged);
|
||||
this.treeSearch.Enter += new System.EventHandler(this.treeSearch_Enter);
|
||||
@@ -1115,13 +1152,28 @@
|
||||
// contextMenuStrip2
|
||||
//
|
||||
this.contextMenuStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.showRelatedAssetsToolStripMenuItem,
|
||||
this.toolStripSeparator7,
|
||||
this.selectAllToolStripMenuItem,
|
||||
this.clearSelectionToolStripMenuItem,
|
||||
this.toolStripSeparator5,
|
||||
this.expandAllToolStripMenuItem,
|
||||
this.collapseAllToolStripMenuItem});
|
||||
this.contextMenuStrip2.Name = "contextMenuStrip2";
|
||||
this.contextMenuStrip2.Size = new System.Drawing.Size(152, 98);
|
||||
this.contextMenuStrip2.Size = new System.Drawing.Size(152, 126);
|
||||
this.contextMenuStrip2.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip2_Opening);
|
||||
//
|
||||
// showRelatedAssetsToolStripMenuItem
|
||||
//
|
||||
this.showRelatedAssetsToolStripMenuItem.Name = "showRelatedAssetsToolStripMenuItem";
|
||||
this.showRelatedAssetsToolStripMenuItem.Size = new System.Drawing.Size(151, 22);
|
||||
this.showRelatedAssetsToolStripMenuItem.Text = "Related assets";
|
||||
this.showRelatedAssetsToolStripMenuItem.Click += new System.EventHandler(this.showRelatedAssetsToolStripMenuItem_Click);
|
||||
//
|
||||
// toolStripSeparator7
|
||||
//
|
||||
this.toolStripSeparator7.Name = "toolStripSeparator7";
|
||||
this.toolStripSeparator7.Size = new System.Drawing.Size(148, 6);
|
||||
//
|
||||
// selectAllToolStripMenuItem
|
||||
//
|
||||
@@ -1239,6 +1291,7 @@
|
||||
this.Name = "AssetStudioGUIForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "AssetStudioModGUI";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.AssetStudioGUIForm_FormClosing);
|
||||
this.DragDrop += new System.Windows.Forms.DragEventHandler(this.AssetStudioGUIForm_DragDrop);
|
||||
this.DragEnter += new System.Windows.Forms.DragEventHandler(this.AssetStudioGUIForm_DragEnter);
|
||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.AssetStudioForm_KeyDown);
|
||||
@@ -1286,7 +1339,6 @@
|
||||
private System.Windows.Forms.TextBox treeSearch;
|
||||
private System.Windows.Forms.ToolStripMenuItem loadFileToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem loadFolderToolStripMenuItem;
|
||||
private System.Windows.Forms.ListView assetListView;
|
||||
private System.Windows.Forms.ColumnHeader columnHeaderName;
|
||||
private System.Windows.Forms.ColumnHeader columnHeaderSize;
|
||||
private System.Windows.Forms.ColumnHeader columnHeaderType;
|
||||
@@ -1386,6 +1438,12 @@
|
||||
private System.Windows.Forms.RichTextBox listSearch;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator6;
|
||||
private System.Windows.Forms.ToolStripMenuItem allLive2DModelsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem showRelatedAssetsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator7;
|
||||
private System.Windows.Forms.ListView assetListView;
|
||||
private System.Windows.Forms.ToolStripMenuItem showConsoleToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem writeLogToFileToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem buildTreeStructureToolStripMenuItem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using AssetStudio;
|
||||
using Newtonsoft.Json;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -21,6 +20,7 @@ using static AssetStudioGUI.Studio;
|
||||
using Font = AssetStudio.Font;
|
||||
using Microsoft.WindowsAPICodePack.Taskbar;
|
||||
#if NET472
|
||||
using OpenTK;
|
||||
using Vector3 = OpenTK.Vector3;
|
||||
using Vector4 = OpenTK.Vector4;
|
||||
#else
|
||||
@@ -112,21 +112,32 @@ namespace AssetStudioGUI
|
||||
[DllImport("gdi32.dll")]
|
||||
private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts);
|
||||
|
||||
private string guiTitle = string.Empty;
|
||||
|
||||
public AssetStudioGUIForm()
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
|
||||
ConsoleWindow.RunConsole(Properties.Settings.Default.showConsole);
|
||||
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.Elapsed += new ElapsedEventHandler(delayTimer_Elapsed);
|
||||
delayTimer.Elapsed += delayTimer_Elapsed;
|
||||
displayAll.Checked = Properties.Settings.Default.displayAll;
|
||||
displayInfo.Checked = Properties.Settings.Default.displayInfo;
|
||||
enablePreview.Checked = Properties.Settings.Default.enablePreview;
|
||||
showConsoleToolStripMenuItem.Checked = Properties.Settings.Default.showConsole;
|
||||
buildTreeStructureToolStripMenuItem.Checked = Properties.Settings.Default.buildTreeStructure;
|
||||
FMODinit();
|
||||
listSearchFilterMode.SelectedIndex = 0;
|
||||
|
||||
logger = new GUILogger(StatusStripUpdate);
|
||||
Logger.Default = logger;
|
||||
writeLogToFileToolStripMenuItem.Checked = Properties.Settings.Default.useFileLogger;
|
||||
|
||||
Progress.Default = new Progress<int>(SetProgressBarValue);
|
||||
Studio.StatusStripUpdate = StatusStripUpdate;
|
||||
}
|
||||
@@ -135,21 +146,32 @@ namespace AssetStudioGUI
|
||||
{
|
||||
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||
{
|
||||
e.Effect = DragDropEffects.Move;
|
||||
e.Effect = DragDropEffects.Copy;
|
||||
}
|
||||
}
|
||||
|
||||
private async void AssetStudioGUIForm_DragDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
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();
|
||||
assetsManager.SpecifyUnityVersion = specifyUnityVersion.Text;
|
||||
await Task.Run(() => assetsManager.LoadFilesAndFolders(out openDirectoryBackup, paths));
|
||||
saveDirectoryBackup = openDirectoryBackup;
|
||||
BuildAssetStructures();
|
||||
if (paths[i].ToLower().EndsWith(".lnk"))
|
||||
{
|
||||
var targetPath = LnkReader.GetLnkTarget(paths[i]);
|
||||
if (!string.IsNullOrEmpty(targetPath))
|
||||
{
|
||||
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)
|
||||
@@ -218,17 +240,11 @@ namespace AssetStudioGUI
|
||||
return;
|
||||
}
|
||||
|
||||
(var productName, var treeNodeCollection) = await Task.Run(() => BuildAssetData());
|
||||
var (productName, treeNodeCollection) = await Task.Run(() => BuildAssetData());
|
||||
var typeMap = await Task.Run(() => BuildClassStructure());
|
||||
productName = string.IsNullOrEmpty(productName) ? "no productName" : productName;
|
||||
|
||||
if (!string.IsNullOrEmpty(productName))
|
||||
{
|
||||
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}";
|
||||
}
|
||||
Text = $"{guiTitle} - {productName} - {assetsManager.assetsFileList[0].unityVersion} - {assetsManager.assetsFileList[0].m_TargetPlatform}";
|
||||
|
||||
assetListView.VirtualListSize = visibleAssets.Count;
|
||||
|
||||
@@ -267,7 +283,10 @@ namespace AssetStudioGUI
|
||||
}
|
||||
allToolStripMenuItem.Checked = true;
|
||||
var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {assetListView.Items.Count} exportable assets";
|
||||
var m_ObjectsCount = assetsManager.assetsFileList.Sum(x => x.m_Objects.Count);
|
||||
var unityVer = assetsManager.assetsFileList[0].version;
|
||||
var m_ObjectsCount = unityVer[0] > 2020 ?
|
||||
assetsManager.assetsFileList.Sum(x => x.m_Objects.LongCount(y => y.classID != (int)ClassIDType.Shader)) :
|
||||
assetsManager.assetsFileList.Sum(x => x.m_Objects.Count);
|
||||
var objectsCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
|
||||
if (m_ObjectsCount != objectsCount)
|
||||
{
|
||||
@@ -499,10 +518,10 @@ namespace AssetStudioGUI
|
||||
switch (e.TabPageIndex)
|
||||
{
|
||||
case 0:
|
||||
treeSearch.Select();
|
||||
sceneTreeView.Select();
|
||||
break;
|
||||
case 1:
|
||||
listSearch.Select();
|
||||
assetListView.Select();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -782,42 +801,42 @@ namespace AssetStudioGUI
|
||||
return;
|
||||
try
|
||||
{
|
||||
switch (assetItem.Asset)
|
||||
switch (assetItem.Type)
|
||||
{
|
||||
case Texture2D m_Texture2D:
|
||||
PreviewTexture2D(assetItem, m_Texture2D);
|
||||
case ClassIDType.Texture2D:
|
||||
PreviewTexture2D(assetItem, assetItem.Asset as Texture2D);
|
||||
break;
|
||||
case AudioClip m_AudioClip:
|
||||
PreviewAudioClip(assetItem, m_AudioClip);
|
||||
case ClassIDType.AudioClip:
|
||||
PreviewAudioClip(assetItem, assetItem.Asset as AudioClip);
|
||||
break;
|
||||
case Shader m_Shader:
|
||||
PreviewShader(m_Shader);
|
||||
case ClassIDType.Shader:
|
||||
PreviewShader(assetItem.Asset as Shader);
|
||||
break;
|
||||
case TextAsset m_TextAsset:
|
||||
PreviewTextAsset(m_TextAsset);
|
||||
case ClassIDType.TextAsset:
|
||||
PreviewTextAsset(assetItem.Asset as TextAsset);
|
||||
break;
|
||||
case MonoBehaviour m_MonoBehaviour:
|
||||
PreviewMonoBehaviour(m_MonoBehaviour);
|
||||
case ClassIDType.MonoBehaviour:
|
||||
PreviewMonoBehaviour(assetItem.Asset as MonoBehaviour);
|
||||
break;
|
||||
case Font m_Font:
|
||||
PreviewFont(m_Font);
|
||||
case ClassIDType.Font:
|
||||
PreviewFont(assetItem.Asset as Font);
|
||||
break;
|
||||
case Mesh m_Mesh:
|
||||
PreviewMesh(m_Mesh);
|
||||
case ClassIDType.Mesh:
|
||||
PreviewMesh(assetItem.Asset as Mesh);
|
||||
break;
|
||||
case VideoClip m_VideoClip:
|
||||
PreviewVideoClip(assetItem, m_VideoClip);
|
||||
case ClassIDType.VideoClip:
|
||||
PreviewVideoClip(assetItem, assetItem.Asset as VideoClip);
|
||||
break;
|
||||
case MovieTexture _:
|
||||
case ClassIDType.MovieTexture:
|
||||
StatusStripUpdate("Only supported export.");
|
||||
break;
|
||||
case Sprite m_Sprite:
|
||||
PreviewSprite(assetItem, m_Sprite);
|
||||
case ClassIDType.Sprite:
|
||||
PreviewSprite(assetItem, assetItem.Asset as Sprite);
|
||||
break;
|
||||
case Animator _:
|
||||
case ClassIDType.Animator:
|
||||
StatusStripUpdate("Can be exported to FBX file.");
|
||||
break;
|
||||
case AnimationClip _:
|
||||
case ClassIDType.AnimationClip:
|
||||
StatusStripUpdate("Can be exported with Animator or Objects");
|
||||
break;
|
||||
default:
|
||||
@@ -1026,7 +1045,7 @@ namespace AssetStudioGUI
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"Width: {m_VideoClip.Width}");
|
||||
sb.AppendLine($"Height: {m_VideoClip.Height}");
|
||||
sb.AppendLine($"Frame rate: {m_VideoClip.m_FrameRate}");
|
||||
sb.AppendLine($"Frame rate: {m_VideoClip.m_FrameRate:.0##}");
|
||||
sb.AppendLine($"Split alpha: {m_VideoClip.m_HasSplitAlpha}");
|
||||
assetItem.InfoText = sb.ToString();
|
||||
|
||||
@@ -1337,7 +1356,7 @@ namespace AssetStudioGUI
|
||||
|
||||
private void ResetForm()
|
||||
{
|
||||
Text = $"{Application.ProductName} v{Application.ProductVersion}";
|
||||
Text = guiTitle;
|
||||
assetsManager.Clear();
|
||||
assemblyLoader.Clear();
|
||||
exportableAssets.Clear();
|
||||
@@ -1809,10 +1828,11 @@ namespace AssetStudioGUI
|
||||
logger.ShowErrorMessage = toolStripMenuItem15.Checked;
|
||||
}
|
||||
|
||||
private void sceneTreeView_MouseClick(object sender, MouseEventArgs e)
|
||||
private void sceneTreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Right && sceneTreeView.Nodes.Count > 0)
|
||||
if (e.Button == MouseButtons.Right)
|
||||
{
|
||||
sceneTreeView.SelectedNode = e.Node;
|
||||
contextMenuStrip2.Show(sceneTreeView, e.Location.X, e.Location.Y);
|
||||
}
|
||||
}
|
||||
@@ -1921,6 +1941,109 @@ namespace AssetStudioGUI
|
||||
}
|
||||
}
|
||||
|
||||
private void selectRelatedAsset(object sender, EventArgs e)
|
||||
{
|
||||
var selectedItem = (ToolStripMenuItem)sender;
|
||||
var index = int.Parse(selectedItem.Name.Split('_')[0]);
|
||||
|
||||
assetListView.SelectedIndices.Clear();
|
||||
tabControl1.SelectedTab = tabPage2;
|
||||
var assetItem = assetListView.Items[index];
|
||||
assetItem.Selected = true;
|
||||
assetItem.EnsureVisible();
|
||||
}
|
||||
|
||||
private void selectAllRelatedAssets(object sender, EventArgs e)
|
||||
{
|
||||
var selectedNode = sceneTreeView.SelectedNode;
|
||||
var relatedAssets = visibleAssets.FindAll(x => x.TreeNode == selectedNode);
|
||||
if (relatedAssets.Count > 0)
|
||||
{
|
||||
assetListView.SelectedIndices.Clear();
|
||||
tabControl1.SelectedTab = tabPage2;
|
||||
foreach (var asset in relatedAssets)
|
||||
{
|
||||
var assetItem = assetListView.Items[assetListView.Items.IndexOf(asset)];
|
||||
assetItem.Selected = true;
|
||||
}
|
||||
assetListView.Items[assetListView.Items.IndexOf(relatedAssets[0])].EnsureVisible();
|
||||
}
|
||||
}
|
||||
|
||||
private void showRelatedAssetsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var selectedNode = sceneTreeView.SelectedNode;
|
||||
var relatedAssets = visibleAssets.FindAll(x => x.TreeNode == selectedNode);
|
||||
if (relatedAssets.Count == 0)
|
||||
{
|
||||
StatusStripUpdate("No related assets were found among the visible assets.");
|
||||
}
|
||||
}
|
||||
|
||||
private void contextMenuStrip2_Opening(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
var selectedNode = sceneTreeView.SelectedNode;
|
||||
var relatedAssets = visibleAssets.FindAll(x => x.TreeNode == selectedNode);
|
||||
showRelatedAssetsToolStripMenuItem.DropDownItems.Clear();
|
||||
if (relatedAssets.Count > 1)
|
||||
{
|
||||
var assetItem = new ToolStripMenuItem
|
||||
{
|
||||
CheckOnClick = false,
|
||||
Name = "selectAllRelatedAssetsToolStripMenuItem",
|
||||
Size = new Size(180, 22),
|
||||
Text = "Select all"
|
||||
};
|
||||
assetItem.Click += selectAllRelatedAssets;
|
||||
showRelatedAssetsToolStripMenuItem.DropDownItems.Add(assetItem);
|
||||
}
|
||||
foreach (var asset in relatedAssets)
|
||||
{
|
||||
var index = assetListView.Items.IndexOf(asset);
|
||||
var assetItem = new ToolStripMenuItem
|
||||
{
|
||||
CheckOnClick = false,
|
||||
Name = $"{index}_{asset.TypeString}",
|
||||
Size = new Size(180, 22),
|
||||
Text = $"({asset.TypeString}) {asset.Text}"
|
||||
};
|
||||
assetItem.Click += selectRelatedAsset;
|
||||
showRelatedAssetsToolStripMenuItem.DropDownItems.Add(assetItem);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
private void FMODinit()
|
||||
{
|
||||
|
||||
@@ -120,9 +120,6 @@
|
||||
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>312, 17</value>
|
||||
</metadata>
|
||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>432, 17</value>
|
||||
</metadata>
|
||||
<data name="fontPreviewBox.Text" xml:space="preserve">
|
||||
<value>abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWYZ
|
||||
1234567890.:,;'\"(!?)+-*/=
|
||||
@@ -141,6 +138,9 @@ The quick brown fox jumps over the lazy dog. 1234567890
|
||||
|
||||
The quick brown fox jumps over the lazy dog. 1234567890</value>
|
||||
</data>
|
||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>432, 17</value>
|
||||
</metadata>
|
||||
<metadata name="contextMenuStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>775, 21</value>
|
||||
</metadata>
|
||||
|
||||
67
AssetStudioGUI/Components/ConsoleWindow.cs
Normal file
67
AssetStudioGUI/Components/ConsoleWindow.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
164
AssetStudioGUI/Components/LnkReader.cs
Normal file
164
AssetStudioGUI/Components/LnkReader.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
156
AssetStudioGUI/ExportOptions.Designer.cs
generated
156
AssetStudioGUI/ExportOptions.Designer.cs
generated
@@ -45,6 +45,12 @@
|
||||
this.topng = new System.Windows.Forms.RadioButton();
|
||||
this.tobmp = new System.Windows.Forms.RadioButton();
|
||||
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.exportAllUvsAsDiffuseMaps = new System.Windows.Forms.CheckBox();
|
||||
this.exportBlendShape = new System.Windows.Forms.CheckBox();
|
||||
@@ -63,9 +69,11 @@
|
||||
this.castToBone = new System.Windows.Forms.CheckBox();
|
||||
this.exportAllNodes = new System.Windows.Forms.CheckBox();
|
||||
this.eulerFilter = new System.Windows.Forms.CheckBox();
|
||||
this.exportUvsTooltip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.optionTooltip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.panel1.SuspendLayout();
|
||||
this.l2dGroupBox.SuspendLayout();
|
||||
this.l2dMotionExportMethodPanel.SuspendLayout();
|
||||
this.groupBox2.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.scaleFactor)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.boneSize)).BeginInit();
|
||||
@@ -77,7 +85,7 @@
|
||||
this.OKbutton.Location = new System.Drawing.Point(381, 380);
|
||||
this.OKbutton.Name = "OKbutton";
|
||||
this.OKbutton.Size = new System.Drawing.Size(75, 23);
|
||||
this.OKbutton.TabIndex = 6;
|
||||
this.OKbutton.TabIndex = 4;
|
||||
this.OKbutton.Text = "OK";
|
||||
this.OKbutton.UseVisualStyleBackColor = true;
|
||||
this.OKbutton.Click += new System.EventHandler(this.OKbutton_Click);
|
||||
@@ -88,7 +96,7 @@
|
||||
this.Cancel.Location = new System.Drawing.Point(462, 380);
|
||||
this.Cancel.Name = "Cancel";
|
||||
this.Cancel.Size = new System.Drawing.Size(75, 23);
|
||||
this.Cancel.TabIndex = 7;
|
||||
this.Cancel.TabIndex = 5;
|
||||
this.Cancel.Text = "Cancel";
|
||||
this.Cancel.UseVisualStyleBackColor = true;
|
||||
this.Cancel.Click += new System.EventHandler(this.Cancel_Click);
|
||||
@@ -106,8 +114,8 @@
|
||||
this.groupBox1.Controls.Add(this.converttexture);
|
||||
this.groupBox1.Location = new System.Drawing.Point(12, 13);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Size = new System.Drawing.Size(301, 362);
|
||||
this.groupBox1.TabIndex = 9;
|
||||
this.groupBox1.Size = new System.Drawing.Size(301, 272);
|
||||
this.groupBox1.TabIndex = 1;
|
||||
this.groupBox1.TabStop = false;
|
||||
this.groupBox1.Text = "Export";
|
||||
//
|
||||
@@ -119,7 +127,7 @@
|
||||
this.exportSpriteWithAlphaMask.Location = new System.Drawing.Point(6, 150);
|
||||
this.exportSpriteWithAlphaMask.Name = "exportSpriteWithAlphaMask";
|
||||
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.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -131,7 +139,7 @@
|
||||
this.openAfterExport.Location = new System.Drawing.Point(6, 196);
|
||||
this.openAfterExport.Name = "openAfterExport";
|
||||
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.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -143,9 +151,9 @@
|
||||
this.restoreExtensionName.Location = new System.Drawing.Point(6, 63);
|
||||
this.restoreExtensionName.Name = "restoreExtensionName";
|
||||
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.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;
|
||||
//
|
||||
// assetGroupOptions
|
||||
@@ -161,7 +169,7 @@
|
||||
this.assetGroupOptions.Location = new System.Drawing.Point(6, 35);
|
||||
this.assetGroupOptions.Name = "assetGroupOptions";
|
||||
this.assetGroupOptions.Size = new System.Drawing.Size(165, 21);
|
||||
this.assetGroupOptions.TabIndex = 8;
|
||||
this.assetGroupOptions.TabIndex = 2;
|
||||
//
|
||||
// label6
|
||||
//
|
||||
@@ -169,7 +177,7 @@
|
||||
this.label6.Location = new System.Drawing.Point(6, 18);
|
||||
this.label6.Name = "label6";
|
||||
this.label6.Size = new System.Drawing.Size(127, 13);
|
||||
this.label6.TabIndex = 7;
|
||||
this.label6.TabIndex = 1;
|
||||
this.label6.Text = "Group exported assets by";
|
||||
//
|
||||
// convertAudio
|
||||
@@ -180,7 +188,7 @@
|
||||
this.convertAudio.Location = new System.Drawing.Point(6, 173);
|
||||
this.convertAudio.Name = "convertAudio";
|
||||
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.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -202,8 +210,7 @@
|
||||
this.towebp.Location = new System.Drawing.Point(201, 7);
|
||||
this.towebp.Name = "towebp";
|
||||
this.towebp.Size = new System.Drawing.Size(54, 17);
|
||||
this.towebp.TabIndex = 5;
|
||||
this.towebp.TabStop = true;
|
||||
this.towebp.TabIndex = 4;
|
||||
this.towebp.Text = "Webp";
|
||||
this.towebp.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -213,7 +220,7 @@
|
||||
this.totga.Location = new System.Drawing.Point(150, 7);
|
||||
this.totga.Name = "totga";
|
||||
this.totga.Size = new System.Drawing.Size(44, 17);
|
||||
this.totga.TabIndex = 2;
|
||||
this.totga.TabIndex = 3;
|
||||
this.totga.Text = "Tga";
|
||||
this.totga.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -223,7 +230,7 @@
|
||||
this.tojpg.Location = new System.Drawing.Point(97, 7);
|
||||
this.tojpg.Name = "tojpg";
|
||||
this.tojpg.Size = new System.Drawing.Size(48, 17);
|
||||
this.tojpg.TabIndex = 4;
|
||||
this.tojpg.TabIndex = 2;
|
||||
this.tojpg.Text = "Jpeg";
|
||||
this.tojpg.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -234,7 +241,7 @@
|
||||
this.topng.Location = new System.Drawing.Point(50, 7);
|
||||
this.topng.Name = "topng";
|
||||
this.topng.Size = new System.Drawing.Size(44, 17);
|
||||
this.topng.TabIndex = 3;
|
||||
this.topng.TabIndex = 1;
|
||||
this.topng.TabStop = true;
|
||||
this.topng.Text = "Png";
|
||||
this.topng.UseVisualStyleBackColor = true;
|
||||
@@ -245,7 +252,7 @@
|
||||
this.tobmp.Location = new System.Drawing.Point(3, 7);
|
||||
this.tobmp.Name = "tobmp";
|
||||
this.tobmp.Size = new System.Drawing.Size(46, 17);
|
||||
this.tobmp.TabIndex = 2;
|
||||
this.tobmp.TabIndex = 0;
|
||||
this.tobmp.Text = "Bmp";
|
||||
this.tobmp.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -257,10 +264,76 @@
|
||||
this.converttexture.Location = new System.Drawing.Point(6, 87);
|
||||
this.converttexture.Name = "converttexture";
|
||||
this.converttexture.Size = new System.Drawing.Size(116, 17);
|
||||
this.converttexture.TabIndex = 1;
|
||||
this.converttexture.TabIndex = 4;
|
||||
this.converttexture.Text = "Convert Texture2D";
|
||||
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
|
||||
//
|
||||
this.groupBox2.AutoSize = true;
|
||||
@@ -284,7 +357,7 @@
|
||||
this.groupBox2.Location = new System.Drawing.Point(313, 13);
|
||||
this.groupBox2.Name = "groupBox2";
|
||||
this.groupBox2.Size = new System.Drawing.Size(224, 362);
|
||||
this.groupBox2.TabIndex = 11;
|
||||
this.groupBox2.TabIndex = 3;
|
||||
this.groupBox2.TabStop = false;
|
||||
this.groupBox2.Text = "Fbx";
|
||||
//
|
||||
@@ -295,9 +368,9 @@
|
||||
this.exportAllUvsAsDiffuseMaps.Location = new System.Drawing.Point(6, 185);
|
||||
this.exportAllUvsAsDiffuseMaps.Name = "exportAllUvsAsDiffuseMaps";
|
||||
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.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.");
|
||||
this.exportAllUvsAsDiffuseMaps.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -309,7 +382,7 @@
|
||||
this.exportBlendShape.Location = new System.Drawing.Point(6, 138);
|
||||
this.exportBlendShape.Name = "exportBlendShape";
|
||||
this.exportBlendShape.Size = new System.Drawing.Size(114, 17);
|
||||
this.exportBlendShape.TabIndex = 22;
|
||||
this.exportBlendShape.TabIndex = 7;
|
||||
this.exportBlendShape.Text = "Export blendshape";
|
||||
this.exportBlendShape.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -321,7 +394,7 @@
|
||||
this.exportAnimations.Location = new System.Drawing.Point(6, 114);
|
||||
this.exportAnimations.Name = "exportAnimations";
|
||||
this.exportAnimations.Size = new System.Drawing.Size(109, 17);
|
||||
this.exportAnimations.TabIndex = 21;
|
||||
this.exportAnimations.TabIndex = 6;
|
||||
this.exportAnimations.Text = "Export animations";
|
||||
this.exportAnimations.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -336,7 +409,7 @@
|
||||
this.scaleFactor.Location = new System.Drawing.Point(83, 243);
|
||||
this.scaleFactor.Name = "scaleFactor";
|
||||
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.Value = new decimal(new int[] {
|
||||
1,
|
||||
@@ -350,7 +423,7 @@
|
||||
this.label5.Location = new System.Drawing.Point(6, 245);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Size = new System.Drawing.Size(64, 13);
|
||||
this.label5.TabIndex = 19;
|
||||
this.label5.TabIndex = 12;
|
||||
this.label5.Text = "ScaleFactor";
|
||||
//
|
||||
// fbxFormat
|
||||
@@ -363,7 +436,7 @@
|
||||
this.fbxFormat.Location = new System.Drawing.Point(77, 275);
|
||||
this.fbxFormat.Name = "fbxFormat";
|
||||
this.fbxFormat.Size = new System.Drawing.Size(61, 21);
|
||||
this.fbxFormat.TabIndex = 18;
|
||||
this.fbxFormat.TabIndex = 15;
|
||||
//
|
||||
// label4
|
||||
//
|
||||
@@ -371,7 +444,7 @@
|
||||
this.label4.Location = new System.Drawing.Point(6, 280);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(59, 13);
|
||||
this.label4.TabIndex = 17;
|
||||
this.label4.TabIndex = 14;
|
||||
this.label4.Text = "FBXFormat";
|
||||
//
|
||||
// fbxVersion
|
||||
@@ -388,7 +461,7 @@
|
||||
this.fbxVersion.Location = new System.Drawing.Point(77, 308);
|
||||
this.fbxVersion.Name = "fbxVersion";
|
||||
this.fbxVersion.Size = new System.Drawing.Size(47, 21);
|
||||
this.fbxVersion.TabIndex = 16;
|
||||
this.fbxVersion.TabIndex = 17;
|
||||
//
|
||||
// label3
|
||||
//
|
||||
@@ -396,7 +469,7 @@
|
||||
this.label3.Location = new System.Drawing.Point(6, 311);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(62, 13);
|
||||
this.label3.TabIndex = 15;
|
||||
this.label3.TabIndex = 16;
|
||||
this.label3.Text = "FBXVersion";
|
||||
//
|
||||
// boneSize
|
||||
@@ -428,7 +501,7 @@
|
||||
this.exportSkins.Location = new System.Drawing.Point(6, 90);
|
||||
this.exportSkins.Name = "exportSkins";
|
||||
this.exportSkins.Size = new System.Drawing.Size(83, 17);
|
||||
this.exportSkins.TabIndex = 8;
|
||||
this.exportSkins.TabIndex = 5;
|
||||
this.exportSkins.Text = "Export skins";
|
||||
this.exportSkins.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -438,7 +511,7 @@
|
||||
this.label1.Location = new System.Drawing.Point(26, 42);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(72, 13);
|
||||
this.label1.TabIndex = 7;
|
||||
this.label1.TabIndex = 2;
|
||||
this.label1.Text = "FilterPrecision";
|
||||
//
|
||||
// filterPrecision
|
||||
@@ -452,7 +525,7 @@
|
||||
this.filterPrecision.Location = new System.Drawing.Point(127, 40);
|
||||
this.filterPrecision.Name = "filterPrecision";
|
||||
this.filterPrecision.Size = new System.Drawing.Size(51, 20);
|
||||
this.filterPrecision.TabIndex = 6;
|
||||
this.filterPrecision.TabIndex = 3;
|
||||
this.filterPrecision.Value = new decimal(new int[] {
|
||||
25,
|
||||
0,
|
||||
@@ -465,7 +538,7 @@
|
||||
this.castToBone.Location = new System.Drawing.Point(6, 161);
|
||||
this.castToBone.Name = "castToBone";
|
||||
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.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -489,7 +562,7 @@
|
||||
this.eulerFilter.Location = new System.Drawing.Point(6, 22);
|
||||
this.eulerFilter.Name = "eulerFilter";
|
||||
this.eulerFilter.Size = new System.Drawing.Size(72, 17);
|
||||
this.eulerFilter.TabIndex = 3;
|
||||
this.eulerFilter.TabIndex = 1;
|
||||
this.eulerFilter.Text = "EulerFilter";
|
||||
this.eulerFilter.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -500,6 +573,7 @@
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.Cancel;
|
||||
this.ClientSize = new System.Drawing.Size(549, 416);
|
||||
this.Controls.Add(this.l2dGroupBox);
|
||||
this.Controls.Add(this.groupBox2);
|
||||
this.Controls.Add(this.groupBox1);
|
||||
this.Controls.Add(this.Cancel);
|
||||
@@ -516,6 +590,10 @@
|
||||
this.groupBox1.PerformLayout();
|
||||
this.panel1.ResumeLayout(false);
|
||||
this.panel1.PerformLayout();
|
||||
this.l2dGroupBox.ResumeLayout(false);
|
||||
this.l2dGroupBox.PerformLayout();
|
||||
this.l2dMotionExportMethodPanel.ResumeLayout(false);
|
||||
this.l2dMotionExportMethodPanel.PerformLayout();
|
||||
this.groupBox2.ResumeLayout(false);
|
||||
this.groupBox2.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.scaleFactor)).EndInit();
|
||||
@@ -559,8 +637,14 @@
|
||||
private System.Windows.Forms.CheckBox restoreExtensionName;
|
||||
private System.Windows.Forms.CheckBox openAfterExport;
|
||||
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.RadioButton towebp;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using AssetStudio;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AssetStudioGUI
|
||||
@@ -14,15 +15,8 @@ namespace AssetStudioGUI
|
||||
converttexture.Checked = Properties.Settings.Default.convertTexture;
|
||||
exportSpriteWithAlphaMask.Checked = Properties.Settings.Default.exportSpriteWithMask;
|
||||
convertAudio.Checked = Properties.Settings.Default.convertAudio;
|
||||
var str = Properties.Settings.Default.convertType.ToString();
|
||||
foreach (Control c in panel1.Controls)
|
||||
{
|
||||
if (c.Text == str)
|
||||
{
|
||||
((RadioButton)c).Checked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var defaultImageType = Properties.Settings.Default.convertType.ToString();
|
||||
((RadioButton)panel1.Controls.Cast<Control>().First(x => x.Text == defaultImageType)).Checked = true;
|
||||
openAfterExport.Checked = Properties.Settings.Default.openAfterExport;
|
||||
eulerFilter.Checked = Properties.Settings.Default.eulerFilter;
|
||||
filterPrecision.Value = Properties.Settings.Default.filterPrecision;
|
||||
@@ -36,7 +30,9 @@ namespace AssetStudioGUI
|
||||
scaleFactor.Value = Properties.Settings.Default.scaleFactor;
|
||||
fbxVersion.SelectedIndex = Properties.Settings.Default.fbxVersion;
|
||||
fbxFormat.SelectedIndex = Properties.Settings.Default.fbxFormat;
|
||||
|
||||
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)
|
||||
@@ -46,14 +42,8 @@ namespace AssetStudioGUI
|
||||
Properties.Settings.Default.convertTexture = converttexture.Checked;
|
||||
Properties.Settings.Default.exportSpriteWithMask = exportSpriteWithAlphaMask.Checked;
|
||||
Properties.Settings.Default.convertAudio = convertAudio.Checked;
|
||||
foreach (Control c in panel1.Controls)
|
||||
{
|
||||
if (((RadioButton)c).Checked)
|
||||
{
|
||||
Properties.Settings.Default.convertType = (ImageFormat)Enum.Parse(typeof(ImageFormat), c.Text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
var checkedImageType = (RadioButton)panel1.Controls.Cast<Control>().First(x => ((RadioButton)x).Checked);
|
||||
Properties.Settings.Default.convertType = (ImageFormat)Enum.Parse(typeof(ImageFormat), checkedImageType.Text);
|
||||
Properties.Settings.Default.openAfterExport = openAfterExport.Checked;
|
||||
Properties.Settings.Default.eulerFilter = eulerFilter.Checked;
|
||||
Properties.Settings.Default.filterPrecision = filterPrecision.Value;
|
||||
@@ -67,6 +57,9 @@ namespace AssetStudioGUI
|
||||
Properties.Settings.Default.scaleFactor = scaleFactor.Value;
|
||||
Properties.Settings.Default.fbxVersion = fbxVersion.SelectedIndex;
|
||||
Properties.Settings.Default.fbxFormat = fbxFormat.SelectedIndex;
|
||||
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();
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</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>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -1,5 +1,8 @@
|
||||
using AssetStudio;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AssetStudioGUI
|
||||
@@ -7,15 +10,122 @@ namespace AssetStudioGUI
|
||||
class GUILogger : ILogger
|
||||
{
|
||||
public bool ShowErrorMessage = false;
|
||||
private bool IsFileLoggerRunning = false;
|
||||
private string LoggerInitString;
|
||||
private string FileLogName;
|
||||
private string FileLogPath;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
//File logger
|
||||
if (_useFileLogger)
|
||||
{
|
||||
LogToFile(loggerEvent, message);
|
||||
}
|
||||
|
||||
//Console logger
|
||||
Console.WriteLine(FormatMessage(loggerEvent, message, toConsole: true));
|
||||
|
||||
//GUI logger
|
||||
switch (loggerEvent)
|
||||
{
|
||||
case LoggerEvent.Error:
|
||||
|
||||
62
AssetStudioGUI/Properties/Settings.Designer.cs
generated
62
AssetStudioGUI/Properties/Settings.Designer.cs
generated
@@ -12,7 +12,7 @@ namespace AssetStudioGUI.Properties {
|
||||
|
||||
|
||||
[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 {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
@@ -286,5 +286,65 @@ namespace AssetStudioGUI.Properties {
|
||||
this["exportSpriteWithMask"] = 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,5 +68,20 @@
|
||||
<Setting Name="exportSpriteWithMask" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</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>
|
||||
</SettingsFile>
|
||||
@@ -158,10 +158,13 @@ namespace AssetStudioGUI
|
||||
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
|
||||
var objectAssetItemDic = new Dictionary<Object, AssetItem>(objectCount);
|
||||
var containers = new List<(PPtr<Object>, string)>();
|
||||
int i = 0;
|
||||
allContainers.Clear();
|
||||
var i = 0;
|
||||
Progress.Reset();
|
||||
foreach (var assetsFile in assetsManager.assetsFileList)
|
||||
{
|
||||
var preloadTable = Array.Empty<PPtr<Object>>();
|
||||
|
||||
foreach (var asset in assetsFile.Objects)
|
||||
{
|
||||
var assetItem = new AssetItem(asset);
|
||||
@@ -170,6 +173,9 @@ namespace AssetStudioGUI
|
||||
var exportable = false;
|
||||
switch (asset)
|
||||
{
|
||||
case PreloadData m_PreloadData:
|
||||
preloadTable = m_PreloadData.m_Assets;
|
||||
break;
|
||||
case GameObject m_GameObject:
|
||||
assetItem.Text = m_GameObject.m_Name;
|
||||
break;
|
||||
@@ -187,7 +193,7 @@ namespace AssetStudioGUI
|
||||
break;
|
||||
case VideoClip m_VideoClip:
|
||||
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;
|
||||
exportable = true;
|
||||
break;
|
||||
@@ -226,17 +232,23 @@ namespace AssetStudioGUI
|
||||
productName = m_PlayerSettings.productName;
|
||||
break;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
case ResourceManager m_ResourceManager:
|
||||
foreach (var m_Container in m_ResourceManager.m_Container)
|
||||
@@ -259,7 +271,7 @@ namespace AssetStudioGUI
|
||||
Progress.Report(++i, objectCount);
|
||||
}
|
||||
}
|
||||
foreach ((var pptr, var container) in containers)
|
||||
foreach (var (pptr, container) in containers)
|
||||
{
|
||||
if (pptr.TryGet(out var obj))
|
||||
{
|
||||
@@ -275,12 +287,19 @@ namespace AssetStudioGUI
|
||||
|
||||
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...");
|
||||
|
||||
var treeNodeCollection = new List<TreeNode>();
|
||||
var treeNodeDictionary = new Dictionary<GameObject, GameObjectTreeNode>();
|
||||
var assetsFileCount = assetsManager.assetsFileList.Count;
|
||||
int j = 0;
|
||||
var j = 0;
|
||||
Progress.Reset();
|
||||
foreach (var assetsFile in assetsManager.assetsFileList)
|
||||
{
|
||||
@@ -348,7 +367,6 @@ namespace AssetStudioGUI
|
||||
Progress.Report(++j, assetsFileCount);
|
||||
}
|
||||
treeNodeDictionary.Clear();
|
||||
|
||||
objectAssetItemDic.Clear();
|
||||
|
||||
return (productName, treeNodeCollection);
|
||||
@@ -386,7 +404,6 @@ namespace AssetStudioGUI
|
||||
typeMap.Add(assetsFile.unityVersion, items);
|
||||
}
|
||||
}
|
||||
|
||||
return typeMap;
|
||||
}
|
||||
|
||||
@@ -745,6 +762,8 @@ namespace AssetStudioGUI
|
||||
public static void ExportLive2D(Object[] cubismMocs, string exportPath)
|
||||
{
|
||||
var baseDestPath = Path.Combine(exportPath, "Live2DOutput");
|
||||
var motionMode = Properties.Settings.Default.l2dMotionMode;
|
||||
var forceBezier = Properties.Settings.Default.l2dForceBezier;
|
||||
|
||||
ThreadPool.QueueUserWorkItem(state =>
|
||||
{
|
||||
@@ -753,48 +772,73 @@ namespace AssetStudioGUI
|
||||
var useFullContainerPath = false;
|
||||
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)
|
||||
{
|
||||
useFullContainerPath = true;
|
||||
}
|
||||
}
|
||||
var basePathList = useFullContainerPath ?
|
||||
cubismMocs.Select(x => allContainers[x]).ToList() :
|
||||
cubismMocs.Select(x => allContainers[x].Substring(0, allContainers[x].LastIndexOf("/"))).ToList();
|
||||
|
||||
var basePathList = cubismMocs.Select(x =>
|
||||
{
|
||||
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(
|
||||
x => basePathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
|
||||
x => x.Key
|
||||
);
|
||||
|
||||
var totalModelCount = lookup.LongCount(x => x.Key != null);
|
||||
var name = "";
|
||||
var modelCounter = 0;
|
||||
foreach (var assets in lookup)
|
||||
{
|
||||
var container = assets.Key;
|
||||
if (container == null)
|
||||
var srcContainer = assets.Key;
|
||||
if (srcContainer == null)
|
||||
continue;
|
||||
name = container;
|
||||
var container = srcContainer;
|
||||
|
||||
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{container}\"...");
|
||||
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer}\"...");
|
||||
try
|
||||
{
|
||||
var modelName = useFullContainerPath ? Path.GetFileNameWithoutExtension(container) : container.Substring(container.LastIndexOf('/') + 1);
|
||||
container = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container;
|
||||
var destPath = Path.Combine(baseDestPath, container) + Path.DirectorySeparatorChar;
|
||||
|
||||
ExtractLive2D(assets, destPath, modelName, assemblyLoader);
|
||||
ExtractLive2D(assets, destPath, modelName, assemblyLoader, motionMode, forceBezier);
|
||||
modelCounter++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error($"Live2D model export error: \"{name}\"", ex);
|
||||
Logger.Error($"Live2D model export error: \"{srcContainer}\"", ex);
|
||||
}
|
||||
Progress.Report(modelCounter, (int)totalModelCount);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
OpenFolderInExplorer(exportPath);
|
||||
|
||||
77
AssetStudioGUI/app.manifest
Normal file
77
AssetStudioGUI/app.manifest
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="AssetStudioMod.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<!-- UAC Manifest Options
|
||||
If you want to change the Windows User Account Control level replace the
|
||||
requestedExecutionLevel node with one of the following.
|
||||
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
|
||||
Specifying requestedExecutionLevel element will disable file and registry virtualization.
|
||||
Remove this element if your application requires this virtualization for backwards
|
||||
compatibility.
|
||||
-->
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- A list of the Windows versions that this application has been tested on
|
||||
and is designed to work with. Uncomment the appropriate elements
|
||||
and Windows will automatically select the most compatible environment. -->
|
||||
|
||||
<!-- Windows Vista -->
|
||||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||
|
||||
<!-- Windows 10 and Windows 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
|
||||
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
|
||||
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
|
||||
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config.
|
||||
|
||||
Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">system</dpiAwareness>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
|
||||
|
||||
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
|
||||
</assembly>
|
||||
@@ -33,7 +33,12 @@ namespace AssetStudio
|
||||
|
||||
public TypeDefinition GetTypeDefinition(string assemblyName, string fullName)
|
||||
{
|
||||
if (moduleDic.TryGetValue(assemblyName, out var module))
|
||||
moduleDic.TryGetValue(assemblyName, out var module);
|
||||
if (module == null && !assemblyName.Contains(".dll"))
|
||||
{
|
||||
moduleDic.TryGetValue(assemblyName + ".dll", out module);
|
||||
}
|
||||
if (module != null)
|
||||
{
|
||||
var typeDef = module.GetType(fullName);
|
||||
if (typeDef == null && assemblyName == "UnityEngine.dll")
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks>
|
||||
<Version>0.17.1.0</Version>
|
||||
<Copyright>Copyright © Perfare 2018-2022</Copyright>
|
||||
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2023</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" !$(TargetFramework.Contains('windows')) ">
|
||||
@@ -23,6 +22,11 @@
|
||||
<PackageReference Include="Kyaru.Texture2DDecoder">
|
||||
<Version>0.17.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
30
AssetStudioUtility/CubismLive2DExtractor/CubismFadeMotion.cs
Normal file
30
AssetStudioUtility/CubismLive2DExtractor/CubismFadeMotion.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AssetStudio;
|
||||
|
||||
@@ -73,7 +72,7 @@ namespace CubismLive2DExtractor
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -99,7 +98,7 @@ namespace CubismLive2DExtractor
|
||||
GetLive2dPath(binding, out var target, out var 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace CubismLive2DExtractor
|
||||
{
|
||||
@@ -18,24 +20,238 @@ namespace CubismLive2DExtractor
|
||||
public float Fps;
|
||||
public bool Loop;
|
||||
public bool AreBeziersRestricted;
|
||||
public float FadeInTime;
|
||||
public float FadeOutTime;
|
||||
public int CurveCount;
|
||||
public int TotalSegmentCount;
|
||||
public int TotalPointCount;
|
||||
public int UserDataCount;
|
||||
public int TotalUserDataSize;
|
||||
};
|
||||
}
|
||||
|
||||
public class SerializableCurve
|
||||
{
|
||||
public string Target;
|
||||
public string Id;
|
||||
public float FadeInTime;
|
||||
public float FadeOutTime;
|
||||
public List<float> Segments;
|
||||
};
|
||||
}
|
||||
|
||||
public class SerializableUserData
|
||||
{
|
||||
public float Time;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace CubismLive2DExtractor
|
||||
{
|
||||
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 destMotionPath = Path.Combine(destPath, "motions") + Path.DirectorySeparatorChar;
|
||||
@@ -26,20 +26,75 @@ namespace CubismLive2DExtractor
|
||||
Directory.CreateDirectory(destPath);
|
||||
Directory.CreateDirectory(destTexturePath);
|
||||
|
||||
var monoBehaviours = new List<MonoBehaviour>();
|
||||
var texture2Ds = new List<Texture2D>();
|
||||
var expressionList = new List<MonoBehaviour>();
|
||||
var fadeMotionList = new List<MonoBehaviour>();
|
||||
var gameObjects = new List<GameObject>();
|
||||
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)
|
||||
{
|
||||
switch (asset)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
case GameObject m_GameObject:
|
||||
gameObjects.Add(m_GameObject);
|
||||
@@ -50,15 +105,12 @@ namespace CubismLive2DExtractor
|
||||
}
|
||||
}
|
||||
|
||||
//physics
|
||||
var physics = monoBehaviours.FirstOrDefault(x =>
|
||||
if (textures.Count == 0)
|
||||
{
|
||||
if (x.m_Script.TryGet(out var m_Script))
|
||||
{
|
||||
return m_Script.m_ClassName == "CubismPhysicsController";
|
||||
}
|
||||
return false;
|
||||
});
|
||||
Logger.Warning($"No textures found for \"{modelName}\" model.");
|
||||
}
|
||||
|
||||
//physics
|
||||
if (physics != null)
|
||||
{
|
||||
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));
|
||||
//motion
|
||||
var motions = new SortedDictionary<string, JArray>();
|
||||
|
||||
//texture
|
||||
var textures = new SortedSet<string>();
|
||||
foreach (var texture2D in texture2Ds)
|
||||
if (motionMode == Live2DMotionMode.MonoBehaviour && fadeMotionList.Count > 0) //motion from MonoBehaviour
|
||||
{
|
||||
using (var image = texture2D.ConvertToImage(flip: true))
|
||||
Logger.Debug("Motion export method: MonoBehaviour (Fade motion)");
|
||||
Directory.CreateDirectory(destMotionPath);
|
||||
foreach (var fadeMotionMono in fadeMotionList)
|
||||
{
|
||||
textures.Add($"textures/{texture2D.m_Name}.png");
|
||||
using (var file = File.OpenWrite($"{destTexturePath}{texture2D.m_Name}.png"))
|
||||
var fadeMotionObj = fadeMotionMono.ToType();
|
||||
if (fadeMotionObj == null)
|
||||
{
|
||||
image.WriteToStream(file, ImageFormat.Png);
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
//motion
|
||||
var motions = new JArray();
|
||||
|
||||
if (gameObjects.Count > 0)
|
||||
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;
|
||||
while (rootTransform.m_Father.TryGet(out var m_Father))
|
||||
{
|
||||
@@ -114,117 +181,37 @@ namespace CubismLive2DExtractor
|
||||
{
|
||||
Directory.CreateDirectory(destMotionPath);
|
||||
}
|
||||
foreach (ImportedKeyframedAnimation animation in converter.AnimationList)
|
||||
foreach (var animation in converter.AnimationList)
|
||||
{
|
||||
var json = new CubismMotion3Json
|
||||
{
|
||||
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;
|
||||
var motionJson = new CubismMotion3Json(animation, forceBezier);
|
||||
|
||||
json.UserData = new CubismMotion3Json.SerializableUserData[animation.Events.Count];
|
||||
var totalUserDataSize = 0;
|
||||
for (var i = 0; i < animation.Events.Count; i++)
|
||||
var animName = animation.Name;
|
||||
if (motions.ContainsKey(animName))
|
||||
{
|
||||
var @event = animation.Events[i];
|
||||
json.UserData[i] = new CubismMotion3Json.SerializableUserData
|
||||
{
|
||||
Time = @event.time,
|
||||
Value = @event.value
|
||||
};
|
||||
totalUserDataSize += @event.value.Length;
|
||||
animName = $"{animName}_{animation.GetHashCode()}";
|
||||
|
||||
if (motions.ContainsKey(animName))
|
||||
continue;
|
||||
}
|
||||
json.Meta.TotalUserDataSize = totalUserDataSize;
|
||||
|
||||
motions.Add(new JObject
|
||||
{
|
||||
{ "Name", animation.Name },
|
||||
{ "File", $"motions/{animation.Name}.motion3.json" }
|
||||
});
|
||||
File.WriteAllText($"{destMotionPath}{animation.Name}.motion3.json", JsonConvert.SerializeObject(json, Formatting.Indented, new MyJsonConverter()));
|
||||
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()));
|
||||
}
|
||||
}
|
||||
if (motions.Count == 0)
|
||||
{
|
||||
Logger.Warning($"No motions found for \"{modelName}\" model.");
|
||||
}
|
||||
|
||||
//expression
|
||||
var expressions = new JArray();
|
||||
var monoBehaviourArray = monoBehaviours.Where(x => x.m_Name.EndsWith(".exp3")).ToArray();
|
||||
if (monoBehaviourArray.Length > 0)
|
||||
if (expressionList.Count > 0)
|
||||
{
|
||||
Directory.CreateDirectory(destExpressionPath);
|
||||
}
|
||||
foreach (var monoBehaviour in monoBehaviourArray)
|
||||
foreach (var monoBehaviour in expressionList)
|
||||
{
|
||||
var fullName = monoBehaviour.m_Name;
|
||||
var expressionName = fullName.Replace(".exp3", "");
|
||||
var expressionName = monoBehaviour.m_Name.Replace(".exp3", "");
|
||||
var expressionObj = monoBehaviour.ToType();
|
||||
if (expressionObj == null)
|
||||
{
|
||||
@@ -241,57 +228,38 @@ namespace CubismLive2DExtractor
|
||||
expressions.Add(new JObject
|
||||
{
|
||||
{ "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 eyeBlinkParameters = monoBehaviours.Where(x =>
|
||||
{
|
||||
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();
|
||||
//Try looking for group IDs among the gameObjects
|
||||
if (eyeBlinkParameters.Count == 0)
|
||||
{
|
||||
eyeBlinkParameters = gameObjects.Where(x =>
|
||||
{
|
||||
return x.m_Name.ToLower().Contains("eye")
|
||||
x.m_Name.ToLower().Contains("eye")
|
||||
&& x.m_Name.ToLower().Contains("open")
|
||||
&& (x.m_Name.ToLower().Contains('l') || x.m_Name.ToLower().Contains('r'));
|
||||
}).Select(x => x.m_Name).ToHashSet();
|
||||
&& (x.m_Name.ToLower().Contains('l') || x.m_Name.ToLower().Contains('r'))
|
||||
).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
|
||||
{
|
||||
Target = "Parameter",
|
||||
Name = "EyeBlink",
|
||||
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
|
||||
{
|
||||
Target = "Parameter",
|
||||
@@ -299,6 +267,7 @@ namespace CubismLive2DExtractor
|
||||
Ids = lipSyncParameters.ToArray()
|
||||
});
|
||||
|
||||
//model
|
||||
var model3 = new CubismModel3Json
|
||||
{
|
||||
Version = 3,
|
||||
@@ -307,7 +276,7 @@ namespace CubismLive2DExtractor
|
||||
{
|
||||
Moc = $"{modelName}.moc3",
|
||||
Textures = textures.ToArray(),
|
||||
Motions = new JObject { { "", motions } },
|
||||
Motions = JObject.FromObject(motions),
|
||||
Expressions = expressions,
|
||||
},
|
||||
Groups = groups.ToArray()
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace CubismLive2DExtractor
|
||||
{
|
||||
public enum Live2DMotionMode
|
||||
{
|
||||
MonoBehaviour,
|
||||
AnimationClip
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,11 @@ namespace AssetStudio
|
||||
{
|
||||
if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D) && m_Sprite.m_RD.alphaTexture.TryGet(out var m_AlphaTexture2D) && spriteMaskMode != SpriteMaskMode.Off)
|
||||
{
|
||||
var tex = CutImage(m_Sprite, m_Texture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw);
|
||||
Image<Bgra32> tex = null;
|
||||
if (spriteMaskMode != SpriteMaskMode.MaskOnly)
|
||||
{
|
||||
tex = CutImage(m_Sprite, m_Texture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw);
|
||||
}
|
||||
var alphaTex = CutImage(m_Sprite, m_AlphaTexture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw);
|
||||
|
||||
switch (spriteMaskMode)
|
||||
@@ -46,7 +50,6 @@ namespace AssetStudio
|
||||
tex.ApplyRGBMask(alphaTex);
|
||||
return tex;
|
||||
case SpriteMaskMode.MaskOnly:
|
||||
tex.Dispose();
|
||||
return alphaTex;
|
||||
}
|
||||
}
|
||||
@@ -89,92 +92,97 @@ namespace AssetStudio
|
||||
var originalImage = m_Texture2D.ConvertToImage(false);
|
||||
if (originalImage != null)
|
||||
{
|
||||
using (originalImage)
|
||||
if (downscaleMultiplier > 0f && downscaleMultiplier != 1f)
|
||||
{
|
||||
if (downscaleMultiplier > 0f && downscaleMultiplier != 1f)
|
||||
var width = (int)(m_Texture2D.m_Width / downscaleMultiplier);
|
||||
var height = (int)(m_Texture2D.m_Height / downscaleMultiplier);
|
||||
originalImage.Mutate(x => x.Resize(width, height));
|
||||
}
|
||||
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 (settingsRaw.packed == 1)
|
||||
{
|
||||
//RotateAndFlip
|
||||
switch (settingsRaw.packingRotation)
|
||||
{
|
||||
var width = (int)(m_Texture2D.m_Width / downscaleMultiplier);
|
||||
var height = (int)(m_Texture2D.m_Height / downscaleMultiplier);
|
||||
originalImage.Mutate(x => x.Resize(width, height));
|
||||
}
|
||||
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));
|
||||
if (settingsRaw.packed == 1)
|
||||
{
|
||||
//RotateAndFlip
|
||||
switch (settingsRaw.packingRotation)
|
||||
{
|
||||
case SpritePackingRotation.FlipHorizontal:
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Horizontal));
|
||||
break;
|
||||
case SpritePackingRotation.FlipVertical:
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||
break;
|
||||
case SpritePackingRotation.Rotate180:
|
||||
spriteImage.Mutate(x => x.Rotate(180));
|
||||
break;
|
||||
case SpritePackingRotation.Rotate90:
|
||||
spriteImage.Mutate(x => x.Rotate(270));
|
||||
break;
|
||||
}
|
||||
case SpritePackingRotation.FlipHorizontal:
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Horizontal));
|
||||
break;
|
||||
case SpritePackingRotation.FlipVertical:
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||
break;
|
||||
case SpritePackingRotation.Rotate180:
|
||||
spriteImage.Mutate(x => x.Rotate(180));
|
||||
break;
|
||||
case SpritePackingRotation.Rotate90:
|
||||
spriteImage.Mutate(x => x.Rotate(270));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Tight
|
||||
if (settingsRaw.packingMode == SpritePackingMode.Tight)
|
||||
//Tight
|
||||
if (settingsRaw.packingMode == SpritePackingMode.Tight)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
var matrix = Matrix3x2.CreateScale(m_Sprite.m_PixelsToUnits);
|
||||
matrix *= Matrix3x2.CreateTranslation(m_Sprite.m_Rect.width * m_Sprite.m_Pivot.X - textureRectOffset.X, m_Sprite.m_Rect.height * m_Sprite.m_Pivot.Y - textureRectOffset.Y);
|
||||
var triangles = GetTriangles(m_Sprite.m_RD);
|
||||
var points = triangles.Select(x => x.Select(y => new PointF(y.X, y.Y)).ToArray());
|
||||
var pathBuilder = new PathBuilder(matrix);
|
||||
foreach (var p in points)
|
||||
{
|
||||
var matrix = Matrix3x2.CreateScale(m_Sprite.m_PixelsToUnits);
|
||||
matrix *= Matrix3x2.CreateTranslation(m_Sprite.m_Rect.width * m_Sprite.m_Pivot.X - textureRectOffset.X, m_Sprite.m_Rect.height * m_Sprite.m_Pivot.Y - textureRectOffset.Y);
|
||||
var triangles = GetTriangles(m_Sprite.m_RD);
|
||||
var points = triangles.Select(x => x.Select(y => new PointF(y.X, y.Y)).ToArray());
|
||||
var pathBuilder = new PathBuilder(matrix);
|
||||
foreach (var p in points)
|
||||
pathBuilder.AddLines(p);
|
||||
pathBuilder.CloseFigure();
|
||||
}
|
||||
var path = pathBuilder.Build();
|
||||
var options = new DrawingOptions
|
||||
{
|
||||
GraphicsOptions = new GraphicsOptions
|
||||
{
|
||||
pathBuilder.AddLines(p);
|
||||
pathBuilder.CloseFigure();
|
||||
Antialias = false,
|
||||
AlphaCompositionMode = PixelAlphaCompositionMode.DestOut
|
||||
}
|
||||
var path = pathBuilder.Build();
|
||||
var options = new DrawingOptions
|
||||
};
|
||||
if (triangles.Length < 1024)
|
||||
{
|
||||
var rectP = new RectangularPolygon(0, 0, rect.Width, rect.Height);
|
||||
try
|
||||
{
|
||||
GraphicsOptions = new GraphicsOptions
|
||||
{
|
||||
Antialias = false,
|
||||
AlphaCompositionMode = PixelAlphaCompositionMode.DestOut
|
||||
}
|
||||
};
|
||||
if (triangles.Length < 1024)
|
||||
{
|
||||
var rectP = new RectangularPolygon(0, 0, rect.Width, rect.Height);
|
||||
spriteImage.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, rectP.Clip(path)));
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||
return spriteImage;
|
||||
}
|
||||
using (var mask = new Image<Bgra32>(rect.Width, rect.Height, SixLabors.ImageSharp.Color.Black))
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
mask.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, path));
|
||||
var brush = new ImageBrush(mask);
|
||||
spriteImage.Mutate(x => x.Fill(options, brush));
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||
return spriteImage;
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
catch
|
||||
using (var mask = new Image<Bgra32>(rect.Width, rect.Height, SixLabors.ImageSharp.Color.Black))
|
||||
{
|
||||
// ignored
|
||||
mask.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, path));
|
||||
var brush = new ImageBrush(mask);
|
||||
spriteImage.Mutate(x => x.Fill(options, brush));
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||
return spriteImage;
|
||||
}
|
||||
}
|
||||
|
||||
//Rectangle
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||
return spriteImage;
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warning($"{m_Sprite.m_Name} Unable to render the packed sprite correctly.\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
//Rectangle
|
||||
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
|
||||
return spriteImage;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,5 +1,40 @@
|
||||
# Changelog
|
||||
|
||||
## v0.17.4.0 [16-12-2023]
|
||||
- Added support for Live2D Fade motions
|
||||
- [GUI] Added related settings to the Export Options window
|
||||
- [CLI] Added options section with related settings
|
||||
- Added support for separate PreloadData (https://github.com/Perfare/AssetStudio/issues/690)
|
||||
- [GUI] Added support for .lnk files via Drag&Drop (https://github.com/aelurum/AssetStudio/issues/13)
|
||||
- [GUI] Added support for Drag&Drop files from a browser
|
||||
- [GUI] Added console logger
|
||||
- Added option to save log to a file
|
||||
- [GUI] Added option to not build a tree structure
|
||||
- Made some other minor fixes and improvements
|
||||
|
||||
## v0.17.3.0 [13-09-2023]
|
||||
- [CLI] Added support for exporting split objects (fbx) (https://github.com/aelurum/AssetStudio/pull/10)
|
||||
- [CLI] Fixed display of asset names in the exported asset list in some working modes
|
||||
- [CLI] Fixed a bug where the default output folder might not exist
|
||||
- Added support of Texture2D assets from Unity 2022.2+
|
||||
- Fixed AssemblyLoader (https://github.com/aelurum/AssetStudio/issues/6)
|
||||
- [CLI] Added --load-all flag to load assets of all types
|
||||
- [CLI] Improved option grouping on the help screen
|
||||
|
||||
## v0.17.2.0 [27-08-2023]
|
||||
- [GUI] Improved Scene Hierarchy tab
|
||||
- Added "Related assets" item to the context menu (https://github.com/aelurum/AssetStudio/issues/7)
|
||||
- [GUI] Added app.manifest for net472 build
|
||||
- Added long paths support (win10 v1607+)
|
||||
- Fixed blurring at high DPI with scaling
|
||||
- [CLI] Fixed sprite export in sprite only mode
|
||||
- Made some changes to motion list for live2d models
|
||||
- Motion list is now sorted
|
||||
- Motions divided into groups (each motion is a separate group)
|
||||
- Motion names are used as group names
|
||||
- Updated dependencies
|
||||
- Made some other minor fixes and improvements
|
||||
|
||||
## v0.17.1.0 [12-07-2023]
|
||||
#### Breaking Changes
|
||||
- With the drag&drop fix (https://github.com/aelurum/AssetStudio/commit/2f8f57c1a63893c0b0d2a55349d6cb6d8f8a5a3b), functions `LoadFiles` and `LoadFolder` in AssetsManager have been replaced with one universal function `LoadFilesAndFolders`
|
||||
|
||||
44
README.md
44
README.md
@@ -11,12 +11,12 @@ Unfortunately, I can't continue Perfare's work and keep AssetStudio up to date.
|
||||
|
||||
## Game specific modifications
|
||||
|
||||
- ArknightsStudio - soon<6F>
|
||||
- [ArknightsStudio](https://github.com/aelurum/AssetStudio/tree/ArknightsStudio)
|
||||
|
||||
## AssetStudio Features
|
||||
|
||||
- Support version:
|
||||
- 3.4 - 2022.1
|
||||
- 3.4 - 2022.3
|
||||
- Support asset types:
|
||||
- **Texture2D** : convert to png, tga, jpeg, bmp, webp
|
||||
- **Sprite** : crop Texture2D to png, tga, jpeg, bmp, webp
|
||||
@@ -36,7 +36,7 @@ Unfortunately, I can't continue Perfare's work and keep AssetStudio up to date.
|
||||
- `Animator` and `AnimationClip` assets are not supported in the CLI version
|
||||
- Support of sprites with alpha mask
|
||||
- Support of image export in WebP format
|
||||
- Support of Live2D Cubism 3 model export
|
||||
- Support of Live2D Cubism model export
|
||||
- Ported from my fork of Perfare's [UnityLive2DExtractor](https://github.com/aelurum/UnityLive2DExtractor)
|
||||
- Using the Live2D export in AssetStudio allows you to specify a Unity version and assembly folder if needed
|
||||
- Detecting bundles with UnityCN encryption
|
||||
@@ -54,6 +54,9 @@ Unfortunately, I can't continue Perfare's work and keep AssetStudio up to date.
|
||||
- AssetStudioMod.net7
|
||||
- GUI/CLI (Windows) - [.NET Desktop Runtime 7.0](https://dotnet.microsoft.com/download/dotnet/7.0)
|
||||
- CLI (Linux/Mac) - [.NET Runtime 7.0](https://dotnet.microsoft.com/download/dotnet/7.0)
|
||||
- AssetStudioMod.net8
|
||||
- GUI/CLI (Windows) - [.NET Desktop Runtime 8.0](https://dotnet.microsoft.com/download/dotnet/8.0)
|
||||
- CLI (Linux/Mac) - [.NET Runtime 8.0](https://dotnet.microsoft.com/download/dotnet/8.0)
|
||||
|
||||
## CLI Usage
|
||||
|
||||
@@ -70,7 +73,7 @@ You can read CLI readme [here](https://github.com/aelurum/AssetStudio/blob/Asset
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m info
|
||||
```
|
||||
- Export assets of all supported types
|
||||
- Export assets of all supported for export types
|
||||
```
|
||||
AssetStudioModCLI <asset folder path>
|
||||
```
|
||||
@@ -89,11 +92,21 @@ AssetStudioModCLI <asset folder path> -g type
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -o <output folder path>
|
||||
```
|
||||
- Dump assets to a specified output folder
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m dump -o <output folder path>
|
||||
```
|
||||
- Export Live2D Cubism models
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m live2d
|
||||
```
|
||||
> When running in live2d mode you can only specify `-o`, `--log-level`, `--log-output`, `--export-asset-list`, `--unity-version` and `--assembly-folder` options.
|
||||
> When running in live2d mode you can only specify `-o`, `--log-level`, `--log-output`, `--l2d-motion-mode`, `--l2d-force-bezier`, `--export-asset-list`, `--unity-version` and `--assembly-folder` options.
|
||||
Any other options will be ignored.
|
||||
- Export all FBX objects (similar to "Export all objects (split)" option in the GUI)
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m splitObjects
|
||||
```
|
||||
> 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.
|
||||
|
||||
### Advanced Samples
|
||||
@@ -109,6 +122,13 @@ AssetStudioModCLI <asset folder path> -m info -t audio --filter-by-name voice
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t audio --filter-by-name voice
|
||||
```
|
||||
- Export audio assets that have "music" or "voice" in their names
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t audio --filter-by-name music,voice
|
||||
```
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t audio --filter-by-name music --filter-by-name voice
|
||||
```
|
||||
- Export audio assets that have "char" in their names **or** containers
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t audio --filter-by-text char
|
||||
@@ -117,6 +137,10 @@ AssetStudioModCLI <asset folder path> -t audio --filter-by-text char
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t audio --filter-by-name voice --filter-by-container char
|
||||
```
|
||||
- Export FBX objects that have "model" or "scene" in their names and set the scale factor to 10
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m splitObjects --filter-by-name model,scene --fbx-scale-factor 10
|
||||
```
|
||||
- Export MonoBehaviour assets that require an assembly folder to read and create a log file
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -t monobehaviour --assembly-folder <assembly folder path> --log-output both
|
||||
@@ -125,6 +149,14 @@ AssetStudioModCLI <asset folder path> -t monobehaviour --assembly-folder <assemb
|
||||
```
|
||||
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)
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m info --load-all
|
||||
```
|
||||
- Load assets of all types and dump Material assets
|
||||
```
|
||||
AssetStudioModCLI <asset folder path> -m dump -t material --load-all
|
||||
```
|
||||
|
||||
## GUI Usage
|
||||
|
||||
@@ -140,7 +172,7 @@ Use **File->Extract file** or **File->Extract folder**.
|
||||
|
||||
### Export Assets, Live2D models
|
||||
|
||||
use **Export** menu.
|
||||
Use **Export** menu.
|
||||
|
||||
### Export Model
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>0.17.1.0</Version>
|
||||
<Version>0.17.4.0</Version>
|
||||
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
Reference in New Issue
Block a user