11 Commits

Author SHA1 Message Date
VaDiM
171962e61f Update version to v0.17.2
- Updated projects and dependencies
2023-08-27 01:47:06 +03:00
VaDiM
c8a21838c9 Some changes to motion list for l2d models
- Motion list is now sorted
- Motions divided into groups (each motion is a separate group)
- Motion names are used as group names
2023-08-27 01:46:06 +03:00
VaDiM
cf67815d53 Minor fixes 2023-08-26 03:22:19 +03:00
VaDiM
94c8b355fe [CLI] Fix asset filter
- Fixed sprite export in sprite only mode
2023-08-14 00:46:27 +03:00
VaDiM
4e41caf203 [CLI] Refactor 2023-08-13 20:58:26 +03:00
VaDiM
74a8555514 Merge branch 'relatedAssets' into AssetStudioMod 2023-08-07 23:20:11 +03:00
VaDiM
e1d883adf6 [CLI] Refactor
- Made some classes static
2023-08-07 23:10:57 +03:00
VaDiM
9784df0e16 [GUI] Add app.manifest for net472 build
- Added long paths support (win10 v1607+)
- Fixed blurring at high DPI with scaling
2023-08-06 20:08:05 +03:00
VaDiM
4d919a2bfe [GUI] Improve Scene Hierarchy tab context menu
- Added "Related assets" item to the context menu
2023-08-05 18:10:36 +03:00
VaDiM
6701f467b7 [CLI] Show the number of successfully loaded assets 2023-07-14 20:20:58 +03:00
VaDiM
50f5da5554 [GUI] Don't count Shaders in unity 2021+ assets 2023-07-14 19:17:56 +03:00
22 changed files with 607 additions and 371 deletions

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>0.17.1.0</Version>
<Version>0.17.2.0</Version>
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
<DebugType>embedded</DebugType>
</PropertyGroup>

View File

@@ -2,13 +2,13 @@
<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>
<Version>0.17.2.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' ">

View File

@@ -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,32 @@ 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.UnionWith(classIDTypes);
}
public void SetAssetFilter(List<ClassIDType> classIDTypeList)
{
foreach (ClassIDType classIDType in classIDTypeList)
SetAssetFilter(classIDType);
SetAssetFilter(classIDTypeList.ToArray());
}
public void LoadFilesAndFolders(params string[] path)

View File

@@ -5,7 +5,7 @@
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks>
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle>
<AssemblyName>AssetStudioModCLI</AssemblyName>
<Version>0.17.1.0</Version>
<Version>0.17.2.0</Version>
<Copyright>Copyright © Perfare; Copyright © aelurum 2023</Copyright>
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>embedded</DebugType>

View File

@@ -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,16 +21,17 @@ 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";
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)

View File

@@ -9,14 +9,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 +53,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 +62,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 +78,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 +109,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 +121,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 +146,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 +158,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 +186,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 +198,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 +206,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 +226,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 +247,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 +260,24 @@ 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 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;
@@ -365,18 +395,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 +414,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:

View File

@@ -55,59 +55,78 @@ 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> supportedAssetTypes;
//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;
//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<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;
public static Option<string> o_assemblyPath;
public static Option<string> o_unityVersion;
public static Option<bool> f_notRestoreExtensionName;
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>();
@@ -303,35 +322,10 @@ namespace AssetStudioCLI.Options
#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);
}
if (isFlag)
{
flagsDict.Add(name, desc);
}
else
{
optionsDict.Add(name, desc);
}
}
private void ParseArgs(string[] args)
{
var brightYellow = CLIAnsiColors.BrightYellow;
var brightRed = CLIAnsiColors.BrightRed;
@@ -658,6 +652,7 @@ namespace AssetStudioCLI.Options
if (Directory.Exists(value))
{
o_assemblyPath.Value = value;
Studio.assemblyLoader.Load(value);
}
else
{
@@ -703,8 +698,13 @@ namespace AssetStudioCLI.Options
return;
}
}
isParsed = true;
#endregion
if (!Studio.assemblyLoader.Loaded)
{
Studio.assemblyLoader.Loaded = true;
}
isParsed = true;
}
private static string[] ValueSplitter(string value)
@@ -713,7 +713,7 @@ namespace AssetStudioCLI.Options
return value.Split(separator);
}
private bool TryShowOptionDescription(string option, Dictionary<string, string> descDict)
private static bool TryShowOptionDescription(string option, Dictionary<string, string> descDict)
{
var optionDesc = descDict.Where(x => x.Key.Contains(option));
if (optionDesc.Any())
@@ -728,7 +728,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 +761,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 +786,7 @@ namespace AssetStudioCLI.Options
}
}
public void ShowCurrentOptions()
public static void ShowCurrentOptions()
{
var sb = new StringBuilder();
sb.AppendLine("[Current Options]");
@@ -821,7 +822,7 @@ namespace AssetStudioCLI.Options
sb.AppendLine(ShowCurrentFilter());
sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\"");
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
sb.AppendLine($"# Restore TextAsset extension: {!f_notRestoreExtensionName.Value}");
sb.AppendLine($"# Restore TextAsset Extension: {!f_notRestoreExtensionName.Value}");
break;
}
sb.AppendLine("======");

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

View File

@@ -8,52 +8,51 @@ 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 && CLIOptions.o_workMode.Value != WorkMode.ExportLive2D)
{
studio.FilterAssets();
Studio.FilterAssets();
}
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;
default:
studio.ExportAssets();
Studio.ExportAssets();
break;
}
}

View File

@@ -11,40 +11,30 @@ using Ansi = AssetStudioCLI.CLIAnsiColors;
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 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.SpecifyUnityVersion = CLIOptions.o_unityVersion.Value;
assetsManager.SetAssetFilter(CLIOptions.o_exportAssetTypes.Value);
assetsManager.LoadFilesAndFolders(options.inputPath);
assetsManager.LoadFilesAndFolders(CLIOptions.inputPath);
if (assetsManager.assetsFileList.Count == 0)
{
Logger.Warning("No Unity file can be loaded.");
@@ -57,7 +47,7 @@ namespace AssetStudioCLI
return isLoaded;
}
public void ParseAssets()
public static void ParseAssets()
{
Logger.Info("Parse assets...");
@@ -141,7 +131,7 @@ namespace AssetStudioCLI
assetItem.Text = assetItem.TypeString + assetItem.UniqueID;
}
isExportable = options.o_exportAssetTypes.Value.Contains(asset.type);
isExportable = CLIOptions.o_exportAssetTypes.Value.Contains(asset.type);
if (isExportable)
{
fileAssetsList.Add(assetItem);
@@ -158,14 +148,34 @@ namespace AssetStudioCLI
}
parsedAssetsList.AddRange(fileAssetsList);
fileAssetsList.Clear();
if (options.o_workMode.Value != WorkMode.ExportLive2D)
if (CLIOptions.o_workMode.Value != WorkMode.ExportLive2D)
{
containers.Clear();
}
}
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 ShowExportableAssetsInfo()
{
var exportableAssetsCountDict = new Dictionary<ClassIDType, int>();
string info = "";
@@ -198,7 +208,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 +218,53 @@ namespace AssetStudioCLI
}
}
public void FilterAssets()
public 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.ToString().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.ToString().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.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) ||
CLIOptions.o_filterByText.Value.Any(y => x.Container.ToString().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.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) &&
CLIOptions.o_filterByContainer.Value.Any(y => x.Container.ToString().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 +272,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 +320,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 +359,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 +372,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,9 +403,9 @@ namespace AssetStudioCLI
Logger.Info($"Finished exporting asset list with {parsedAssetsList.Count} items.");
}
public void ExportLive2D()
public static void ExportLive2D()
{
var baseDestPath = Path.Combine(options.o_outputFolder.Value, "Live2DOutput");
var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput");
var useFullContainerPath = false;
Progress.Reset();
@@ -461,7 +471,7 @@ namespace AssetStudioCLI
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);
}

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>0.17.1.0</Version>
<Version>0.17.2.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © hozuki 2020</Copyright>
<DebugType>embedded</DebugType>
</PropertyGroup>

View File

@@ -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

View File

@@ -7,13 +7,14 @@
<ApplicationIcon>Resources\as.ico</ApplicationIcon>
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle>
<AssemblyName>AssetStudioModGUI</AssemblyName>
<Version>0.17.1.0</Version>
<Version>0.17.2.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-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>
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
<PackageReference Include="OpenTK" Version="4.7.7" />
<PackageReference Include="OpenTK.Graphics" Version="4.8.0" />
<PackageReference Include="OpenTK.Windowing.Desktop" Version="4.8.0" />
<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' ">

View File

@@ -125,6 +125,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();
@@ -526,7 +528,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);
//
@@ -620,7 +622,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 +631,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 +1117,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
//
@@ -1286,7 +1303,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 +1402,9 @@
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;
}
}

View File

@@ -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
@@ -267,7 +267,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 +502,10 @@ namespace AssetStudioGUI
switch (e.TabPageIndex)
{
case 0:
treeSearch.Select();
sceneTreeView.Select();
break;
case 1:
listSearch.Select();
assetListView.Select();
break;
}
}
@@ -782,42 +785,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 +1029,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();
@@ -1809,10 +1812,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 +1925,77 @@ 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);
}
}
#region FMOD
private void FMODinit()
{

View File

@@ -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>

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

View File

@@ -2,15 +2,14 @@
<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>
<Version>0.17.2.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="1.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
</ItemGroup>
<ItemGroup>

View File

@@ -99,7 +99,7 @@ namespace CubismLive2DExtractor
}
//motion
var motions = new JArray();
var motions = new SortedDictionary<string, JArray>();
if (gameObjects.Count > 0)
{
@@ -205,11 +205,8 @@ namespace CubismLive2DExtractor
}
json.Meta.TotalUserDataSize = totalUserDataSize;
motions.Add(new JObject
{
{ "Name", animation.Name },
{ "File", $"motions/{animation.Name}.motion3.json" }
});
var motionPath = new JObject(new JProperty("File", $"motions/{animation.Name}.motion3.json"));
motions.Add(animation.Name, new JArray(motionPath));
File.WriteAllText($"{destMotionPath}{animation.Name}.motion3.json", JsonConvert.SerializeObject(json, Formatting.Indented, new MyJsonConverter()));
}
}
@@ -307,7 +304,7 @@ namespace CubismLive2DExtractor
{
Moc = $"{modelName}.moc3",
Textures = textures.ToArray(),
Motions = new JObject { { "", motions } },
Motions = JObject.FromObject(motions),
Expressions = expressions,
},
Groups = groups.ToArray()

View File

@@ -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,90 @@ 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));
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;
}
}
//Tight
if (settingsRaw.packingMode == SpritePackingMode.Tight)
{
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)
{
pathBuilder.AddLines(p);
pathBuilder.CloseFigure();
}
var path = pathBuilder.Build();
var options = new DrawingOptions
{
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))
{
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;
}
}
catch
{
// ignored
}
}
//Rectangle
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
return spriteImage;
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)
{
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)
{
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)
{
pathBuilder.AddLines(p);
pathBuilder.CloseFigure();
}
var path = pathBuilder.Build();
var options = new DrawingOptions
{
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))
{
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;
}
}
catch
{
// ignored
}
}
//Rectangle
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
return spriteImage;
}
return null;

View File

@@ -1,5 +1,19 @@
# Changelog
## 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`

View File

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