26 Commits

Author SHA1 Message Date
VaDiM
6d3875cb2c Update version to v0.17.3 2023-09-13 10:55:57 +03:00
VaDiM
be4ced77ea Update readmes 2023-09-12 03:44:18 +03:00
VaDiM
2bd762e8f4 [CLI] Add names for AssetBundle and Animator assets 2023-09-12 03:33:59 +03:00
VaDiM
a926644ff6 [CLI] Add --load-all flag
- Added --load-all flag to load assets of all types
- Improved option grouping on the help screen
2023-09-12 03:33:53 +03:00
VaDiM
a4cdff5934 Fix a bit of incorrect copy-paste
in commit e216abd6be
2023-09-08 23:47:26 +03:00
VaDiM
2e10e627b0 AssemblyLoader fix (#6) 2023-09-08 06:18:41 +03:00
VaDiM
e216abd6be Unity 2022.2+ Texture2D fix 2023-09-08 06:18:34 +03:00
VaDiM
93c7e617d8 [CLI] Show names of all loaded assets in the exported asset list 2023-09-08 06:18:34 +03:00
VaDiM
000916913e [CLI] Fix a bug where the default output folder might not exist 2023-09-08 06:17:34 +03:00
VaDiM
28f9744497 Update README.md 2023-09-08 06:17:34 +03:00
VaDiM
c3f99216b6 [CLI] Add --filter-by-name support for fbx export mode 2023-09-08 01:45:25 +03:00
VaDiM
19c4835ea3 Merge branch 'pr/10' into AssetStudioMod 2023-09-05 19:38:04 +03:00
VaDiM
6b321da695 [CLI] Add FBX native libs (Linux, Mac) 2023-09-05 00:48:44 +03:00
svn
7cca301f7a Add split object fbx export to CLI 2023-08-31 21:07:02 +01:00
svn
5ac597c935 Add CMakeLists for cross platform FBXNative build 2023-08-31 21:03:07 +01:00
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
33 changed files with 1285 additions and 519 deletions

View File

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

View File

@@ -2,13 +2,13 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks> <TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks>
<Version>0.17.1.0</Version> <Version>0.17.3.0</Version>
<Copyright>Copyright © Perfare 2018-2022</Copyright> <Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2023</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' "> <ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.5" /> <PackageReference Include="K4os.Compression.LZ4" Version="1.3.6" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">

View File

@@ -12,7 +12,7 @@ namespace AssetStudio
{ {
public string SpecifyUnityVersion; public string SpecifyUnityVersion;
public List<SerializedFile> assetsFileList = new List<SerializedFile>(); 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, int> assetsFileIndexCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
internal Dictionary<string, BinaryReader> resourceFileReaders = new Dictionary<string, BinaryReader>(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> noexistFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private HashSet<string> assetsFileListHash = 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) if (filteredAssetTypesList.Count == 0)
{ {
filteredAssetTypesList.AddRange(new List<ClassIDType> filteredAssetTypesList.UnionWith(new HashSet<ClassIDType>
{ {
ClassIDType.AssetBundle, ClassIDType.AssetBundle,
ClassIDType.ResourceManager, ClassIDType.ResourceManager,
}); });
} }
if (classIDType == ClassIDType.MonoBehaviour) if (classIDTypes.Contains(ClassIDType.MonoBehaviour))
{ {
filteredAssetTypesList.AddRange(new List<ClassIDType> filteredAssetTypesList.Add(ClassIDType.MonoScript);
{
ClassIDType.MonoScript,
ClassIDType.MonoBehaviour
});
} }
else if (classIDTypes.Contains(ClassIDType.Sprite))
{ {
filteredAssetTypesList.Add(classIDType); filteredAssetTypesList.Add(ClassIDType.Texture2D);
} }
filteredAssetTypesList.UnionWith(classIDTypes);
} }
public void SetAssetFilter(List<ClassIDType> classIDTypeList) public void SetAssetFilter(List<ClassIDType> classIDTypeList)
{ {
foreach (ClassIDType classIDType in classIDTypeList) SetAssetFilter(classIDTypeList.ToArray());
SetAssetFilter(classIDType);
} }
public void LoadFilesAndFolders(params string[] path) public void LoadFilesAndFolders(params string[] path)

View File

@@ -91,7 +91,14 @@ namespace AssetStudio
} }
if (version[0] > 2019 || (version[0] == 2019 && version[1] >= 3)) //2019.3 and up 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 if (version[0] >= 3) //3.0.0 - 5.4
{ {
@@ -100,6 +107,11 @@ namespace AssetStudio
var m_ReadAllowed = reader.ReadBoolean(); 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 if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
{ {
var m_StreamingMipmaps = reader.ReadBoolean(); var m_StreamingMipmaps = reader.ReadBoolean();

View File

@@ -5,7 +5,7 @@
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks>
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle> <AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle>
<AssemblyName>AssetStudioModCLI</AssemblyName> <AssemblyName>AssetStudioModCLI</AssemblyName>
<Version>0.17.1.0</Version> <Version>0.17.3.0</Version>
<Copyright>Copyright © Perfare; Copyright © aelurum 2023</Copyright> <Copyright>Copyright © Perfare; Copyright © aelurum 2023</Copyright>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
@@ -24,6 +24,8 @@
<Target Name="CopyExtraFilesPortable" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' "> <Target Name="CopyExtraFilesPortable" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' ">
<Message Text="Copying windows extra files for $(TargetFramework)... " Importance="high" /> <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\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="$(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" /> <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' "> <Target Name="CopyExtraFilesPortableNet" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' AND '$(TargetFramework)' != 'net472' ">
<Message Text="Copying other platforms extra files for $(TargetFramework)... " Importance="high" /> <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-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\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" /> <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 --> The dll is cross-platform while the executable isn't -->
<Target Name="PublishExtraFilesPortable" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' "> <Target Name="PublishExtraFilesPortable" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' ">
<Message Text="Publishing windows extra files for Portable build ($(TargetFramework))... " Importance="high" /> <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-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-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" /> <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' "> <Target Name="PublishExtraFilesPortableNet" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' AND '$(TargetFramework)' != 'net472' ">
<Message Text="Publishing other platforms extra files for Portable build ($(TargetFramework))... " Importance="high" /> <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-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\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" /> <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' "> <Target Name="CopyExtraFilesWin86" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x86' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" /> <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="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x86\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" /> <Copy SourceFiles="$(ProjectDir)Libraries\win-x86\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target> </Target>
<Target Name="CopyExtraFilesWin64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x64' "> <Target Name="CopyExtraFilesWin64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" /> <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="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x64\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" /> <Copy SourceFiles="$(ProjectDir)Libraries\win-x64\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target> </Target>
<Target Name="PublishExtraFilesWin" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('win-x')) "> <Target Name="PublishExtraFilesWin" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('win-x')) ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" /> <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)\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\fmod.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" /> <Copy SourceFiles="$(TargetDir)\fmod.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target> </Target>
<Target Name="CopyExtraFilesLinux64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' "> <Target Name="CopyExtraFilesLinux64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" /> <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" /> <Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libfmod.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target> </Target>
<Target Name="PublishExtraFilesLinux64" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' "> <Target Name="PublishExtraFilesLinux64" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" /> <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" /> <Copy SourceFiles="$(TargetDir)\libfmod.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target> </Target>
<Target Name="CopyExtraFilesMac" AfterTargets="AfterBuild" Condition=" $(RuntimeIdentifier.Contains('osx-')) "> <Target Name="CopyExtraFilesMac" AfterTargets="AfterBuild" Condition=" $(RuntimeIdentifier.Contains('osx-')) ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" /> <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" /> <Copy SourceFiles="$(ProjectDir)Libraries\$(RuntimeIdentifier)\libfmod.dylib" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target> </Target>
<Target Name="PublishExtraFilesMac" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('osx-')) "> <Target Name="PublishExtraFilesMac" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('osx-')) ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" /> <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" /> <Copy SourceFiles="$(TargetDir)\libfmod.dylib" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target> </Target>

View File

@@ -1,9 +1,9 @@
using AssetStudio; using AssetStudio;
using AssetStudioCLI.Options;
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using AssetStudioCLI.Options;
namespace AssetStudioCLI namespace AssetStudioCLI
{ {
@@ -21,16 +21,17 @@ namespace AssetStudioCLI
public string LogName; public string LogName;
public string LogPath; public string LogPath;
public CLILogger(CLIOptions options) public CLILogger()
{ {
logOutput = options.o_logOutput.Value; logOutput = CLIOptions.o_logOutput.Value;
logMinLevel = options.o_logLevel.Value; logMinLevel = CLIOptions.o_logLevel.Value;
LogName = $"AssetStudioCLI_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log"; 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); LogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogName);
var arch = Environment.Is64BitProcess ? "x64" : "x32";
var ver = typeof(Program).Assembly.GetName().Version; LogToFile(LoggerEvent.Verbose, $"---{appAssembly.Name} v{appAssembly.Version} [{arch}] | Logger launched---\n" +
LogToFile(LoggerEvent.Verbose, $"---AssetStudioCLI v{ver} | Logger launched---\n" + $"CMD Args: {string.Join(" ", CLIOptions.cliArgs)}");
$"CMD Args: {string.Join(" ", options.cliArgs)}");
} }
private static string ColorLogLevel(LoggerEvent logLevel) private static string ColorLogLevel(LoggerEvent logLevel)

View File

@@ -13,6 +13,7 @@ namespace AssetStudioCLI
public ClassIDType Type; public ClassIDType Type;
public string Text; public string Text;
public string UniqueID; public string UniqueID;
public GameObjectNode Node;
public AssetItem(Object asset) public AssetItem(Object asset)
{ {

View 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()
{
}
}
}

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

View File

@@ -1,6 +1,7 @@
using AssetStudio; using AssetStudio;
using AssetStudioCLI.Options; using AssetStudioCLI.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -9,14 +10,38 @@ namespace AssetStudioCLI
{ {
internal static class Exporter 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; 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)) if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false; 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); var image = m_Texture2D.ConvertToImage(flip: true);
if (image == null) if (image == null)
{ {
@@ -29,7 +54,7 @@ namespace AssetStudioCLI
{ {
image.WriteToStream(file, type); image.WriteToStream(file, type);
} }
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\""); Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true; return true;
} }
} }
@@ -38,12 +63,12 @@ namespace AssetStudioCLI
if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath)) if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath))
return false; return false;
File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData()); 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; return true;
} }
} }
public static bool ExportAudioClip(AssetItem item, string exportPath, CLIOptions options) public static bool ExportAudioClip(AssetItem item, string exportPath)
{ {
string exportFullPath; string exportFullPath;
var m_AudioClip = (AudioClip)item.Asset; var m_AudioClip = (AudioClip)item.Asset;
@@ -54,18 +79,21 @@ namespace AssetStudioCLI
return false; return false;
} }
var converter = new AudioClipConverter(m_AudioClip); 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)) if (!TryExportFile(exportPath, item, ".wav", out exportFullPath))
return false; return false;
var sb = new StringBuilder(); if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
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}"); var sb = new StringBuilder();
sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); sb.AppendLine($"Converting \"{m_AudioClip.m_Name}\" to wav..");
sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); sb.AppendLine(m_AudioClip.version[0] < 5 ? $"AudioClip type: {m_AudioClip.m_Type}" : $"AudioClip compression format: {m_AudioClip.m_CompressionFormat}");
sb.AppendLine($"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}"); sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}");
Logger.Debug(sb.ToString()); 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); var buffer = converter.ConvertToWav(m_AudioData);
if (buffer == null) if (buffer == null)
@@ -82,7 +110,7 @@ namespace AssetStudioCLI
File.WriteAllBytes(exportFullPath, m_AudioData); 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; return true;
} }
@@ -94,16 +122,19 @@ namespace AssetStudioCLI
if (!TryExportFile(exportPath, item, Path.GetExtension(m_VideoClip.m_OriginalPath), out var exportFullPath)) if (!TryExportFile(exportPath, item, Path.GetExtension(m_VideoClip.m_OriginalPath), out var exportFullPath))
return false; return false;
var sb = new StringBuilder(); if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
sb.AppendLine($"VideoClip format: {m_VideoClip.m_Format}"); {
sb.AppendLine($"VideoClip width: {m_VideoClip.Width}"); var sb = new StringBuilder();
sb.AppendLine($"VideoClip height: {m_VideoClip.Height}"); sb.AppendLine($"VideoClip format: {m_VideoClip.m_Format}");
sb.AppendLine($"VideoClip frame rate: {m_VideoClip.m_FrameRate}"); sb.AppendLine($"VideoClip width: {m_VideoClip.Width}");
sb.AppendLine($"VideoClip split alpha: {m_VideoClip.m_HasSplitAlpha}"); sb.AppendLine($"VideoClip height: {m_VideoClip.Height}");
Logger.Debug(sb.ToString()); 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); 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 true;
} }
return false; return false;
@@ -116,7 +147,7 @@ namespace AssetStudioCLI
return false; return false;
File.WriteAllBytes(exportFullPath, m_MovieTexture.m_MovieData); 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; return true;
} }
@@ -128,16 +159,16 @@ namespace AssetStudioCLI
var str = m_Shader.Convert(); var str = m_Shader.Convert();
File.WriteAllText(exportFullPath, str); 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 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 m_TextAsset = (TextAsset)item.Asset;
var extension = ".txt"; var extension = ".txt";
var assetExtension = Path.GetExtension(m_TextAsset.m_Name); var assetExtension = Path.GetExtension(m_TextAsset.m_Name);
if (!options.f_notRestoreExtensionName.Value) if (!CLIOptions.f_notRestoreExtensionName.Value)
{ {
if (!string.IsNullOrEmpty(assetExtension)) if (!string.IsNullOrEmpty(assetExtension))
{ {
@@ -156,11 +187,11 @@ namespace AssetStudioCLI
return false; return false;
File.WriteAllBytes(exportFullPath, m_TextAsset.m_Script); 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; 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)) if (!TryExportFile(exportPath, item, ".json", out var exportFullPath))
return false; return false;
@@ -168,7 +199,7 @@ namespace AssetStudioCLI
var type = m_MonoBehaviour.ToType(); var type = m_MonoBehaviour.ToType();
if (type == null) if (type == null)
{ {
var m_Type = m_MonoBehaviour.ConvertToTypeTree(assemblyLoader); var m_Type = m_MonoBehaviour.ConvertToTypeTree(Studio.assemblyLoader);
type = m_MonoBehaviour.ToType(m_Type); type = m_MonoBehaviour.ToType(m_Type);
} }
if (type != null) if (type != null)
@@ -176,7 +207,7 @@ namespace AssetStudioCLI
var str = JsonConvert.SerializeObject(type, Formatting.Indented); var str = JsonConvert.SerializeObject(type, Formatting.Indented);
File.WriteAllText(exportFullPath, str); 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 true;
} }
return false; return false;
@@ -196,15 +227,15 @@ namespace AssetStudioCLI
return false; return false;
File.WriteAllBytes(exportFullPath, m_Font.m_FontData); 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 true;
} }
return false; 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; var alphaMask = SpriteMaskMode.On;
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath)) if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false; return false;
@@ -217,7 +248,7 @@ namespace AssetStudioCLI
{ {
image.WriteToStream(file, type); image.WriteToStream(file, type);
} }
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\""); Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true; return true;
} }
} }
@@ -230,24 +261,52 @@ namespace AssetStudioCLI
return false; return false;
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData()); 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; 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)) if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath))
return false; return false;
var str = item.Asset.Dump(); var str = item.Asset.Dump();
if (str == null && item.Asset is MonoBehaviour m_MonoBehaviour) 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); str = m_MonoBehaviour.Dump(m_Type);
} }
if (str != null) if (str != null)
{ {
File.WriteAllText(exportFullPath, str); 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 true;
} }
return false; return false;
@@ -365,18 +424,18 @@ namespace AssetStudioCLI
sb.Replace("NaN", "0"); sb.Replace("NaN", "0");
File.WriteAllText(exportFullPath, sb.ToString()); 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; 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) switch (item.Type)
{ {
case ClassIDType.Texture2D: case ClassIDType.Texture2D:
return ExportTexture2D(item, exportPath, options); return ExportTexture2D(item, exportPath);
case ClassIDType.AudioClip: case ClassIDType.AudioClip:
return ExportAudioClip(item, exportPath, options); return ExportAudioClip(item, exportPath);
case ClassIDType.VideoClip: case ClassIDType.VideoClip:
return ExportVideoClip(item, exportPath); return ExportVideoClip(item, exportPath);
case ClassIDType.MovieTexture: case ClassIDType.MovieTexture:
@@ -384,13 +443,13 @@ namespace AssetStudioCLI
case ClassIDType.Shader: case ClassIDType.Shader:
return ExportShader(item, exportPath); return ExportShader(item, exportPath);
case ClassIDType.TextAsset: case ClassIDType.TextAsset:
return ExportTextAsset(item, exportPath, options); return ExportTextAsset(item, exportPath);
case ClassIDType.MonoBehaviour: case ClassIDType.MonoBehaviour:
return ExportMonoBehaviour(item, exportPath, assemblyLoader); return ExportMonoBehaviour(item, exportPath);
case ClassIDType.Font: case ClassIDType.Font:
return ExportFont(item, exportPath); return ExportFont(item, exportPath);
case ClassIDType.Sprite: case ClassIDType.Sprite:
return ExportSprite(item, exportPath, options); return ExportSprite(item, exportPath);
case ClassIDType.Mesh: case ClassIDType.Mesh:
return ExportMesh(item, exportPath); return ExportMesh(item, exportPath);
default: default:

View File

@@ -12,6 +12,8 @@ namespace AssetStudioCLI.Options
General, General,
Convert, Convert,
Logger, Logger,
FBX,
Filter,
Advanced, Advanced,
} }
@@ -22,6 +24,7 @@ namespace AssetStudioCLI.Options
Dump, Dump,
Info, Info,
ExportLive2D, ExportLive2D,
SplitObjects,
} }
internal enum AssetGroupOption internal enum AssetGroupOption
@@ -55,65 +58,90 @@ namespace AssetStudioCLI.Options
NameAndContainer, 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) public static bool isParsed;
{ public static bool showHelp;
CLIOptions.OptionGrouping(optionName, optionDescription, optionHelpGroup, isFlag); public static string[] cliArgs;
} public static string inputPath;
} public static FilterBy filterBy;
internal class CLIOptions
{
public bool isParsed;
public bool showHelp;
public string[] cliArgs;
public string inputPath;
public FilterBy filterBy;
private static Dictionary<string, string> optionsDict; private static Dictionary<string, string> optionsDict;
private static Dictionary<string, string> flagsDict; private static Dictionary<string, string> flagsDict;
private static Dictionary<HelpGroups, Dictionary<string, string>> optionGroups; private static Dictionary<HelpGroups, Dictionary<string, string>> optionGroups;
private List<ClassIDType> supportedAssetTypes; private static List<ClassIDType> exportableAssetTypes;
private static Dictionary<string, ClassIDType> knownAssetTypesDict;
//general //general
public Option<WorkMode> o_workMode; public static Option<WorkMode> o_workMode;
public Option<List<ClassIDType>> o_exportAssetTypes; public static Option<List<ClassIDType>> o_exportAssetTypes;
public Option<AssetGroupOption> o_groupAssetsBy; public static Option<AssetGroupOption> o_groupAssetsBy;
public Option<string> o_outputFolder; public static Option<string> o_outputFolder;
public Option<bool> o_displayHelp; public static Option<bool> o_displayHelp;
//logger //logger
public Option<LoggerEvent> o_logLevel; public static Option<LoggerEvent> o_logLevel;
public Option<LogOutputMode> o_logOutput; public static Option<LogOutputMode> o_logOutput;
//convert //convert
public bool convertTexture; public static bool convertTexture;
public Option<ImageFormat> o_imageFormat; public static Option<ImageFormat> o_imageFormat;
public Option<AudioFormat> o_audioFormat; public static Option<AudioFormat> o_audioFormat;
//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 //advanced
public Option<ExportListType> o_exportAssetList; public static Option<ExportListType> o_exportAssetList;
public Option<List<string>> o_filterByName; public static Option<string> o_assemblyPath;
public Option<List<string>> o_filterByContainer; public static Option<string> o_unityVersion;
public Option<List<string>> o_filterByPathID; public static Option<bool> f_notRestoreExtensionName;
public Option<List<string>> o_filterByText; public static Option<bool> f_loadAllAssets;
public Option<string> o_assemblyPath;
public Option<string> o_unityVersion;
public Option<bool> f_notRestoreExtensionName;
public CLIOptions(string[] args) static CLIOptions()
{ {
cliArgs = args; OptionExtensions.OptionGrouping = OptionGrouping;
InitOptions(); 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; isParsed = false;
showHelp = false; showHelp = false;
cliArgs = null;
inputPath = ""; inputPath = "";
filterBy = FilterBy.None; filterBy = FilterBy.None;
optionsDict = new Dictionary<string, string>(); optionsDict = new Dictionary<string, string>();
flagsDict = new Dictionary<string, string>(); flagsDict = new Dictionary<string, string>();
optionGroups = new Dictionary<HelpGroups, Dictionary<string, string>>(); optionGroups = new Dictionary<HelpGroups, Dictionary<string, string>>();
supportedAssetTypes = new List<ClassIDType> exportableAssetTypes = new List<ClassIDType>
{ {
ClassIDType.Texture2D, ClassIDType.Texture2D,
ClassIDType.Sprite, ClassIDType.Sprite,
@@ -126,6 +154,7 @@ namespace AssetStudioCLI.Options
ClassIDType.MovieTexture, ClassIDType.MovieTexture,
ClassIDType.Mesh, ClassIDType.Mesh,
}; };
knownAssetTypesDict = ((ClassIDType[])Enum.GetValues(typeof(ClassIDType))).ToHashSet().ToDictionary(x => x.ToString().ToLower(), y => y);
#region Init General Options #region Init General Options
o_workMode = new GroupedOption<WorkMode> o_workMode = new GroupedOption<WorkMode>
@@ -133,18 +162,19 @@ namespace AssetStudioCLI.Options
optionDefaultValue: WorkMode.Export, optionDefaultValue: WorkMode.Export,
optionName: "-m, --mode <value>", optionName: "-m, --mode <value>",
optionDescription: "Specify working mode\n" + 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" + "Export - Exports converted assets\n" +
"ExportRaw - Exports raw data\n" + "ExportRaw - Exports raw data\n" +
"Dump - Makes asset dumps\n" + "Dump - Makes asset dumps\n" +
"Info - Loads file(s), shows the number of available for export assets and exits\n" + "Info - Loads file(s), shows the number of available for export assets and exits\n" +
"Live2D - Exports Live2D Cubism 3 models\n" + "Live2D - Exports Live2D Cubism 3 models\n" +
"SplitObjects - Exports split objects (fbx)\n" +
"Example: \"-m info\"\n", "Example: \"-m info\"\n",
optionHelpGroup: HelpGroups.General optionHelpGroup: HelpGroups.General
); );
o_exportAssetTypes = new GroupedOption<List<ClassIDType>> o_exportAssetTypes = new GroupedOption<List<ClassIDType>>
( (
optionDefaultValue: supportedAssetTypes, optionDefaultValue: exportableAssetTypes,
optionName: "-t, --asset-type <value(s)>", optionName: "-t, --asset-type <value(s)>",
optionDescription: "Specify asset type(s) to export\n" + optionDescription: "Specify asset type(s) to export\n" +
"<Value(s): tex2d, sprite, textAsset, monoBehaviour, font, shader, movieTexture,\n" + "<Value(s): tex2d, sprite, textAsset, monoBehaviour, font, shader, movieTexture,\n" +
@@ -170,7 +200,7 @@ namespace AssetStudioCLI.Options
); );
o_outputFolder = new GroupedOption<string> o_outputFolder = new GroupedOption<string>
( (
optionDefaultValue: "", optionDefaultValue: "ASExport",
optionName: "-o, --output <path>", optionName: "-o, --output <path>",
optionDescription: "Specify path to the output folder\n" + optionDescription: "Specify path to the output folder\n" +
"If path isn't specifyed, 'ASExport' folder will be created in the program's work folder\n", "If path isn't specifyed, 'ASExport' folder will be created in the program's work folder\n",
@@ -230,6 +260,67 @@ namespace AssetStudioCLI.Options
); );
#endregion #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 #region Init Advanced Options
o_exportAssetList = new GroupedOption<ExportListType> o_exportAssetList = new GroupedOption<ExportListType>
( (
@@ -241,101 +332,47 @@ namespace AssetStudioCLI.Options
"Example: \"--export-asset-list xml\"\n", "Example: \"--export-asset-list xml\"\n",
optionHelpGroup: HelpGroups.Advanced 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> o_assemblyPath = new GroupedOption<string>
( (
optionDefaultValue: "", optionDefaultValue: "",
optionName: "--assembly-folder <path>", optionName: "--assembly-folder <path>",
optionDescription: "Specify the path to the assembly folder", optionDescription: "Specify the path to the assembly folder\n",
optionHelpGroup: HelpGroups.Advanced optionHelpGroup: HelpGroups.Advanced
); );
o_unityVersion = new GroupedOption<string> o_unityVersion = new GroupedOption<string>
( (
optionDefaultValue: "", optionDefaultValue: "",
optionName: "--unity-version <text>", 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 optionHelpGroup: HelpGroups.Advanced
); );
f_notRestoreExtensionName = new GroupedOption<bool> f_notRestoreExtensionName = new GroupedOption<bool>
( (
optionDefaultValue: false, optionDefaultValue: false,
optionName: "--not-restore-extension", optionName: "--not-restore-extension",
optionDescription: "(Flag) If specified, AssetStudio will not try to use/restore original TextAsset\nextension name, and will just export all TextAssets with the \".txt\" extension", optionDescription: "(Flag) If specified, 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, optionHelpGroup: HelpGroups.Advanced,
isFlag: true isFlag: true
); );
#endregion #endregion
} }
internal static void OptionGrouping(string name, string desc, HelpGroups group, bool isFlag) public static void ParseArgs(string[] args)
{ {
if (string.IsNullOrEmpty(name)) cliArgs = args;
{
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 void ParseArgs(string[] args)
{
var brightYellow = CLIAnsiColors.BrightYellow; var brightYellow = CLIAnsiColors.BrightYellow;
var brightRed = CLIAnsiColors.BrightRed; 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; showHelp = true;
return; return;
@@ -350,7 +387,6 @@ namespace AssetStudioCLI.Options
$"Specified file or folder was not found. The input path must be specified as the first argument."); $"Specified file or folder was not found. The input path must be specified as the first argument.");
return; return;
} }
o_outputFolder.Value = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ASExport");
} }
else else
{ {
@@ -375,6 +411,64 @@ namespace AssetStudioCLI.Options
} }
}; };
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");
TryShowOptionDescription(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 "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");
Console.WriteLine(o_workMode.Description);
return;
}
resplittedArgs.RemoveRange(workModeOptionIndex, 2);
}
#region Parse Flags #region Parse Flags
for (int i = 0; i < resplittedArgs.Count; i++) for (int i = 0; i < resplittedArgs.Count; i++)
{ {
@@ -386,6 +480,21 @@ namespace AssetStudioCLI.Options
f_notRestoreExtensionName.Value = true; f_notRestoreExtensionName.Value = true;
resplittedArgs.RemoveAt(i); resplittedArgs.RemoveAt(i);
break; 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");
Console.WriteLine(f_loadAllAssets.Description);
return;
}
break;
} }
} }
#endregion #endregion
@@ -399,43 +508,9 @@ namespace AssetStudioCLI.Options
var value = resplittedArgs[i + 1].Replace("\"", ""); var value = resplittedArgs[i + 1].Replace("\"", "");
switch (option) 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 "-t":
case "--asset-type": case "--asset-type":
if (o_workMode.Value == WorkMode.ExportLive2D) if (o_workMode.Value == WorkMode.ExportLive2D || o_workMode.Value == WorkMode.SplitObjects)
{ {
i++; i++;
continue; continue;
@@ -447,43 +522,34 @@ namespace AssetStudioCLI.Options
switch (type.ToLower()) switch (type.ToLower())
{ {
case "tex2d": case "tex2d":
case "texture2d":
o_exportAssetTypes.Value.Add(ClassIDType.Texture2D); o_exportAssetTypes.Value.Add(ClassIDType.Texture2D);
break; 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 "audio":
case "audioclip":
o_exportAssetTypes.Value.Add(ClassIDType.AudioClip); o_exportAssetTypes.Value.Add(ClassIDType.AudioClip);
break; break;
case "video": case "video":
case "videoclip":
o_exportAssetTypes.Value.Add(ClassIDType.VideoClip); o_exportAssetTypes.Value.Add(ClassIDType.VideoClip);
break; break;
case "movietexture":
o_exportAssetTypes.Value.Add(ClassIDType.MovieTexture);
break;
case "mesh":
o_exportAssetTypes.Value.Add(ClassIDType.Mesh);
break;
case "all": case "all":
o_exportAssetTypes.Value = supportedAssetTypes; o_exportAssetTypes.Value = exportableAssetTypes;
break; break;
default: default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported asset type: [{value.Color(brightRed)}].\n"); 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");
Console.WriteLine(o_exportAssetTypes.Description);
return;
}
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Asset type [{type.Color(brightRed)}] is not supported for exporting.\n");
Console.WriteLine(o_exportAssetTypes.Description); Console.WriteLine(o_exportAssetTypes.Description);
return; return;
} }
@@ -523,6 +589,10 @@ namespace AssetStudioCLI.Options
{ {
Directory.CreateDirectory(value); Directory.CreateDirectory(value);
} }
if (!value.EndsWith($"{Path.DirectorySeparatorChar}"))
{
value += Path.DirectorySeparatorChar;
}
o_outputFolder.Value = value; o_outputFolder.Value = value;
} }
catch (Exception ex) catch (Exception ex)
@@ -623,6 +693,32 @@ namespace AssetStudioCLI.Options
return; return;
} }
break; 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");
Console.WriteLine(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");
Console.WriteLine(o_fbxBoneSize.Description);
return;
}
break;
case "--export-asset-list": case "--export-asset-list":
switch (value.ToLower()) switch (value.ToLower())
{ {
@@ -658,6 +754,7 @@ namespace AssetStudioCLI.Options
if (Directory.Exists(value)) if (Directory.Exists(value))
{ {
o_assemblyPath.Value = value; o_assemblyPath.Value = value;
Studio.assemblyLoader.Load(value);
} }
else else
{ {
@@ -703,8 +800,22 @@ namespace AssetStudioCLI.Options
return; return;
} }
} }
isParsed = true;
#endregion #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) private static string[] ValueSplitter(string value)
@@ -713,7 +824,7 @@ namespace AssetStudioCLI.Options
return value.Split(separator); 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)); var optionDesc = descDict.Where(x => x.Key.Contains(option));
if (optionDesc.Any()) if (optionDesc.Any())
@@ -728,7 +839,7 @@ namespace AssetStudioCLI.Options
return false; return false;
} }
public void ShowHelp(bool showUsageOnly = false) public static void ShowHelp(bool showUsageOnly = false)
{ {
const int indent = 22; const int indent = 22;
var helpMessage = new StringBuilder(); var helpMessage = new StringBuilder();
@@ -761,12 +872,13 @@ namespace AssetStudioCLI.Options
} }
else 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}"); Console.WriteLine($"{usage}\n\n{helpMessage}");
} }
} }
private string ShowCurrentFilter() private static string ShowCurrentFilter()
{ {
switch (filterBy) switch (filterBy)
{ {
@@ -785,7 +897,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(); var sb = new StringBuilder();
sb.AppendLine("[Current Options]"); sb.AppendLine("[Current Options]");
@@ -793,8 +920,35 @@ namespace AssetStudioCLI.Options
sb.AppendLine($"# Input Path: \"{inputPath}\""); sb.AppendLine($"# Input Path: \"{inputPath}\"");
switch (o_workMode.Value) 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($"# Assebmly 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: 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 Level: {o_logLevel}");
sb.AppendLine($"# Log Output: {o_logOutput}"); sb.AppendLine($"# Log Output: {o_logOutput}");
sb.AppendLine($"# Export Asset List: {o_exportAssetList}"); sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
@@ -802,27 +956,22 @@ namespace AssetStudioCLI.Options
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\""); sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
break; break;
case WorkMode.ExportLive2D: case WorkMode.ExportLive2D:
case WorkMode.SplitObjects:
sb.AppendLine($"# Output Path: \"{o_outputFolder}\""); sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
sb.AppendLine($"# Log Level: {o_logLevel}"); sb.AppendLine($"# Log Level: {o_logLevel}");
sb.AppendLine($"# Log Output: {o_logOutput}"); sb.AppendLine($"# Log Output: {o_logOutput}");
sb.AppendLine($"# Export Asset List: {o_exportAssetList}"); sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
sb.AppendLine($"# 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($"# Assebmly Path: \"{o_assemblyPath}\"");
}
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\""); sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
break; 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("======"); sb.AppendLine("======");
Logger.Info(sb.ToString()); Logger.Info(sb.ToString());

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,54 @@ namespace AssetStudioCLI
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
var options = new CLIOptions(args); CLIOptions.ParseArgs(args);
if (options.isParsed) if (CLIOptions.isParsed)
{ {
CLIRun(options); CLIRun();
} }
else if (options.showHelp) else if (CLIOptions.showHelp)
{ {
options.ShowHelp(); CLIOptions.ShowHelp();
} }
else else
{ {
Console.WriteLine(); 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; Logger.Default = cliLogger;
var studio = new Studio(options); CLIOptions.ShowCurrentOptions();
options.ShowCurrentOptions();
try try
{ {
if (studio.LoadAssets()) if (Studio.LoadAssets())
{ {
studio.ParseAssets(); Studio.ParseAssets();
if (options.filterBy != FilterBy.None && options.o_workMode.Value != WorkMode.ExportLive2D) 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: case WorkMode.Info:
studio.ShowExportableAssetsInfo(); Studio.ShowExportableAssetsInfo();
break; break;
case WorkMode.ExportLive2D: case WorkMode.ExportLive2D:
studio.ExportLive2D(); Studio.ExportLive2D();
break;
case WorkMode.SplitObjects:
Studio.ExportSplitObjects();
break; break;
default: default:
studio.ExportAssets(); Studio.ExportAssets();
break; break;
} }
} }

View File

@@ -1,6 +1,6 @@
## AssetStudioModCLI ## AssetStudioModCLI
CLI version of AssetStudioMod. 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.* - *There are no plans to add support for `AnimationClip`, `Animator` for now.*
### Usage ### Usage
@@ -10,20 +10,22 @@ AssetStudioModCLI <input path to asset file/folder> [-m, --mode <value>]
[-o, --output <path>] [-h, --help] [-o, --output <path>] [-h, --help]
[--log-level <value>] [--log-output <value>] [--log-level <value>] [--log-output <value>]
[--image-format <value>] [--audio-format <value>] [--image-format <value>] [--audio-format <value>]
[--export-asset-list <value>] [--filter-by-name <text>] [--fbx-scale-factor <value>] [--fbx-bone-size <value>]
[--filter-by-container <text>] [--filter-by-pathid <text>] [--filter-by-name <text>] [--filter-by-container <text>]
[--filter-by-text <text>] [--assembly-folder <path>] [--filter-by-pathid <text>] [--filter-by-text <text>]
[--export-asset-list <value>] [--assembly-folder <path>]
[--unity-version <text>] [--not-restore-extension] [--unity-version <text>] [--not-restore-extension]
[--load-all]
General Options: General Options:
-m, --mode <value> Specify working mode -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 Export - Exports converted assets
ExportRaw - Exports raw data ExportRaw - Exports raw data
Dump - Makes asset dumps Dump - Makes asset dumps
Info - Loads file(s), shows the number of available for export assets and exits Info - Loads file(s), shows the number of available for export assets and exits
Live2D - Exports Live2D Cubism 3 models Live2D - Exports Live2D Cubism 3 models
SplitObjects - Exports split objects (fbx)
Example: "-m info" Example: "-m info"
-t, --asset-type <value(s)> Specify asset type(s) to export -t, --asset-type <value(s)> Specify asset type(s) to export
@@ -67,12 +69,16 @@ Convert Options:
None - Do not convert audios and export them in their own format None - Do not convert audios and export them in their own format
Example: "--audio-format wav" Example: "--audio-format wav"
Advanced Options: FBX Options:
--export-asset-list <value> Specify the format in which you want to export asset list --fbx-scale-factor <value> Specify the FBX Scale Factor
<Value: none(default) | xml> <Value: float number from 0 to 100 (default=1)
None - Do not export asset list Example: "--fbx-scale-factor 50"
Example: "--export-asset-list xml"
--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 --filter-by-name <text> Specify the name by which assets should be filtered
*To specify multiple names write them separated by ',' or ';' without spaces *To specify multiple names write them separated by ',' or ';' without spaces
Example: "--filter-by-name char" or "--filter-by-name char,bg" Example: "--filter-by-name char" or "--filter-by-name char,bg"
@@ -90,8 +96,21 @@ Advanced Options:
*To specify multiple values write them separated by ',' or ';' without spaces *To specify multiple values write them separated by ',' or ';' without spaces
Example: "--filter-by-text portrait" or "--filter-by-text portrait,art" 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 --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 --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 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)
``` ```

View File

@@ -11,40 +11,33 @@ using Ansi = AssetStudioCLI.CLIAnsiColors;
namespace AssetStudioCLI namespace AssetStudioCLI
{ {
internal class Studio internal static class Studio
{ {
public AssetsManager assetsManager = new AssetsManager(); public static AssetsManager assetsManager = new AssetsManager();
public List<AssetItem> parsedAssetsList = new List<AssetItem>(); 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 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); 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"); Console.Write($"[{value:000}%]\r");
} }
public bool LoadAssets() public static bool LoadAssets()
{ {
var isLoaded = false; var isLoaded = false;
assetsManager.SpecifyUnityVersion = options.o_unityVersion.Value; assetsManager.SpecifyUnityVersion = CLIOptions.o_unityVersion.Value;
assetsManager.SetAssetFilter(options.o_exportAssetTypes.Value); if (!CLIOptions.f_loadAllAssets.Value)
{
assetsManager.LoadFilesAndFolders(options.inputPath); assetsManager.SetAssetFilter(CLIOptions.o_exportAssetTypes.Value);
}
assetsManager.LoadFilesAndFolders(CLIOptions.inputPath);
if (assetsManager.assetsFileList.Count == 0) if (assetsManager.assetsFileList.Count == 0)
{ {
Logger.Warning("No Unity file can be loaded."); Logger.Warning("No Unity file can be loaded.");
@@ -57,12 +50,13 @@ namespace AssetStudioCLI
return isLoaded; return isLoaded;
} }
public void ParseAssets() public static void ParseAssets()
{ {
Logger.Info("Parse assets..."); Logger.Info("Parse assets...");
var fileAssetsList = new List<AssetItem>(); var fileAssetsList = new List<AssetItem>();
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count); var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
var objectAssetItemDic = new Dictionary<AssetStudio.Object, AssetItem>(objectCount);
Progress.Reset(); Progress.Reset();
var i = 0; var i = 0;
@@ -71,6 +65,7 @@ namespace AssetStudioCLI
foreach (var asset in assetsFile.Objects) foreach (var asset in assetsFile.Objects)
{ {
var assetItem = new AssetItem(asset); var assetItem = new AssetItem(asset);
objectAssetItemDic.Add(asset, assetItem);
assetItem.UniqueID = "_#" + i; assetItem.UniqueID = "_#" + i;
var isExportable = false; var isExportable = false;
switch (asset) switch (asset)
@@ -90,6 +85,7 @@ namespace AssetStudioCLI
} }
} }
} }
assetItem.Text = m_AssetBundle.m_Name;
break; break;
case ResourceManager m_ResourceManager: case ResourceManager m_ResourceManager:
foreach (var m_Container in m_ResourceManager.m_Container) foreach (var m_Container in m_ResourceManager.m_Container)
@@ -114,14 +110,7 @@ namespace AssetStudioCLI
if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath)) if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath))
assetItem.FullSize = asset.byteSize + m_VideoClip.m_ExternalResources.m_Size; assetItem.FullSize = asset.byteSize + m_VideoClip.m_ExternalResources.m_Size;
assetItem.Text = m_VideoClip.m_Name; assetItem.Text = m_VideoClip.m_Name;
break; break;
case Mesh _:
case MovieTexture _:
case TextAsset _:
case Font _:
case Sprite _:
assetItem.Text = ((NamedObject)asset).m_Name;
break;
case Shader m_Shader: case Shader m_Shader:
assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name; assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name;
break; break;
@@ -135,14 +124,26 @@ namespace AssetStudioCLI
assetItem.Text = m_MonoBehaviour.m_Name; assetItem.Text = m_MonoBehaviour.m_Name;
} }
break; 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; assetItem.Text = assetItem.TypeString + assetItem.UniqueID;
} }
isExportable = options.o_exportAssetTypes.Value.Contains(asset.type); isExportable = CLIOptions.o_exportAssetTypes.Value.Contains(asset.type);
if (isExportable) if (isExportable || (CLIOptions.f_loadAllAssets.Value && CLIOptions.o_exportAssetTypes.Value == CLIOptions.o_exportAssetTypes.DefaultValue))
{ {
fileAssetsList.Add(assetItem); fileAssetsList.Add(assetItem);
} }
@@ -158,14 +159,119 @@ namespace AssetStudioCLI
} }
parsedAssetsList.AddRange(fileAssetsList); parsedAssetsList.AddRange(fileAssetsList);
fileAssetsList.Clear(); fileAssetsList.Clear();
if (options.o_workMode.Value != WorkMode.ExportLive2D) if (CLIOptions.o_workMode.Value != WorkMode.ExportLive2D)
{ {
containers.Clear(); 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>(); var exportableAssetsCountDict = new Dictionary<ClassIDType, int>();
string info = ""; string info = "";
@@ -198,7 +304,7 @@ namespace AssetStudioCLI
info += "No exportable assets found."; info += "No exportable assets found.";
} }
if (options.o_logLevel.Value > LoggerEvent.Info) if (CLIOptions.o_logLevel.Value > LoggerEvent.Info)
{ {
Console.WriteLine(info); Console.WriteLine(info);
} }
@@ -208,53 +314,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 assetsCount = parsedAssetsList.Count;
var filteredAssets = new List<AssetItem>(); var filteredAssets = new List<AssetItem>();
switch(options.filterBy) switch(CLIOptions.filterBy)
{ {
case FilterBy.Name: 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( Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " + $"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; break;
case FilterBy.Container: 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( Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " + $"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; break;
case FilterBy.PathID: 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( Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " + $"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; break;
case FilterBy.NameOrContainer: case FilterBy.NameOrContainer:
filteredAssets = parsedAssetsList.FindAll(x => filteredAssets = parsedAssetsList.FindAll(x =>
options.o_filterByText.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) || CLIOptions.o_filterByText.Value.Any(y => x.Text.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.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
); );
Logger.Info( Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " + $"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", 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; break;
case FilterBy.NameAndContainer: case FilterBy.NameAndContainer:
filteredAssets = parsedAssetsList.FindAll(x => filteredAssets = parsedAssetsList.FindAll(x =>
options.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) && CLIOptions.o_filterByName.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) &&
options.o_filterByContainer.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) CLIOptions.o_filterByContainer.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
); );
Logger.Info( Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " + $"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", options.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers " + $"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers " +
$"and {$"\"{string.Join("\", \"", options.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names." $"and {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
); );
break; break;
} }
@@ -262,13 +381,13 @@ namespace AssetStudioCLI
parsedAssetsList = filteredAssets; 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 toExportCount = parsedAssetsList.Count;
var exportedCount = 0; var exportedCount = 0;
var groupOption = options.o_groupAssetsBy.Value; var groupOption = CLIOptions.o_groupAssetsBy.Value;
foreach (var asset in parsedAssetsList) foreach (var asset in parsedAssetsList)
{ {
string exportPath; string exportPath;
@@ -310,25 +429,25 @@ namespace AssetStudioCLI
exportPath += Path.DirectorySeparatorChar; exportPath += Path.DirectorySeparatorChar;
try try
{ {
switch (options.o_workMode.Value) switch (CLIOptions.o_workMode.Value)
{ {
case WorkMode.ExportRaw: 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)) if (ExportRawFile(asset, exportPath))
{ {
exportedCount++; exportedCount++;
} }
break; break;
case WorkMode.Dump: case WorkMode.Dump:
Logger.Debug($"{options.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}"); Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
if (ExportDumpFile(asset, exportPath, assemblyLoader)) if (ExportDumpFile(asset, exportPath))
{ {
exportedCount++; exportedCount++;
} }
break; break;
case WorkMode.Export: case WorkMode.Export:
Logger.Debug($"{options.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}"); Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
if (ExportConvertFile(asset, exportPath, options, assemblyLoader)) if (ExportConvertFile(asset, exportPath))
{ {
exportedCount++; exportedCount++;
} }
@@ -349,11 +468,11 @@ namespace AssetStudioCLI
} }
else if (toExportCount > exportedCount) 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 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) if (toExportCount > exportedCount)
@@ -362,11 +481,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: case ExportListType.XML:
var filename = Path.Combine(savePath, "assets.xml"); var filename = Path.Combine(savePath, "assets.xml");
@@ -393,9 +512,104 @@ namespace AssetStudioCLI
Logger.Info($"Finished exporting asset list with {parsedAssetsList.Count} items."); 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 useFullContainerPath = false;
Progress.Reset(); Progress.Reset();
@@ -461,7 +675,7 @@ namespace AssetStudioCLI
Progress.Report(modelCounter, (int)totalModelCount); Progress.Report(modelCounter, (int)totalModelCount);
} }
var status = modelCounter > 0 ? var status = modelCounter > 0 ?
$"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s) to \"{options.o_outputFolder.Value.Color(Ansi.BrightCyan)}\"" : $"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightCyan)}\"" :
"Nothing exported."; "Nothing exported.";
Logger.Default.Log(LoggerEvent.Info, status, ignoreLevel: true); Logger.Default.Log(LoggerEvent.Info, status, ignoreLevel: true);
} }

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

View File

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

View File

@@ -1,14 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
namespace AssetStudioGUI namespace AssetStudioGUI

View File

@@ -7,13 +7,14 @@
<ApplicationIcon>Resources\as.ico</ApplicationIcon> <ApplicationIcon>Resources\as.ico</ApplicationIcon>
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle> <AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle>
<AssemblyName>AssetStudioModGUI</AssemblyName> <AssemblyName>AssetStudioModGUI</AssemblyName>
<Version>0.17.1.0</Version> <Version>0.17.3.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2021-2023</Copyright> <Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2021-2023</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net472' "> <PropertyGroup Condition=" '$(TargetFramework)' == 'net472' ">
<IsPublishable>false</IsPublishable> <IsPublishable>false</IsPublishable>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -52,8 +53,15 @@
</ContentWithTargetPath> </ContentWithTargetPath>
</ItemGroup> </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' "> <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"> <Reference Include="OpenTK.WinForms">
<HintPath>Libraries\OpenTK.WinForms.dll</HintPath> <HintPath>Libraries\OpenTK.WinForms.dll</HintPath>
</Reference> </Reference>
@@ -64,12 +72,6 @@
<PackageReference Include="OpenTK.GLControl" Version="3.3.3" /> <PackageReference Include="OpenTK.GLControl" Version="3.3.3" />
</ItemGroup> </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 --> <!-- 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' "> <Target Name="CopyExtraFilesPortable" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' OR '$(TargetFramework)' == 'net472' ">

View File

@@ -125,6 +125,8 @@
this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
this.contextMenuStrip2 = new System.Windows.Forms.ContextMenuStrip(this.components); 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.selectAllToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.clearSelectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.clearSelectionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator(); this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator();
@@ -526,7 +528,7 @@
this.allToolStripMenuItem.CheckOnClick = true; this.allToolStripMenuItem.CheckOnClick = true;
this.allToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; this.allToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
this.allToolStripMenuItem.Name = "allToolStripMenuItem"; 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.Text = "All";
this.allToolStripMenuItem.Click += new System.EventHandler(this.typeToolStripMenuItem_Click); this.allToolStripMenuItem.Click += new System.EventHandler(this.typeToolStripMenuItem_Click);
// //
@@ -620,7 +622,7 @@
this.sceneTreeView.Size = new System.Drawing.Size(472, 587); this.sceneTreeView.Size = new System.Drawing.Size(472, 587);
this.sceneTreeView.TabIndex = 1; this.sceneTreeView.TabIndex = 1;
this.sceneTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.sceneTreeView_AfterCheck); 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 // treeSearch
// //
@@ -629,7 +631,7 @@
this.treeSearch.Location = new System.Drawing.Point(0, 0); this.treeSearch.Location = new System.Drawing.Point(0, 0);
this.treeSearch.Name = "treeSearch"; this.treeSearch.Name = "treeSearch";
this.treeSearch.Size = new System.Drawing.Size(472, 20); this.treeSearch.Size = new System.Drawing.Size(472, 20);
this.treeSearch.TabIndex = 0; this.treeSearch.TabIndex = 2;
this.treeSearch.Text = " Search "; this.treeSearch.Text = " Search ";
this.treeSearch.TextChanged += new System.EventHandler(this.treeSearch_TextChanged); this.treeSearch.TextChanged += new System.EventHandler(this.treeSearch_TextChanged);
this.treeSearch.Enter += new System.EventHandler(this.treeSearch_Enter); this.treeSearch.Enter += new System.EventHandler(this.treeSearch_Enter);
@@ -1115,13 +1117,28 @@
// contextMenuStrip2 // contextMenuStrip2
// //
this.contextMenuStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.contextMenuStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.showRelatedAssetsToolStripMenuItem,
this.toolStripSeparator7,
this.selectAllToolStripMenuItem, this.selectAllToolStripMenuItem,
this.clearSelectionToolStripMenuItem, this.clearSelectionToolStripMenuItem,
this.toolStripSeparator5, this.toolStripSeparator5,
this.expandAllToolStripMenuItem, this.expandAllToolStripMenuItem,
this.collapseAllToolStripMenuItem}); this.collapseAllToolStripMenuItem});
this.contextMenuStrip2.Name = "contextMenuStrip2"; 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 // selectAllToolStripMenuItem
// //
@@ -1286,7 +1303,6 @@
private System.Windows.Forms.TextBox treeSearch; private System.Windows.Forms.TextBox treeSearch;
private System.Windows.Forms.ToolStripMenuItem loadFileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem loadFileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem loadFolderToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem loadFolderToolStripMenuItem;
private System.Windows.Forms.ListView assetListView;
private System.Windows.Forms.ColumnHeader columnHeaderName; private System.Windows.Forms.ColumnHeader columnHeaderName;
private System.Windows.Forms.ColumnHeader columnHeaderSize; private System.Windows.Forms.ColumnHeader columnHeaderSize;
private System.Windows.Forms.ColumnHeader columnHeaderType; private System.Windows.Forms.ColumnHeader columnHeaderType;
@@ -1386,6 +1402,9 @@
private System.Windows.Forms.RichTextBox listSearch; private System.Windows.Forms.RichTextBox listSearch;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator6; private System.Windows.Forms.ToolStripSeparator toolStripSeparator6;
private System.Windows.Forms.ToolStripMenuItem allLive2DModelsToolStripMenuItem; 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 AssetStudio;
using Newtonsoft.Json; using Newtonsoft.Json;
using OpenTK;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -21,6 +20,7 @@ using static AssetStudioGUI.Studio;
using Font = AssetStudio.Font; using Font = AssetStudio.Font;
using Microsoft.WindowsAPICodePack.Taskbar; using Microsoft.WindowsAPICodePack.Taskbar;
#if NET472 #if NET472
using OpenTK;
using Vector3 = OpenTK.Vector3; using Vector3 = OpenTK.Vector3;
using Vector4 = OpenTK.Vector4; using Vector4 = OpenTK.Vector4;
#else #else
@@ -267,7 +267,10 @@ namespace AssetStudioGUI
} }
allToolStripMenuItem.Checked = true; allToolStripMenuItem.Checked = true;
var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {assetListView.Items.Count} exportable assets"; 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); var objectsCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
if (m_ObjectsCount != objectsCount) if (m_ObjectsCount != objectsCount)
{ {
@@ -499,10 +502,10 @@ namespace AssetStudioGUI
switch (e.TabPageIndex) switch (e.TabPageIndex)
{ {
case 0: case 0:
treeSearch.Select(); sceneTreeView.Select();
break; break;
case 1: case 1:
listSearch.Select(); assetListView.Select();
break; break;
} }
} }
@@ -782,42 +785,42 @@ namespace AssetStudioGUI
return; return;
try try
{ {
switch (assetItem.Asset) switch (assetItem.Type)
{ {
case Texture2D m_Texture2D: case ClassIDType.Texture2D:
PreviewTexture2D(assetItem, m_Texture2D); PreviewTexture2D(assetItem, assetItem.Asset as Texture2D);
break; break;
case AudioClip m_AudioClip: case ClassIDType.AudioClip:
PreviewAudioClip(assetItem, m_AudioClip); PreviewAudioClip(assetItem, assetItem.Asset as AudioClip);
break; break;
case Shader m_Shader: case ClassIDType.Shader:
PreviewShader(m_Shader); PreviewShader(assetItem.Asset as Shader);
break; break;
case TextAsset m_TextAsset: case ClassIDType.TextAsset:
PreviewTextAsset(m_TextAsset); PreviewTextAsset(assetItem.Asset as TextAsset);
break; break;
case MonoBehaviour m_MonoBehaviour: case ClassIDType.MonoBehaviour:
PreviewMonoBehaviour(m_MonoBehaviour); PreviewMonoBehaviour(assetItem.Asset as MonoBehaviour);
break; break;
case Font m_Font: case ClassIDType.Font:
PreviewFont(m_Font); PreviewFont(assetItem.Asset as Font);
break; break;
case Mesh m_Mesh: case ClassIDType.Mesh:
PreviewMesh(m_Mesh); PreviewMesh(assetItem.Asset as Mesh);
break; break;
case VideoClip m_VideoClip: case ClassIDType.VideoClip:
PreviewVideoClip(assetItem, m_VideoClip); PreviewVideoClip(assetItem, assetItem.Asset as VideoClip);
break; break;
case MovieTexture _: case ClassIDType.MovieTexture:
StatusStripUpdate("Only supported export."); StatusStripUpdate("Only supported export.");
break; break;
case Sprite m_Sprite: case ClassIDType.Sprite:
PreviewSprite(assetItem, m_Sprite); PreviewSprite(assetItem, assetItem.Asset as Sprite);
break; break;
case Animator _: case ClassIDType.Animator:
StatusStripUpdate("Can be exported to FBX file."); StatusStripUpdate("Can be exported to FBX file.");
break; break;
case AnimationClip _: case ClassIDType.AnimationClip:
StatusStripUpdate("Can be exported with Animator or Objects"); StatusStripUpdate("Can be exported with Animator or Objects");
break; break;
default: default:
@@ -1026,7 +1029,7 @@ namespace AssetStudioGUI
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine($"Width: {m_VideoClip.Width}"); sb.AppendLine($"Width: {m_VideoClip.Width}");
sb.AppendLine($"Height: {m_VideoClip.Height}"); 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}"); sb.AppendLine($"Split alpha: {m_VideoClip.m_HasSplitAlpha}");
assetItem.InfoText = sb.ToString(); assetItem.InfoText = sb.ToString();
@@ -1809,10 +1812,11 @@ namespace AssetStudioGUI
logger.ShowErrorMessage = toolStripMenuItem15.Checked; 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); 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 #region FMOD
private void FMODinit() 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"> <metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>312, 17</value> <value>312, 17</value>
</metadata> </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"> <data name="fontPreviewBox.Text" xml:space="preserve">
<value>abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWYZ <value>abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWYZ
1234567890.:,;'\"(!?)+-*/= 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> The quick brown fox jumps over the lazy dog. 1234567890</value>
</data> </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"> <metadata name="contextMenuStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>775, 21</value> <value>775, 21</value>
</metadata> </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

@@ -33,7 +33,12 @@ namespace AssetStudio
public TypeDefinition GetTypeDefinition(string assemblyName, string fullName) 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); var typeDef = module.GetType(fullName);
if (typeDef == null && assemblyName == "UnityEngine.dll") if (typeDef == null && assemblyName == "UnityEngine.dll")

View File

@@ -2,15 +2,14 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks> <TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks>
<Version>0.17.1.0</Version> <Version>0.17.3.0</Version>
<Copyright>Copyright © Perfare 2018-2022</Copyright> <Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2023</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Mono.Cecil" Version="0.11.3" /> <PackageReference Include="Mono.Cecil" Version="0.11.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" !$(TargetFramework.Contains('windows')) "> <ItemGroup Condition=" !$(TargetFramework.Contains('windows')) ">
@@ -23,6 +22,11 @@
<PackageReference Include="Kyaru.Texture2DDecoder"> <PackageReference Include="Kyaru.Texture2DDecoder">
<Version>0.17.0</Version> <Version>0.17.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -99,7 +99,7 @@ namespace CubismLive2DExtractor
} }
//motion //motion
var motions = new JArray(); var motions = new SortedDictionary<string, JArray>();
if (gameObjects.Count > 0) if (gameObjects.Count > 0)
{ {
@@ -205,11 +205,8 @@ namespace CubismLive2DExtractor
} }
json.Meta.TotalUserDataSize = totalUserDataSize; json.Meta.TotalUserDataSize = totalUserDataSize;
motions.Add(new JObject var motionPath = new JObject(new JProperty("File", $"motions/{animation.Name}.motion3.json"));
{ motions.Add(animation.Name, new JArray(motionPath));
{ "Name", animation.Name },
{ "File", $"motions/{animation.Name}.motion3.json" }
});
File.WriteAllText($"{destMotionPath}{animation.Name}.motion3.json", JsonConvert.SerializeObject(json, Formatting.Indented, new MyJsonConverter())); File.WriteAllText($"{destMotionPath}{animation.Name}.motion3.json", JsonConvert.SerializeObject(json, Formatting.Indented, new MyJsonConverter()));
} }
} }
@@ -307,7 +304,7 @@ namespace CubismLive2DExtractor
{ {
Moc = $"{modelName}.moc3", Moc = $"{modelName}.moc3",
Textures = textures.ToArray(), Textures = textures.ToArray(),
Motions = new JObject { { "", motions } }, Motions = JObject.FromObject(motions),
Expressions = expressions, Expressions = expressions,
}, },
Groups = groups.ToArray() 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) 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); 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) switch (spriteMaskMode)
@@ -46,7 +50,6 @@ namespace AssetStudio
tex.ApplyRGBMask(alphaTex); tex.ApplyRGBMask(alphaTex);
return tex; return tex;
case SpriteMaskMode.MaskOnly: case SpriteMaskMode.MaskOnly:
tex.Dispose();
return alphaTex; return alphaTex;
} }
} }
@@ -89,92 +92,90 @@ namespace AssetStudio
var originalImage = m_Texture2D.ConvertToImage(false); var originalImage = m_Texture2D.ConvertToImage(false);
if (originalImage != null) 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);
var width = (int)(m_Texture2D.m_Width / downscaleMultiplier); originalImage.Mutate(x => x.Resize(width, height));
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 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; return null;

View File

@@ -1,5 +1,28 @@
# Changelog # Changelog
## 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] ## v0.17.1.0 [12-07-2023]
#### Breaking Changes #### 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` - 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

@@ -89,12 +89,22 @@ AssetStudioModCLI <asset folder path> -g type
``` ```
AssetStudioModCLI <asset folder path> -o <output folder path> 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 - Export Live2D Cubism models
``` ```
AssetStudioModCLI <asset folder path> -m live2d 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`, `--export-asset-list`, `--unity-version` and `--assembly-folder` options.
Any other options will be ignored. 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 ### Advanced Samples
- Export image assets converted to webp format to a specified output folder - Export image assets converted to webp format to a specified output folder
@@ -109,6 +119,13 @@ AssetStudioModCLI <asset folder path> -m info -t audio --filter-by-name voice
``` ```
AssetStudioModCLI <asset folder path> -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 - Export audio assets that have "char" in their names **or** containers
``` ```
AssetStudioModCLI <asset folder path> -t audio --filter-by-text char AssetStudioModCLI <asset folder path> -t audio --filter-by-text char
@@ -117,6 +134,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 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 - 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 AssetStudioModCLI <asset folder path> -t monobehaviour --assembly-folder <assembly folder path> --log-output both
@@ -125,6 +146,14 @@ AssetStudioModCLI <asset folder path> -t monobehaviour --assembly-folder <assemb
``` ```
AssetStudioModCLI <asset folder path> --unity-version 2017.4.39f1 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 ## GUI Usage
@@ -140,7 +169,7 @@ Use **File->Extract file** or **File->Extract folder**.
### Export Assets, Live2D models ### Export Assets, Live2D models
use **Export** menu. Use **Export** menu.
### Export Model ### Export Model

View File

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