Add AssetStudioCLI project

- Updated projects and dependencies
This commit is contained in:
VaDiM 2023-03-07 06:29:11 +03:00
parent 5c662d64e4
commit c52940abc4
33 changed files with 2074 additions and 137 deletions

3
.gitignore vendored
View File

@ -35,6 +35,9 @@ bld/
# Visual Studio 2017 auto generated files # Visual Studio 2017 auto generated files
Generated\ Files/ Generated\ Files/
# Launch Settings
*launchSettings.json
# MSTest test Results # MSTest test Results
[Tt]est[Rr]esult*/ [Tt]est[Rr]esult*/
[Bb]uild[Ll]og.* [Bb]uild[Ll]og.*

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>0.16.47.1</Version> <Version>0.16.48.1</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

@ -4,33 +4,29 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if NETFRAMEWORK
namespace AssetStudio.PInvoke namespace AssetStudio.PInvoke
{ {
public static class DllLoader public static class DllLoader
{ {
public static void PreloadDll(string dllName) public static void PreloadDll(string dllName)
{ {
var dllDir = GetDirectedDllDirectory(); var localPath = Process.GetCurrentProcess().MainModule.FileName;
var localDir = Path.GetDirectoryName(localPath);
// Not using OperatingSystem.Platform. // Not using OperatingSystem.Platform.
// See: https://www.mono-project.com/docs/faq/technical/#how-to-detect-the-execution-platform // See: https://www.mono-project.com/docs/faq/technical/#how-to-detect-the-execution-platform
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
Win32.LoadDll(dllDir, dllName); Win32.LoadDll(GetDirectedDllDirectory(localDir), dllName);
}
else
{
Posix.LoadDll(dllDir, dllName);
} }
} }
private static string GetDirectedDllDirectory() private static string GetDirectedDllDirectory(string localDir)
{ {
var localPath = Process.GetCurrentProcess().MainModule.FileName; var win32Path = Path.Combine("runtimes", "win-x86", "native");
var localDir = Path.GetDirectoryName(localPath); var win64Path = Path.Combine("runtimes", "win-x64", "native");
var subDir = Environment.Is64BitProcess ? win64Path : win32Path;
var subDir = Environment.Is64BitProcess ? "x64" : "x86";
var directedDllDir = Path.Combine(localDir, subDir); var directedDllDir = Path.Combine(localDir, subDir);
@ -64,61 +60,7 @@ namespace AssetStudio.PInvoke
private const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x1000; private const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x1000;
private const uint LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x100; private const uint LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x100;
} }
private static class Posix
{
internal static void LoadDll(string dllDir, string dllName)
{
string dllExtension;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
dllExtension = ".so";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
dllExtension = ".dylib";
}
else
{
throw new NotSupportedException();
}
var dllFileName = $"lib{dllName}{dllExtension}";
var directedDllPath = Path.Combine(dllDir, dllFileName);
const int ldFlags = RTLD_NOW | RTLD_GLOBAL;
var hLibrary = DlOpen(directedDllPath, ldFlags);
if (hLibrary == IntPtr.Zero)
{
var pErrStr = DlError();
// `PtrToStringAnsi` always uses the specific constructor of `String` (see dotnet/core#2325),
// which in turn interprets the byte sequence with system default codepage. On OSX and Linux
// the codepage is UTF-8 so the error message should be handled correctly.
var errorMessage = Marshal.PtrToStringAnsi(pErrStr);
throw new DllNotFoundException(errorMessage);
}
}
// OSX and most Linux OS use LP64 so `int` is still 32-bit even on 64-bit platforms.
// void *dlopen(const char *filename, int flag);
[DllImport("libdl", EntryPoint = "dlopen")]
private static extern IntPtr DlOpen([MarshalAs(UnmanagedType.LPStr)] string fileName, int flags);
// char *dlerror(void);
[DllImport("libdl", EntryPoint = "dlerror")]
private static extern IntPtr DlError();
private const int RTLD_LAZY = 0x1;
private const int RTLD_NOW = 0x2;
private const int RTLD_GLOBAL = 0x100;
}
} }
} }
#endif

View File

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.31410.357 VisualStudioVersion = 17.5.33414.496
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudio", "AssetStudio\AssetStudio.csproj", "{422FEC21-EF60-4F29-AA56-95DFDA23C913}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudio", "AssetStudio\AssetStudio.csproj", "{422FEC21-EF60-4F29-AA56-95DFDA23C913}"
EndProject EndProject
@ -25,6 +25,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssetStudioFBXNative", "Ass
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Texture2DDecoderNative", "Texture2DDecoderNative\Texture2DDecoderNative.vcxproj", "{29356642-C46E-4144-83D8-22DC09D0D7FD}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Texture2DDecoderNative", "Texture2DDecoderNative\Texture2DDecoderNative.vcxproj", "{29356642-C46E-4144-83D8-22DC09D0D7FD}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioCLI", "AssetStudioCLI\AssetStudioCLI.csproj", "{34B6329B-0E73-45AC-B8CC-015F119F63DC}"
ProjectSection(ProjectDependencies) = postProject
{422FEC21-EF60-4F29-AA56-95DFDA23C913} = {422FEC21-EF60-4F29-AA56-95DFDA23C913}
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF} = {65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -131,6 +137,18 @@ Global
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x64.Build.0 = Release|x64 {29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x64.Build.0 = Release|x64
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.ActiveCfg = Release|Win32 {29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.ActiveCfg = Release|Win32
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.Build.0 = Release|Win32 {29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.Build.0 = Release|Win32
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|x64.ActiveCfg = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|x64.Build.0 = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|x86.ActiveCfg = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|x86.Build.0 = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|Any CPU.Build.0 = Release|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|x64.ActiveCfg = Release|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|x64.Build.0 = Release|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|x86.ActiveCfg = Release|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

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

View File

@ -0,0 +1,99 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks>
<AssemblyTitle>AssetStudio Mod by VaDiM</AssemblyTitle>
<Version>0.16.48.1</Version>
<Copyright>Copyright © Perfare; Copyright © aelurum 2023</Copyright>
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>embedded</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetStudioUtility\AssetStudioUtility.csproj" />
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj" />
</ItemGroup>
<!-- Use local compiled win-x86 and win-x64 Texture2DDecoder libs, because libs from Kyaru.Texture2DDecoder.Windows were compiled with /MD flag -->
<Target Name="CopyExtraFilesPortable" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' ">
<Message Text="Copying extra files for $(TargetFramework)... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x86\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x64\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesPortableNet" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' AND '$(TargetFramework)' != 'net472' ">
<Message Text="Copying extra files for .NET ($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x86\libfmod.so" DestinationFolder="$(TargetDir)runtimes\linux-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libfmod.so" DestinationFolder="$(TargetDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-x64\libfmod.dylib" DestinationFolder="$(TargetDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-arm64\libfmod.dylib" DestinationFolder="$(TargetDir)runtimes\osx-arm64\native" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesPortable" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' ">
<Message Text="Publishing extra files for Portable build ($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\fmod.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\fmod.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesPortableNet" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' AND '$(TargetFramework)' != 'net472' ">
<Message Text="Publishing extra files for Portable .NET build ($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-x86\native\libfmod.so" DestinationFolder="$(PublishDir)runtimes\linux-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-x64\native\libfmod.so" DestinationFolder="$(PublishDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-x64\native\libfmod.dylib" DestinationFolder="$(PublishDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-arm64\native\libfmod.dylib" DestinationFolder="$(PublishDir)runtimes\osx-arm64\native" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesWin86" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x86' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$$(ProjectDir)Libraries\win-x86\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesWin64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x64\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesWin" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('win-x')) ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\fmod.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesLinux64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libfmod.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesLinux64" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\libfmod.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesMac64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'osx-x64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-x64\libfmod.dylib" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesMacArm64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'osx-arm64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-arm64\libfmod.dylib" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesMac" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('osx-')) ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\libfmod.dylib" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
</Project>

117
AssetStudioCLI/CLILogger.cs Normal file
View File

@ -0,0 +1,117 @@
using AssetStudio;
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using AssetStudioCLI.Options;
namespace AssetStudioCLI
{
internal enum LogOutputMode
{
Console,
File,
Both,
}
internal class CLILogger : ILogger
{
private readonly LogOutputMode logOutput;
private readonly LoggerEvent logMinLevel;
public string LogName;
public string LogPath;
public CLILogger(CLIOptions options)
{
logOutput = options.o_logOutput.Value;
logMinLevel = options.o_logLevel.Value;
LogName = $"AssetStudioCLI_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log";
LogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogName);
var ver = typeof(Program).Assembly.GetName().Version;
LogToFile(LoggerEvent.Verbose, $"---AssetStudioCLI v{ver} | Logger launched---\n" +
$"CMD Args: {string.Join(" ", options.cliArgs)}");
}
private static string ColorLogLevel(LoggerEvent logLevel)
{
string formattedLevel = $"[{logLevel}]";
switch (logLevel)
{
case LoggerEvent.Info:
return $"{formattedLevel.Color(CLIAnsiColors.BrightCyan)}";
case LoggerEvent.Warning:
return $"{formattedLevel.Color(CLIAnsiColors.BrightYellow)}";
case LoggerEvent.Error:
return $"{formattedLevel.Color(CLIAnsiColors.BrightRed)}";
default:
return formattedLevel;
}
}
private static string FormatMessage(LoggerEvent logMsgLevel, string message, bool consoleMode = false)
{
var curTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
message = message.TrimEnd();
var multiLine = message.Contains('\n');
string formattedMessage;
if (consoleMode)
{
string colorLogLevel = ColorLogLevel(logMsgLevel);
formattedMessage = $"{colorLogLevel} {message}";
if (multiLine)
{
formattedMessage = formattedMessage.Replace("\n", $"\n{colorLogLevel} ");
}
}
else
{
message = Regex.Replace(message, @"\e\[[0-9;]*m(?:\e\[K)?", ""); //Delete ANSI colors
var logLevel = $"{logMsgLevel.ToString().ToUpper(),-7}";
formattedMessage = $"{curTime} | {logLevel} | {message}";
if (multiLine)
{
formattedMessage = formattedMessage.Replace("\n", $"\n{curTime} | {logLevel} | ");
}
}
return formattedMessage;
}
public void LogToConsole(LoggerEvent logMsgLevel, string message)
{
if (logOutput != LogOutputMode.File)
{
Console.WriteLine(FormatMessage(logMsgLevel, message, consoleMode: true));
}
}
public async void LogToFile(LoggerEvent logMsgLevel, string message)
{
if (logOutput != LogOutputMode.Console)
{
using (var sw = new StreamWriter(LogPath, append: true, System.Text.Encoding.UTF8))
{
await sw.WriteLineAsync(FormatMessage(logMsgLevel, message));
}
}
}
public void Log(LoggerEvent logMsgLevel, string message)
{
if (logMsgLevel < logMinLevel || string.IsNullOrEmpty(message))
{
return;
}
if (logOutput != LogOutputMode.File)
{
LogToConsole(logMsgLevel, message);
}
if (logOutput != LogOutputMode.Console)
{
LogToFile(logMsgLevel, message);
}
}
}
}

View File

@ -0,0 +1,27 @@
using AssetStudio;
namespace AssetStudioCLI
{
internal class AssetItem
{
public Object Asset;
public SerializedFile SourceFile;
public string Container = string.Empty;
public string TypeString;
public long m_PathID;
public long FullSize;
public ClassIDType Type;
public string Text;
public string UniqueID;
public AssetItem(Object asset)
{
Asset = asset;
SourceFile = asset.assetsFile;
Type = asset.type;
TypeString = Type.ToString();
m_PathID = asset.m_PathID;
FullSize = asset.byteSize;
}
}
}

View File

@ -0,0 +1,49 @@
using System;
namespace AssetStudioCLI
{
// Represents set with 16 base colors using ANSI escape codes, which should be supported in most terminals
// (well, except for windows editions before windows 10)
public static class CLIAnsiColors
{
public static readonly string
Black = "\u001b[30m",
Red = "\u001b[31m",
Green = "\u001b[32m",
Yellow = "\u001b[33m", //remapped to ~BrightWhite in Windows PowerShell 6
Blue = "\u001b[34m",
Magenta = "\u001b[35m", //remapped to ~Blue in Windows PowerShell 6
Cyan = "\u001b[36m",
White = "\u001b[37m",
BrightBlack = "\u001b[30;1m",
BrightRed = "\u001b[31;1m",
BrightGreen = "\u001b[32;1m",
BrightYellow = "\u001b[33;1m",
BrightBlue = "\u001b[34;1m",
BrightMagenta = "\u001b[35;1m",
BrightCyan = "\u001b[36;1m",
BrightWhite = "\u001b[37;1m";
private static readonly string Reset = "\u001b[0m";
public static string Color(this string str, string ansiColor)
{
if (!CLIWinAnsiFix.isAnsiSupported)
{
return str;
}
return $"{ansiColor}{str}{Reset}";
}
public static void ANSICodesTest()
{
Console.WriteLine("ANSI escape codes test");
Console.WriteLine($"Supported: {CLIWinAnsiFix.isAnsiSupported}");
Console.WriteLine("\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m");
Console.WriteLine("\u001b[34m E \u001b[35m F \u001b[36m G \u001b[37m H \u001b[0m");
Console.WriteLine("\u001b[30;1m A \u001b[31;1m B \u001b[32;1m C \u001b[33;1m D \u001b[0m");
Console.WriteLine("\u001b[34;1m E \u001b[35;1m F \u001b[36;1m G \u001b[37;1m H \u001b[0m");
}
}
}

View File

@ -0,0 +1,62 @@
// Based on code by tomzorz (https://gist.github.com/tomzorz/6142d69852f831fb5393654c90a1f22e)
using System;
using System.Runtime.InteropServices;
namespace AssetStudioCLI
{
static class CLIWinAnsiFix
{
public static readonly bool isAnsiSupported;
private const int STD_OUTPUT_HANDLE = -11;
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
[DllImport("kernel32.dll")]
private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll")]
private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(int nStdHandle);
static CLIWinAnsiFix()
{
bool isWin = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
if (isWin)
{
isAnsiSupported = TryEnableVTMode();
if (!isAnsiSupported)
{
//Check for bash terminal emulator. E.g., Git Bash, Cmder
isAnsiSupported = Environment.GetEnvironmentVariable("TERM") != null;
}
}
else
{
isAnsiSupported = true;
}
}
// Enable support for ANSI escape codes
// (but probably only suitable for windows 10+)
// https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
private static bool TryEnableVTMode()
{
var iStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (!GetConsoleMode(iStdOut, out uint outConsoleMode))
{
return false;
}
outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode(iStdOut, outConsoleMode))
{
return false;
}
return true;
}
}
}

289
AssetStudioCLI/Exporter.cs Normal file
View File

@ -0,0 +1,289 @@
using AssetStudio;
using AssetStudioCLI.Options;
using Newtonsoft.Json;
using System.IO;
using System.Linq;
namespace AssetStudioCLI
{
internal static class Exporter
{
public static AssemblyLoader assemblyLoader = new AssemblyLoader();
public static bool ExportTexture2D(AssetItem item, string exportPath, CLIOptions options)
{
var m_Texture2D = (Texture2D)item.Asset;
if (options.convertTexture)
{
var type = options.o_imageFormat.Value;
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false;
var image = m_Texture2D.ConvertToImage(flip: true);
if (image == null)
{
Logger.Error($"Failed to convert texture \"{m_Texture2D.m_Name}\" into image");
return false;
}
using (image)
{
using (var file = File.OpenWrite(exportFullPath))
{
image.WriteToStream(file, type);
}
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
}
else
{
if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData());
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
}
public static bool ExportAudioClip(AssetItem item, string exportPath, CLIOptions options)
{
string exportFullPath;
var m_AudioClip = (AudioClip)item.Asset;
var m_AudioData = m_AudioClip.m_AudioData.GetData();
if (m_AudioData == null || m_AudioData.Length == 0)
{
Logger.Error($"[{item.Text}]: AudioData was not found");
return false;
}
var converter = new AudioClipConverter(m_AudioClip);
if (options.o_audioFormat.Value != AudioFormat.None && converter.IsSupport)
{
if (!TryExportFile(exportPath, item, ".wav", out exportFullPath))
return false;
Logger.Debug($"Converting \"{m_AudioClip.m_Name}\" to wav..\n" +
$"AudioClip sound compression: {m_AudioClip.m_CompressionFormat}\n" +
$"AudioClip channel count: {m_AudioClip.m_Channels}\n" +
$"AudioClip sample rate: {m_AudioClip.m_Frequency}\n" +
$"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}");
var buffer = converter.ConvertToWav(m_AudioData);
if (buffer == null)
{
Logger.Error($"[{item.Text}]: Failed to convert to Wav");
return false;
}
File.WriteAllBytes(exportFullPath, buffer);
}
else
{
if (!TryExportFile(exportPath, item, converter.GetExtensionName(), out exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_AudioData);
}
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportVideoClip(AssetItem item, string exportPath)
{
var m_VideoClip = (VideoClip)item.Asset;
if (m_VideoClip.m_ExternalResources.m_Size > 0)
{
if (!TryExportFile(exportPath, item, Path.GetExtension(m_VideoClip.m_OriginalPath), out var exportFullPath))
return false;
m_VideoClip.m_VideoData.WriteData(exportFullPath);
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
return false;
}
public static bool ExportShader(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".shader", out var exportFullPath))
return false;
var m_Shader = (Shader)item.Asset;
var str = m_Shader.Convert();
File.WriteAllText(exportFullPath, str);
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportTextAsset(AssetItem item, string exportPath, CLIOptions options)
{
var m_TextAsset = (TextAsset)item.Asset;
var extension = ".txt";
if (!options.f_notRestoreExtensionName.Value)
{
if (!string.IsNullOrEmpty(item.Container))
{
extension = Path.GetExtension(item.Container);
}
}
if (!TryExportFile(exportPath, item, extension, out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_TextAsset.m_Script);
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportMonoBehaviour(AssetItem item, string exportPath, CLIOptions options)
{
if (!TryExportFile(exportPath, item, ".json", out var exportFullPath))
return false;
var m_MonoBehaviour = (MonoBehaviour)item.Asset;
var type = m_MonoBehaviour.ToType();
if (type == null)
{
var m_Type = MonoBehaviourToTypeTree(m_MonoBehaviour, options);
type = m_MonoBehaviour.ToType(m_Type);
}
var str = JsonConvert.SerializeObject(type, Formatting.Indented);
File.WriteAllText(exportFullPath, str);
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportFont(AssetItem item, string exportPath)
{
var m_Font = (Font)item.Asset;
if (m_Font.m_FontData != null)
{
var extension = ".ttf";
if (m_Font.m_FontData[0] == 79 && m_Font.m_FontData[1] == 84 && m_Font.m_FontData[2] == 84 && m_Font.m_FontData[3] == 79)
{
extension = ".otf";
}
if (!TryExportFile(exportPath, item, extension, out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_Font.m_FontData);
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
return false;
}
public static bool ExportSprite(AssetItem item, string exportPath, CLIOptions options)
{
var type = options.o_imageFormat.Value;
var alphaMask = SpriteMaskMode.On;
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false;
var image = ((Sprite)item.Asset).GetImage(alphaMask);
if (image != null)
{
using (image)
{
using (var file = File.OpenWrite(exportFullPath))
{
image.WriteToStream(file, type);
}
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
}
return false;
}
public static bool ExportRawFile(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportDumpFile(AssetItem item, string exportPath, CLIOptions options)
{
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath))
return false;
var str = item.Asset.Dump();
if (str == null && item.Asset is MonoBehaviour m_MonoBehaviour)
{
var m_Type = MonoBehaviourToTypeTree(m_MonoBehaviour, options);
str = m_MonoBehaviour.Dump(m_Type);
}
if (str != null)
{
File.WriteAllText(exportFullPath, str);
Logger.Debug($"{item.TypeString}: \"{item.Text}\" saved to \"{exportFullPath}\"");
return true;
}
return false;
}
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath)
{
var fileName = FixFileName(item.Text);
fullPath = Path.Combine(dir, fileName + extension);
if (!File.Exists(fullPath))
{
Directory.CreateDirectory(dir);
return true;
}
fullPath = Path.Combine(dir, fileName + item.UniqueID + extension);
if (!File.Exists(fullPath))
{
Directory.CreateDirectory(dir);
return true;
}
Logger.Error($"Export error. File \"{fullPath.Color(CLIAnsiColors.BrightRed)}\" already exist");
return false;
}
public static bool ExportConvertFile(AssetItem item, string exportPath, CLIOptions options)
{
switch (item.Type)
{
case ClassIDType.Texture2D:
return ExportTexture2D(item, exportPath, options);
case ClassIDType.AudioClip:
return ExportAudioClip(item, exportPath, options);
case ClassIDType.VideoClip:
return ExportVideoClip(item, exportPath);
case ClassIDType.Shader:
return ExportShader(item, exportPath);
case ClassIDType.TextAsset:
return ExportTextAsset(item, exportPath, options);
case ClassIDType.MonoBehaviour:
return ExportMonoBehaviour(item, exportPath, options);
case ClassIDType.Font:
return ExportFont(item, exportPath);
case ClassIDType.Sprite:
return ExportSprite(item, exportPath, options);
default:
return ExportRawFile(item, exportPath);
}
}
public static TypeTree MonoBehaviourToTypeTree(MonoBehaviour m_MonoBehaviour, CLIOptions options)
{
if (!assemblyLoader.Loaded)
{
var assemblyFolder = options.o_assemblyPath.Value;
if (assemblyFolder != "")
{
assemblyLoader.Load(assemblyFolder);
}
else
{
assemblyLoader.Loaded = true;
}
}
return m_MonoBehaviour.ConvertToTypeTree(assemblyLoader);
}
public static string FixFileName(string str)
{
if (str.Length >= 260) return Path.GetRandomFileName();
return Path.GetInvalidFileNameChars().Aggregate(str, (current, c) => current.Replace(c, '_'));
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,790 @@
using AssetStudio;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace AssetStudioCLI.Options
{
internal enum HelpGroups
{
General,
Convert,
Logger,
Advanced,
}
internal enum WorkMode
{
Export,
ExportRaw,
Dump,
Info,
}
internal enum AssetGroupOption
{
None,
TypeName,
ContainerPath,
SourceFileName,
}
internal enum ExportListType
{
None,
XML,
}
internal enum AudioFormat
{
None,
Wav,
}
internal enum FilterBy
{
None,
Name,
Container,
PathID,
NameOrContainer,
NameAndContainer,
}
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)
{
CLIOptions.OptionGrouping(optionName, optionDescription, optionHelpGroup, isFlag);
}
}
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> flagsDict;
private static Dictionary<HelpGroups, Dictionary<string, string>> optionGroups;
private List<ClassIDType> supportedAssetTypes;
//general
public Option<WorkMode> o_workMode;
public Option<List<ClassIDType>> o_exportAssetTypes;
public Option<AssetGroupOption> o_groupAssetsBy;
public Option<string> o_outputFolder;
public Option<bool> o_displayHelp;
//logger
public Option<LoggerEvent> o_logLevel;
public Option<LogOutputMode> o_logOutput;
//convert
public bool convertTexture;
public Option<ImageFormat> o_imageFormat;
public Option<AudioFormat> o_audioFormat;
//advanced
public Option<ExportListType> o_exportAssetList;
public Option<List<string>> o_filterByName;
public Option<List<string>> o_filterByContainer;
public Option<List<string>> o_filterByPathID;
public Option<List<string>> o_filterByText;
public Option<string> o_assemblyPath;
public Option<string> o_unityVersion;
public Option<bool> f_notRestoreExtensionName;
public CLIOptions(string[] args)
{
cliArgs = args;
InitOptions();
ParseArgs(args);
}
private void InitOptions()
{
isParsed = false;
showHelp = false;
inputPath = "";
filterBy = FilterBy.None;
optionsDict = new Dictionary<string, string>();
flagsDict = new Dictionary<string, string>();
optionGroups = new Dictionary<HelpGroups, Dictionary<string, string>>();
supportedAssetTypes = new List<ClassIDType>
{
ClassIDType.Texture2D,
ClassIDType.Sprite,
ClassIDType.TextAsset,
ClassIDType.MonoBehaviour,
ClassIDType.Font,
ClassIDType.Shader,
ClassIDType.AudioClip,
ClassIDType.VideoClip,
};
#region Init General Options
o_workMode = new GroupedOption<WorkMode>
(
optionDefaultValue: WorkMode.Export,
optionName: "-m, --mode <value>",
optionDescription: "Specify working mode\n" +
"<Value: export(default) | exportRaw | dump | info>\n" +
"Export - Exports converted assets\n" +
"ExportRaw - Exports raw data\n" +
"Dump - Makes asset dumps\n" +
"Info - Loads file(s), shows the number of supported for export assets and exits\n" +
"Example: \"-m info\"\n",
optionHelpGroup: HelpGroups.General
);
o_exportAssetTypes = new GroupedOption<List<ClassIDType>>
(
optionDefaultValue: supportedAssetTypes,
optionName: "-t, --asset-type <value(s)>",
optionDescription: "Specify asset type(s) to export\n" +
"<Value(s): tex2d, sprite, textAsset, monoBehaviour, font, shader, \naudio, video | all(default)>\n" +
"All - export all asset types, which are listed in the values\n" +
"*To specify multiple asset types, write them separated by ',' or ';' without spaces\n" +
"Examples: \"-t sprite\" or \"-t all\" or \"-t tex2d,sprite,audio\" or \"-t tex2d;sprite;font\"\n",
optionHelpGroup: HelpGroups.General
);
o_groupAssetsBy = new GroupedOption<AssetGroupOption>
(
optionDefaultValue: AssetGroupOption.ContainerPath,
optionName: "-g, --group-option <value>",
optionDescription: "Specify the way in which exported assets should be grouped\n" +
"<Value: none | type | container(default) | filename>\n" +
"None - Do not group exported assets\n" +
"Type - Group exported assets by type name\n" +
"Container - Group exported assets by container path\n" +
"Filename - Group exported assets by source file name\n" +
"Example: \"-g container\"\n",
optionHelpGroup: HelpGroups.General
);
o_outputFolder = new GroupedOption<string>
(
optionDefaultValue: "",
optionName: "-o, --output <path>",
optionDescription: "Specify path to the output folder\n" +
"If path isn't specifyed, 'ASExport' folder will be created in the program's work folder\n",
optionHelpGroup: HelpGroups.General
);
o_displayHelp = new GroupedOption<bool>
(
optionDefaultValue: false,
optionName: "-h, --help",
optionDescription: "Display help and exit",
optionHelpGroup: HelpGroups.General
);
#endregion
#region Init Logger Options
o_logLevel = new GroupedOption<LoggerEvent>
(
optionDefaultValue: LoggerEvent.Info,
optionName: "--log-level <value>",
optionDescription: "Specify the log level\n" +
"<Value: verbose | debug | info(default) | warning | error>\n" +
"Example: \"--log-level warning\"\n",
optionHelpGroup: HelpGroups.Logger
);
o_logOutput = new GroupedOption<LogOutputMode>
(
optionDefaultValue: LogOutputMode.Console,
optionName: "--log-output <value>",
optionDescription: "Specify the log output\n" +
"<Value: console(default) | file | both>\n" +
"Example: \"--log-output both\"",
optionHelpGroup: HelpGroups.Logger
);
#endregion
#region Init Convert Options
convertTexture = true;
o_imageFormat = new GroupedOption<ImageFormat>
(
optionDefaultValue: ImageFormat.Png,
optionName: "--image-format <value>",
optionDescription: "Specify the format for converting image assets\n" +
"<Value: none | jpg | png(default) | bmp | tga | webp>\n" +
"None - Do not convert images and export them as texture data (.tex)\n" +
"Example: \"--image-format jpg\"\n",
optionHelpGroup: HelpGroups.Convert
);
o_audioFormat = new GroupedOption<AudioFormat>
(
optionDefaultValue: AudioFormat.Wav,
optionName: "--audio-format <value>",
optionDescription: "Specify the format for converting audio assets\n" +
"<Value: none | wav(default)>\n" +
"None - Do not convert audios and export them in their own format\n" +
"Example: \"--audio-format wav\"",
optionHelpGroup: HelpGroups.Convert
);
#endregion
#region Init Advanced Options
o_exportAssetList = new GroupedOption<ExportListType>
(
optionDefaultValue: ExportListType.None,
optionName: "--export-asset-list <value>",
optionDescription: "Specify the format in which you want to export asset list\n" +
"<Value: none(default) | xml>\n" +
"None - Do not export asset list\n" +
"Example: \"--export-asset-list xml\"\n",
optionHelpGroup: HelpGroups.Advanced
);
o_filterByName = new GroupedOption<List<string>>
(
optionDefaultValue: new List<string>(),
optionName: "--filter-by-name <text>",
optionDescription: "Specify the name by which assets should be filtered\n" +
"*To specify multiple names write them separated by ',' or ';' without spaces\n" +
"Example: \"--filter-by-name char\" or \"--filter-by-name char,bg\"\n",
optionHelpGroup: HelpGroups.Advanced
);
o_filterByContainer = new GroupedOption<List<string>>
(
optionDefaultValue: new List<string>(),
optionName: "--filter-by-container <text>",
optionDescription: "Specify the container by which assets should be filtered\n" +
"*To specify multiple containers write them separated by ',' or ';' without spaces\n" +
"Example: \"--filter-by-container arts\" or \"--filter-by-container arts,icons\"\n",
optionHelpGroup: HelpGroups.Advanced
);
o_filterByPathID = new GroupedOption<List<string>>
(
optionDefaultValue: new List<string>(),
optionName: "--filter-by-pathid <text>",
optionDescription: "Specify the PathID by which assets should be filtered\n" +
"*To specify multiple PathIDs write them separated by ',' or ';' without spaces\n" +
"Example: \"--filter-by-pathid 7238605633795851352,-2430306240205277265\"\n",
optionHelpGroup: HelpGroups.Advanced
);
o_filterByText = new GroupedOption<List<string>>
(
optionDefaultValue: new List<string>(),
optionName: "--filter-by-text <text>",
optionDescription: "Specify the text by which assets should be filtered\n" +
"Looks for assets that contain the specified text in their names or containers\n" +
"*To specify multiple values write them separated by ',' or ';' without spaces\n" +
"Example: \"--filter-by-text portrait\" or \"--filter-by-text portrait,art\"\n",
optionHelpGroup: HelpGroups.Advanced
);
o_assemblyPath = new GroupedOption<string>
(
optionDefaultValue: "",
optionName: "--assembly-folder <path>",
optionDescription: "Specify the path to the assembly folder",
optionHelpGroup: HelpGroups.Advanced
);
o_unityVersion = new GroupedOption<string>
(
optionDefaultValue: "",
optionName: "--unity-version <text>",
optionDescription: "Specify Unity version. Example: \"--unity-version 2017.4.39f1\"",
optionHelpGroup: HelpGroups.Advanced
);
f_notRestoreExtensionName = new GroupedOption<bool>
(
optionDefaultValue: false,
optionName: "--not-restore-extension",
optionDescription: "(Flag) If specified, AssetStudio will not try to restore TextAssets extension name, \nand will just export all TextAssets with the \".txt\" extension",
optionHelpGroup: HelpGroups.Advanced,
isFlag: true
);
#endregion
}
internal 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 void ParseArgs(string[] args)
{
var brightYellow = CLIAnsiColors.BrightYellow;
var brightRed = CLIAnsiColors.BrightRed;
if (args.Length == 0 || args.Any(x => x == "-h" || x == "--help"))
{
showHelp = true;
return;
}
if (!args[0].StartsWith("-"))
{
inputPath = Path.GetFullPath(args[0]).Replace("\"", "");
if (!Directory.Exists(inputPath) && !File.Exists(inputPath))
{
Console.WriteLine($"{"Error:".Color(brightRed)} Invalid input path \"{args[0].Color(brightRed)}\".\n" +
$"Specified file or folder was not found. The input path must be specified as the first argument.");
return;
}
o_outputFolder.Value = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ASExport");
}
else
{
Console.WriteLine($"{"Error:".Color(brightRed)} Input path was empty. Specify the input path as the first argument.");
return;
}
var resplittedArgs = new List<string>();
for (int i = 1; i < args.Length; i++)
{
string arg = args[i];
if (arg.Contains('='))
{
var splittedArgs = arg.Split('=');
resplittedArgs.Add(splittedArgs[0]);
resplittedArgs.Add(splittedArgs[1]);
}
else
{
resplittedArgs.Add(arg);
}
};
#region Parse Flags
for (int i = 0; i < resplittedArgs.Count; i++)
{
string flag = resplittedArgs[i].ToLower();
switch(flag)
{
case "--not-restore-extension":
f_notRestoreExtensionName.Value = true;
resplittedArgs.RemoveAt(i);
break;
}
}
#endregion
#region Parse Options
for (int i = 0; i < resplittedArgs.Count; i++)
{
var option = resplittedArgs[i].ToLower();
try
{
var value = resplittedArgs[i + 1].Replace("\"", "");
switch (option)
{
case "-m":
case "--mode":
switch (value.ToLower())
{
case "export":
o_workMode.Value = WorkMode.Export;
break;
case "raw":
case "exportraw":
o_workMode.Value = WorkMode.ExportRaw;
break;
case "dump":
o_workMode.Value = WorkMode.Dump;
break;
case "info":
o_workMode.Value = WorkMode.Info;
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported working mode: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_workMode.Description);
return;
}
break;
case "-t":
case "--asset-type":
var splittedTypes = ValueSplitter(value);
o_exportAssetTypes.Value = new List<ClassIDType>();
foreach (var type in splittedTypes)
{
switch (type.ToLower())
{
case "tex2d":
case "texture2d":
o_exportAssetTypes.Value.Add(ClassIDType.Texture2D);
break;
case "sprite":
o_exportAssetTypes.Value.Add(ClassIDType.Sprite);
break;
case "textasset":
o_exportAssetTypes.Value.Add(ClassIDType.TextAsset);
break;
case "monobehaviour":
o_exportAssetTypes.Value.Add(ClassIDType.MonoBehaviour);
break;
case "font":
o_exportAssetTypes.Value.Add(ClassIDType.Font);
break;
case "shader":
o_exportAssetTypes.Value.Add(ClassIDType.Shader);
break;
case "audio":
case "audioclip":
o_exportAssetTypes.Value.Add(ClassIDType.AudioClip);
break;
case "video":
case "videoclip":
o_exportAssetTypes.Value.Add(ClassIDType.VideoClip);
break;
case "all":
o_exportAssetTypes.Value = supportedAssetTypes;
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported asset type: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_exportAssetTypes.Description);
return;
}
}
break;
case "-g":
case "--group-option":
switch (value.ToLower())
{
case "type":
o_groupAssetsBy.Value = AssetGroupOption.TypeName;
break;
case "container":
o_groupAssetsBy.Value = AssetGroupOption.ContainerPath;
break;
case "filename":
o_groupAssetsBy.Value = AssetGroupOption.SourceFileName;
break;
case "none":
o_groupAssetsBy.Value = AssetGroupOption.None;
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported grouping option: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_groupAssetsBy.Description);
return;
}
break;
case "-o":
case "--output":
try
{
value = Path.GetFullPath(value);
if (!Directory.Exists(value))
{
Directory.CreateDirectory(value);
}
o_outputFolder.Value = value;
}
catch (Exception ex)
{
Console.WriteLine($"{"Warning:".Color(brightYellow)} Invalid output folder \"{value.Color(brightYellow)}\".\n{ex.Message}");
Console.WriteLine($"Working folder \"{o_outputFolder.Value.Color(brightYellow)}\" will be used as the output folder.\n");
Console.WriteLine("Press ESC to exit or any other key to continue...\n");
switch (Console.ReadKey(intercept: true).Key)
{
case ConsoleKey.Escape:
return;
}
}
break;
case "--log-level":
switch (value.ToLower())
{
case "verbose":
o_logLevel.Value = LoggerEvent.Verbose;
break;
case "debug":
o_logLevel.Value = LoggerEvent.Debug;
break;
case "info":
o_logLevel.Value = LoggerEvent.Info;
break;
case "warning":
o_logLevel.Value = LoggerEvent.Warning;
break;
case "error":
o_logLevel.Value = LoggerEvent.Error;
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported log level value: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_logLevel.Description);
return;
}
break;
case "--log-output":
switch (value.ToLower())
{
case "console":
o_logOutput.Value = LogOutputMode.Console;
break;
case "file":
o_logOutput.Value = LogOutputMode.File;
break;
case "both":
o_logOutput.Value = LogOutputMode.Both;
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported log output mode: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_logOutput.Description);
return;
}
break;
case "--image-format":
switch (value.ToLower())
{
case "jpg":
case "jpeg":
o_imageFormat.Value = ImageFormat.Jpeg;
break;
case "png":
o_imageFormat.Value = ImageFormat.Png;
break;
case "bmp":
o_imageFormat.Value = ImageFormat.Bmp;
break;
case "tga":
o_imageFormat.Value = ImageFormat.Tga;
break;
case "webp":
o_imageFormat.Value = ImageFormat.Webp;
break;
case "none":
convertTexture = false;
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported image format: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_imageFormat.Description);
return;
}
break;
case "--audio-format":
switch (value.ToLower())
{
case "wav":
case "wave":
o_audioFormat.Value = AudioFormat.Wav;
break;
case "none":
o_audioFormat.Value = AudioFormat.None;
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported audio format: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_audioFormat.Description);
return;
}
break;
case "--export-asset-list":
switch (value.ToLower())
{
case "xml":
o_exportAssetList.Value = ExportListType.XML;
break;
case "none":
o_exportAssetList.Value = ExportListType.None;
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported asset list export option: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_exportAssetList.Description);
return;
}
break;
case "--filter-by-name":
o_filterByName.Value.AddRange(ValueSplitter(value));
filterBy = filterBy == FilterBy.None ? FilterBy.Name : filterBy == FilterBy.Container ? FilterBy.NameAndContainer : filterBy;
break;
case "--filter-by-container":
o_filterByContainer.Value.AddRange(ValueSplitter(value));
filterBy = filterBy == FilterBy.None ? FilterBy.Container : filterBy == FilterBy.Name ? FilterBy.NameAndContainer : filterBy;
break;
case "--filter-by-pathid":
o_filterByPathID.Value.AddRange(ValueSplitter(value));
filterBy = FilterBy.PathID;
break;
case "--filter-by-text":
o_filterByText.Value.AddRange(ValueSplitter(value));
filterBy = FilterBy.NameOrContainer;
break;
case "--assembly-folder":
if (Directory.Exists(value))
{
o_assemblyPath.Value = value;
}
else
{
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Assembly folder [{value.Color(brightRed)}] was not found.");
return;
}
break;
case "--unity-version":
o_unityVersion.Value = value;
break;
default:
Console.WriteLine($"{"Error:".Color(brightRed)} Unknown option [{option.Color(brightRed)}].\n");
if (!TryShowOptionDescription(option, optionsDict))
{
TryShowOptionDescription(option, flagsDict);
}
return;
}
i++;
}
catch (IndexOutOfRangeException)
{
if (optionsDict.Any(x => x.Key.Contains(option)))
{
Console.WriteLine($"{"Error during parsing options:".Color(brightRed)} Value for [{option.Color(brightRed)}] option was not found.\n");
TryShowOptionDescription(option, optionsDict);
}
else if (flagsDict.Any(x => x.Key.Contains(option)))
{
Console.WriteLine($"{"Error:".Color(brightRed)} Unknown flag [{option.Color(brightRed)}].\n");
TryShowOptionDescription(option, flagsDict);
}
else
{
Console.WriteLine($"{"Error:".Color(brightRed)} Unknown option [{option.Color(brightRed)}].");
}
return;
}
catch (Exception ex)
{
Console.WriteLine("Unknown Error.".Color(CLIAnsiColors.Red));
Console.WriteLine(ex);
return;
}
}
isParsed = true;
#endregion
}
private static string[] ValueSplitter(string value)
{
var separator = value.Contains(';') ? ';' : ',';
return value.Split(separator);
}
private bool TryShowOptionDescription(string option, Dictionary<string, string> descDict)
{
var optionDesc = descDict.Where(x => x.Key.Contains(option));
if (optionDesc.Any())
{
var rand = new Random();
var rndOption = optionDesc.ElementAt(rand.Next(0, optionDesc.Count()));
Console.WriteLine($"Did you mean [{ $"{rndOption.Key}".Color(CLIAnsiColors.BrightYellow) }] option?");
Console.WriteLine($"Here's a description of it: \n\n{rndOption.Value}");
return true;
}
return false;
}
public void ShowHelp(bool showUsageOnly = false)
{
const int indent = 22;
var helpMessage = new StringBuilder();
var usage = new StringBuilder();
var appAssembly = typeof(Program).Assembly.GetName();
usage.Append($"Usage: {appAssembly.Name} <input path to asset file/folder> ");
var i = 0;
foreach (var optionsGroup in optionGroups.Keys)
{
helpMessage.AppendLine($"{optionsGroup} Options:");
foreach (var optionDict in optionGroups[optionsGroup])
{
var optionName = $"{optionDict.Key,-indent - 8}";
var optionDesc = optionDict.Value.Replace("\n", $"{"\n",-indent - 11}");
helpMessage.AppendLine($" {optionName}{optionDesc}");
usage.Append($"[{optionDict.Key}] ");
if (i++ % 2 == 0)
{
usage.Append($"\n{"",indent}");
}
}
helpMessage.AppendLine();
}
if (showUsageOnly)
{
Console.WriteLine(usage);
}
else
{
Console.WriteLine($"# {appAssembly.Name}\n# Based on AssetStudio Mod v{appAssembly.Version}\n");
Console.WriteLine($"{usage}\n\n{helpMessage}");
}
}
private string ShowCurrentFilter()
{
switch (filterBy)
{
case FilterBy.Name:
return $"# Filter by {filterBy}(s): \"{string.Join("\", \"", o_filterByName.Value)}\"";
case FilterBy.Container:
return $"# Filter by {filterBy}(s): \"{string.Join("\", \"", o_filterByContainer.Value)}\"";
case FilterBy.PathID:
return $"# Filter by {filterBy}(s): \"{string.Join("\", \"", o_filterByPathID.Value)}\"";
case FilterBy.NameOrContainer:
return $"# Filter by Text: \"{string.Join("\", \"", o_filterByText.Value)}\"";
case FilterBy.NameAndContainer:
return $"# Filter by Name(s): \"{string.Join("\", \"", o_filterByName.Value)}\"\n# Filter by Container(s): \"{string.Join("\", \"", o_filterByContainer.Value)}\"";
default:
return $"# Filter by: {filterBy}";
}
}
public void ShowCurrentOptions()
{
var sb = new StringBuilder();
sb.AppendLine("[Current Options]");
sb.AppendLine($"# Working Mode: {o_workMode}");
sb.AppendLine($"# Input Path: \"{inputPath}\"");
if (o_workMode.Value != WorkMode.Info)
{
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}");
}
else
{
sb.AppendLine($"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}");
sb.AppendLine($"# Log Level: {o_logLevel}");
sb.AppendLine($"# Log Output: {o_logOutput}");
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
sb.AppendLine(ShowCurrentFilter());
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
}
sb.AppendLine("======");
Logger.Info(sb.ToString());
}
}
}

View File

@ -0,0 +1,27 @@
namespace AssetStudioCLI.Options
{
internal class Option<T>
{
public string Name { get; }
public string Description { get; }
public T Value { get; set; }
public T DefaultValue { get; }
public HelpGroups HelpGroup { get; }
public bool IsFlag { get; }
public Option(T optionDefaultValue, string optionName, string optionDescription, HelpGroups optionHelpGroup, bool isFlag)
{
Name = optionName;
Description = optionDescription;
DefaultValue = optionDefaultValue;
Value = DefaultValue;
HelpGroup = optionHelpGroup;
IsFlag = isFlag;
}
public override string ToString()
{
return Value != null ? Value.ToString() : string.Empty;
}
}
}

65
AssetStudioCLI/Program.cs Normal file
View File

@ -0,0 +1,65 @@
using AssetStudio;
using AssetStudioCLI.Options;
using System;
namespace AssetStudioCLI
{
class Program
{
public static void Main(string[] args)
{
var options = new CLIOptions(args);
if (options.isParsed)
{
CLIRun(options);
}
else if (options.showHelp)
{
options.ShowHelp();
}
else
{
Console.WriteLine();
options.ShowHelp(showUsageOnly: true);
}
}
private static void CLIRun(CLIOptions options)
{
var cliLogger = new CLILogger(options);
Logger.Default = cliLogger;
var studio = new Studio(options);
options.ShowCurrentOptions();
try
{
if (studio.LoadAssets())
{
studio.ParseAssets();
if (options.filterBy != FilterBy.None)
{
studio.FilterAssets();
}
if (options.o_exportAssetList.Value != ExportListType.None)
{
studio.ExportAssetList();
}
if (options.o_workMode.Value == WorkMode.Info)
{
studio.ShowExportableAssetsInfo();
return;
}
studio.ExportAssets();
}
}
catch (Exception ex)
{
Logger.Error(ex.ToString());
}
finally
{
cliLogger.LogToFile(LoggerEvent.Verbose, "---Program ended---");
}
}
}
}

376
AssetStudioCLI/Studio.cs Normal file
View File

@ -0,0 +1,376 @@
using AssetStudio;
using AssetStudioCLI.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using static AssetStudioCLI.Exporter;
using Ansi = AssetStudioCLI.CLIAnsiColors;
namespace AssetStudioCLI
{
internal class Studio
{
public AssetsManager assetsManager = new AssetsManager();
public List<AssetItem> parsedAssetsList = new List<AssetItem>();
private readonly CLIOptions options;
public Studio(CLIOptions cliOptions)
{
Progress.Default = new Progress<int>(ShowCurProgressValue);
options = cliOptions;
}
private void ShowCurProgressValue(int value)
{
Console.Write($"[{value:000}%]\r");
}
public bool LoadAssets()
{
var isLoaded = false;
assetsManager.SpecifyUnityVersion = options.o_unityVersion.Value;
if (Directory.Exists(options.inputPath))
{
assetsManager.LoadFolder(options.inputPath);
}
else
{
assetsManager.LoadFiles(options.inputPath);
}
if (assetsManager.assetsFileList.Count == 0)
{
Logger.Warning("No Unity file can be loaded.");
}
else
{
isLoaded = true;
}
return isLoaded;
}
public void ParseAssets()
{
Logger.Info("Parse assets...");
var fileAssetsList = new List<AssetItem>();
var containers = new Dictionary<AssetStudio.Object, string>();
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
Progress.Reset();
var i = 0;
foreach (var assetsFile in assetsManager.assetsFileList)
{
foreach (var asset in assetsFile.Objects)
{
var assetItem = new AssetItem(asset);
assetItem.UniqueID = "_#" + i;
var isExportable = false;
switch (asset)
{
case AssetBundle m_AssetBundle:
foreach (var m_Container in m_AssetBundle.m_Container)
{
var preloadIndex = m_Container.Value.preloadIndex;
var preloadSize = m_Container.Value.preloadSize;
var preloadEnd = preloadIndex + preloadSize;
for (int k = preloadIndex; k < preloadEnd; k++)
{
var pptr = m_AssetBundle.m_PreloadTable[k];
if (pptr.TryGet(out var obj))
{
containers[obj] = m_Container.Key;
}
}
}
break;
case ResourceManager m_ResourceManager:
foreach (var m_Container in m_ResourceManager.m_Container)
{
if (m_Container.Value.TryGet(out var obj))
{
containers[obj] = m_Container.Key;
}
}
break;
case Texture2D m_Texture2D:
if (!string.IsNullOrEmpty(m_Texture2D.m_StreamData?.path))
assetItem.FullSize = asset.byteSize + m_Texture2D.m_StreamData.size;
assetItem.Text = m_Texture2D.m_Name;
break;
case AudioClip m_AudioClip:
if (!string.IsNullOrEmpty(m_AudioClip.m_Source))
assetItem.FullSize = asset.byteSize + m_AudioClip.m_Size;
assetItem.Text = m_AudioClip.m_Name;
break;
case VideoClip m_VideoClip:
if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath))
assetItem.FullSize = asset.byteSize + m_VideoClip.m_ExternalResources.m_Size;
assetItem.Text = m_VideoClip.m_Name;
break;
case TextAsset _:
case Font _:
case Sprite _:
assetItem.Text = ((NamedObject)asset).m_Name;
break;
case Shader m_Shader:
assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name;
break;
case MonoBehaviour m_MonoBehaviour:
if (m_MonoBehaviour.m_Name == "" && m_MonoBehaviour.m_Script.TryGet(out var m_Script))
{
assetItem.Text = m_Script.m_ClassName;
}
else
{
assetItem.Text = m_MonoBehaviour.m_Name;
}
break;
}
if (assetItem.Text == "")
{
assetItem.Text = assetItem.TypeString + assetItem.UniqueID;
}
isExportable = options.o_exportAssetTypes.Value.Contains(asset.type);
if (isExportable)
{
fileAssetsList.Add(assetItem);
}
Progress.Report(++i, objectCount);
}
foreach (var asset in fileAssetsList)
{
if (containers.ContainsKey(asset.Asset))
{
asset.Container = containers[asset.Asset];
}
}
parsedAssetsList.AddRange(fileAssetsList);
containers.Clear();
fileAssetsList.Clear();
}
}
public void ShowExportableAssetsInfo()
{
var exportableAssetsCountDict = new Dictionary<ClassIDType, int>();
if (parsedAssetsList.Count > 0)
{
foreach (var asset in parsedAssetsList)
{
if (exportableAssetsCountDict.ContainsKey(asset.Type))
{
exportableAssetsCountDict[asset.Type] += 1;
}
else
{
exportableAssetsCountDict.Add(asset.Type, 1);
}
}
var info = "\n[Exportable Assets Count]\n";
foreach (var assetType in exportableAssetsCountDict.Keys)
{
info += $"# {assetType}: {exportableAssetsCountDict[assetType]}\n";
}
if (exportableAssetsCountDict.Count > 1)
{
info += $"#\n# Total: {parsedAssetsList.Count} assets";
}
if (options.o_logLevel.Value > LoggerEvent.Info)
{
Console.WriteLine(info);
}
else
{
Logger.Info(info);
}
}
}
public void FilterAssets()
{
var assetsCount = parsedAssetsList.Count;
var filteredAssets = new List<AssetItem>();
switch(options.filterBy)
{
case FilterBy.Name:
filteredAssets = parsedAssetsList.FindAll(x => options.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", options.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
);
break;
case FilterBy.Container:
filteredAssets = parsedAssetsList.FindAll(x => options.o_filterByContainer.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", options.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers."
);
break;
case FilterBy.PathID:
filteredAssets = parsedAssetsList.FindAll(x => options.o_filterByPathID.Value.Any(y => x.m_PathID.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", options.o_filterByPathID.Value)}\"".Color(Ansi.BrightYellow)} in their PathIDs."
);
break;
case FilterBy.NameOrContainer:
filteredAssets = parsedAssetsList.FindAll(x =>
options.o_filterByText.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) ||
options.o_filterByText.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
);
Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", options.o_filterByText.Value)}\"".Color(Ansi.BrightYellow)} in their Names or Contaniers."
);
break;
case FilterBy.NameAndContainer:
filteredAssets = parsedAssetsList.FindAll(x =>
options.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) &&
options.o_filterByContainer.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
);
Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", options.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers " +
$"and {$"\"{string.Join("\", \"", options.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
);
break;
}
parsedAssetsList.Clear();
parsedAssetsList = filteredAssets;
}
public void ExportAssets()
{
var savePath = options.o_outputFolder.Value;
var toExportCount = parsedAssetsList.Count;
var exportedCount = 0;
foreach (var asset in parsedAssetsList)
{
string exportPath;
switch (options.o_groupAssetsBy.Value)
{
case AssetGroupOption.TypeName:
exportPath = Path.Combine(savePath, asset.TypeString);
break;
case AssetGroupOption.ContainerPath:
if (!string.IsNullOrEmpty(asset.Container))
{
exportPath = Path.Combine(savePath, Path.GetDirectoryName(asset.Container));
}
else
{
exportPath = savePath;
}
break;
case AssetGroupOption.SourceFileName:
if (string.IsNullOrEmpty(asset.SourceFile.originalPath))
{
exportPath = Path.Combine(savePath, asset.SourceFile.fileName + "_export");
}
else
{
exportPath = Path.Combine(savePath, Path.GetFileName(asset.SourceFile.originalPath) + "_export", asset.SourceFile.fileName);
}
break;
default:
exportPath = savePath;
break;
}
exportPath += Path.DirectorySeparatorChar;
try
{
switch (options.o_workMode.Value)
{
case WorkMode.ExportRaw:
Logger.Debug($"{options.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
if (ExportRawFile(asset, exportPath))
{
exportedCount++;
}
break;
case WorkMode.Dump:
Logger.Debug($"{options.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
if (ExportDumpFile(asset, exportPath, options))
{
exportedCount++;
}
break;
case WorkMode.Export:
Logger.Debug($"{options.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
if (ExportConvertFile(asset, exportPath, options))
{
exportedCount++;
}
break;
}
}
catch (Exception ex)
{
Logger.Error($"{asset.SourceFile.originalPath}: [{$"{asset.Type}: {asset.Text}".Color(Ansi.BrightRed)}] : Export error\n{ex}");
}
Console.Write($"Exported [{exportedCount}/{toExportCount}]\r");
}
Console.WriteLine("");
if (exportedCount == 0)
{
Logger.Info("Nothing exported.");
}
else if (toExportCount > exportedCount)
{
Logger.Info($"Finished exporting {exportedCount} asset(s) to \"{options.o_outputFolder.Value.Color(Ansi.BrightYellow)}\".");
}
else
{
Logger.Info($"Finished exporting {exportedCount} asset(s) to \"{options.o_outputFolder.Value.Color(Ansi.BrightGreen)}\".");
}
if (toExportCount > exportedCount)
{
Logger.Info($"{toExportCount - exportedCount} asset(s) skipped (not extractable or file(s) already exist).");
}
}
public void ExportAssetList()
{
var savePath = options.o_outputFolder.Value;
switch (options.o_exportAssetList.Value)
{
case ExportListType.XML:
var filename = Path.Combine(savePath, "assets.xml");
var doc = new XDocument(
new XElement("Assets",
new XAttribute("filename", filename),
new XAttribute("createdAt", DateTime.UtcNow.ToString("s")),
parsedAssetsList.Select(
asset => new XElement("Asset",
new XElement("Name", asset.Text),
new XElement("Container", asset.Container),
new XElement("Type", new XAttribute("id", (int)asset.Type), asset.TypeString),
new XElement("PathID", asset.m_PathID),
new XElement("Source", asset.SourceFile.fullName),
new XElement("Size", asset.FullSize)
)
)
)
);
doc.Save(filename);
break;
}
Logger.Info($"Finished exporting asset list with {parsedAssetsList.Count} items.");
}
}
}

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>0.16.47.1</Version> <Version>0.16.48.1</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,16 +1,20 @@
using AssetStudio.FbxInterop; using AssetStudio.FbxInterop;
using AssetStudio.PInvoke;
using System.IO; using System.IO;
#if NETFRAMEWORK
using AssetStudio.PInvoke;
#endif
namespace AssetStudio namespace AssetStudio
{ {
public static partial class Fbx public static partial class Fbx
{ {
#if NETFRAMEWORK
static Fbx() static Fbx()
{ {
DllLoader.PreloadDll(FbxDll.DllName); DllLoader.PreloadDll(FbxDll.DllName);
} }
#endif
public static Vector3 QuaternionToEuler(Quaternion q) public static Vector3 QuaternionToEuler(Quaternion q)
{ {

View File

@ -6,7 +6,7 @@
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>Resources\as.ico</ApplicationIcon> <ApplicationIcon>Resources\as.ico</ApplicationIcon>
<AssemblyTitle>AssetStudio Mod by VaDiM</AssemblyTitle> <AssemblyTitle>AssetStudio Mod by VaDiM</AssemblyTitle>
<Version>0.16.47.1</Version> <Version>0.16.48.1</Version>
<Copyright>Copyright © Perfare 2018-2022</Copyright> <Copyright>Copyright © Perfare 2018-2022</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
@ -40,44 +40,76 @@
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Libraries\x86\fmod.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>x86\fmod.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Libraries\x64\fmod.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>x64\fmod.dll</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' "> <ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
<PackageReference Include="OpenTK" Version="4.6.7" /> <PackageReference Include="OpenTK" Version="4.7.7" />
<Reference Include="OpenTK.WinForms"> <Reference Include="OpenTK.WinForms">
<HintPath>Libraries\OpenTK.WinForms.dll</HintPath> <HintPath>Libraries\OpenTK.WinForms.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<PackageReference Include="OpenTK" Version="3.1.0" /> <PackageReference Include="OpenTK" Version="3.3.3" />
<PackageReference Include="OpenTK.GLControl" Version="3.1.0" /> <PackageReference Include="OpenTK.GLControl" Version="3.3.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup> </ItemGroup>
<Target Name="CopyExtraFiles" AfterTargets="AfterBuild"> <!-- Use local compiled win-x86 and win-x64 Texture2DDecoder libs, because libs from Kyaru.Texture2DDecoder.Windows were compiled with /MD flag -->
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\Win32\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)x86" ContinueOnError="true" />
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\x64\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)x64" ContinueOnError="true" /> <Target Name="CopyExtraFilesPortable" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' OR '$(TargetFramework)' == 'net472' ">
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)x86" ContinueOnError="true" /> <Message Text="Copying extra files for $(TargetFramework)... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)x64" ContinueOnError="true" /> <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)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="$(ProjectDir)Libraries\x86\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\x64\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
</Target> </Target>
<Target Name="PublishExtraFiles" AfterTargets="Publish"> <!-- Publishing an app as framework-dependent produces a cross-platform binary as a dll file, and a platform-specific executable that targets your current platform.
<Copy SourceFiles="$(TargetDir)x86\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)x86" ContinueOnError="true" /> The dll is cross-platform while the executable isn't -->
<Copy SourceFiles="$(TargetDir)x64\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)x64" ContinueOnError="true" /> <Target Name="PublishExtraFilesPortable" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' ">
<Copy SourceFiles="$(TargetDir)x86\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)x86" ContinueOnError="true" /> <Message Text="Publishing extra files for Portable build ($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)x64\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)x64" ContinueOnError="true" /> <Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\fmod.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\fmod.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
</Target> </Target>
</Project>
<!-- Published net472 build of AssetStudioGUI is only suitable for x32(x86)-bit OS -->
<Target Name="PublishExtraFilesNet472Warning" AfterTargets="Publish" Condition=" '$(TargetFramework)' == 'net472' ">
<Message Text="%0a NOTE: Published net472 build of AssetStudioGUI is only suitable for x32(x86)-bit OS." Importance="high" />
<Message Text=" If u need cross-architecture support (x32/x64) in net472, using the binaries created after the build is highly recommended (no need to publish).%0a" Importance="high" />
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(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-x86\native\Texture2DDecoderNative.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" />
</Target>
<Target Name="CopyExtraFilesWin86" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x86' OR ( '$(RuntimeIdentifier)' == 'win7-x86' AND '$(TargetFramework)' != 'net472' ) ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\Win32\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\x86\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesWin64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\x64\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\x64\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesWin" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('win-x')) OR ( '$(RuntimeIdentifier)' == 'win7-x86' AND '$(TargetFramework)' != 'net472' ) ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\fmod.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
</Project>

View File

@ -79,6 +79,7 @@
this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.tabControl1 = new System.Windows.Forms.TabControl(); this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage(); this.tabPage1 = new System.Windows.Forms.TabPage();
this.sceneTreeView = new AssetStudioGUI.GOHierarchy();
this.treeSearch = new System.Windows.Forms.TextBox(); this.treeSearch = new System.Windows.Forms.TextBox();
this.tabPage2 = new System.Windows.Forms.TabPage(); this.tabPage2 = new System.Windows.Forms.TabPage();
this.filterExcludeMode = new System.Windows.Forms.CheckBox(); this.filterExcludeMode = new System.Windows.Forms.CheckBox();
@ -127,7 +128,6 @@
this.dumpSelectedAssetsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.dumpSelectedAssetsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.goToSceneHierarchyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.goToSceneHierarchyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.showOriginalFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.showOriginalFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.sceneTreeView = new AssetStudioGUI.GOHierarchy();
this.menuStrip1.SuspendLayout(); this.menuStrip1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout(); this.splitContainer1.Panel1.SuspendLayout();
@ -573,6 +573,17 @@
this.tabPage1.Text = "Scene Hierarchy"; this.tabPage1.Text = "Scene Hierarchy";
this.tabPage1.UseVisualStyleBackColor = true; this.tabPage1.UseVisualStyleBackColor = true;
// //
// sceneTreeView
//
this.sceneTreeView.CheckBoxes = true;
this.sceneTreeView.Dock = System.Windows.Forms.DockStyle.Fill;
this.sceneTreeView.HideSelection = false;
this.sceneTreeView.Location = new System.Drawing.Point(0, 20);
this.sceneTreeView.Name = "sceneTreeView";
this.sceneTreeView.Size = new System.Drawing.Size(472, 587);
this.sceneTreeView.TabIndex = 1;
this.sceneTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.sceneTreeView_AfterCheck);
//
// treeSearch // treeSearch
// //
this.treeSearch.Dock = System.Windows.Forms.DockStyle.Top; this.treeSearch.Dock = System.Windows.Forms.DockStyle.Top;
@ -599,7 +610,7 @@
this.tabPage2.Text = "Asset List"; this.tabPage2.Text = "Asset List";
this.tabPage2.UseVisualStyleBackColor = true; this.tabPage2.UseVisualStyleBackColor = true;
// //
// filterExludeMode // filterExcludeMode
// //
this.filterExcludeMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.filterExcludeMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.filterExcludeMode.AutoSize = true; this.filterExcludeMode.AutoSize = true;
@ -607,14 +618,15 @@
this.filterExcludeMode.Enabled = false; this.filterExcludeMode.Enabled = false;
this.filterExcludeMode.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.filterExcludeMode.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.filterExcludeMode.ForeColor = System.Drawing.SystemColors.ControlText; this.filterExcludeMode.ForeColor = System.Drawing.SystemColors.ControlText;
this.filterExcludeMode.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.filterExcludeMode.Location = new System.Drawing.Point(409, 2); this.filterExcludeMode.Location = new System.Drawing.Point(409, 2);
this.filterExcludeMode.Name = "filterExludeMode"; this.filterExcludeMode.Name = "filterExcludeMode";
this.filterExcludeMode.Size = new System.Drawing.Size(61, 17); this.filterExcludeMode.Size = new System.Drawing.Size(61, 17);
this.filterExcludeMode.TabIndex = 2; this.filterExcludeMode.TabIndex = 2;
this.filterExcludeMode.Text = "Exclude"; this.filterExcludeMode.Text = "Exclude";
this.filterExcludeMode.TextAlign = System.Drawing.ContentAlignment.BottomRight; this.filterExcludeMode.TextAlign = System.Drawing.ContentAlignment.BottomRight;
this.filterExcludeMode.UseVisualStyleBackColor = false; this.filterExcludeMode.UseVisualStyleBackColor = false;
this.filterExcludeMode.CheckedChanged += new System.EventHandler(this.filterExludeMode_CheckedChanged); this.filterExcludeMode.CheckedChanged += new System.EventHandler(this.filterExcludeMode_CheckedChanged);
// //
// assetListView // assetListView
// //
@ -731,6 +743,7 @@
// progressBar1 // progressBar1
// //
this.progressBar1.Dock = System.Windows.Forms.DockStyle.Bottom; this.progressBar1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.progressBar1.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.progressBar1.Location = new System.Drawing.Point(1, 3); this.progressBar1.Location = new System.Drawing.Point(1, 3);
this.progressBar1.Name = "progressBar1"; this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(478, 18); this.progressBar1.Size = new System.Drawing.Size(478, 18);
@ -782,6 +795,7 @@
this.assetInfoLabel.AutoSize = true; this.assetInfoLabel.AutoSize = true;
this.assetInfoLabel.BackColor = System.Drawing.Color.Transparent; this.assetInfoLabel.BackColor = System.Drawing.Color.Transparent;
this.assetInfoLabel.ForeColor = System.Drawing.SystemColors.ControlLightLight; this.assetInfoLabel.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.assetInfoLabel.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.assetInfoLabel.Location = new System.Drawing.Point(4, 8); this.assetInfoLabel.Location = new System.Drawing.Point(4, 8);
this.assetInfoLabel.Name = "assetInfoLabel"; this.assetInfoLabel.Name = "assetInfoLabel";
this.assetInfoLabel.Size = new System.Drawing.Size(0, 13); this.assetInfoLabel.Size = new System.Drawing.Size(0, 13);
@ -811,6 +825,7 @@
// //
this.FMODcopyright.AutoSize = true; this.FMODcopyright.AutoSize = true;
this.FMODcopyright.ForeColor = System.Drawing.SystemColors.ControlLight; this.FMODcopyright.ForeColor = System.Drawing.SystemColors.ControlLight;
this.FMODcopyright.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.FMODcopyright.Location = new System.Drawing.Point(214, 365); this.FMODcopyright.Location = new System.Drawing.Point(214, 365);
this.FMODcopyright.Name = "FMODcopyright"; this.FMODcopyright.Name = "FMODcopyright";
this.FMODcopyright.Size = new System.Drawing.Size(283, 13); this.FMODcopyright.Size = new System.Drawing.Size(283, 13);
@ -821,6 +836,7 @@
// //
this.FMODinfoLabel.AutoSize = true; this.FMODinfoLabel.AutoSize = true;
this.FMODinfoLabel.ForeColor = System.Drawing.SystemColors.ControlLightLight; this.FMODinfoLabel.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.FMODinfoLabel.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.FMODinfoLabel.Location = new System.Drawing.Point(275, 255); this.FMODinfoLabel.Location = new System.Drawing.Point(275, 255);
this.FMODinfoLabel.Name = "FMODinfoLabel"; this.FMODinfoLabel.Name = "FMODinfoLabel";
this.FMODinfoLabel.Size = new System.Drawing.Size(0, 13); this.FMODinfoLabel.Size = new System.Drawing.Size(0, 13);
@ -830,6 +846,7 @@
// //
this.FMODtimerLabel.AutoSize = true; this.FMODtimerLabel.AutoSize = true;
this.FMODtimerLabel.ForeColor = System.Drawing.SystemColors.ControlLightLight; this.FMODtimerLabel.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.FMODtimerLabel.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.FMODtimerLabel.Location = new System.Drawing.Point(457, 253); this.FMODtimerLabel.Location = new System.Drawing.Point(457, 253);
this.FMODtimerLabel.Name = "FMODtimerLabel"; this.FMODtimerLabel.Name = "FMODtimerLabel";
this.FMODtimerLabel.Size = new System.Drawing.Size(102, 13); this.FMODtimerLabel.Size = new System.Drawing.Size(102, 13);
@ -840,6 +857,7 @@
// //
this.FMODstatusLabel.AutoSize = true; this.FMODstatusLabel.AutoSize = true;
this.FMODstatusLabel.ForeColor = System.Drawing.SystemColors.ControlLightLight; this.FMODstatusLabel.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.FMODstatusLabel.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.FMODstatusLabel.Location = new System.Drawing.Point(214, 255); this.FMODstatusLabel.Location = new System.Drawing.Point(214, 255);
this.FMODstatusLabel.Name = "FMODstatusLabel"; this.FMODstatusLabel.Name = "FMODstatusLabel";
this.FMODstatusLabel.Size = new System.Drawing.Size(47, 13); this.FMODstatusLabel.Size = new System.Drawing.Size(47, 13);
@ -849,6 +867,7 @@
// FMODprogressBar // FMODprogressBar
// //
this.FMODprogressBar.AutoSize = false; this.FMODprogressBar.AutoSize = false;
this.FMODprogressBar.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.FMODprogressBar.Location = new System.Drawing.Point(213, 274); this.FMODprogressBar.Location = new System.Drawing.Point(213, 274);
this.FMODprogressBar.Maximum = 1000; this.FMODprogressBar.Maximum = 1000;
this.FMODprogressBar.Name = "FMODprogressBar"; this.FMODprogressBar.Name = "FMODprogressBar";
@ -861,6 +880,7 @@
// //
// FMODvolumeBar // FMODvolumeBar
// //
this.FMODvolumeBar.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.FMODvolumeBar.LargeChange = 2; this.FMODvolumeBar.LargeChange = 2;
this.FMODvolumeBar.Location = new System.Drawing.Point(460, 303); this.FMODvolumeBar.Location = new System.Drawing.Point(460, 303);
this.FMODvolumeBar.Name = "FMODvolumeBar"; this.FMODvolumeBar.Name = "FMODvolumeBar";
@ -873,6 +893,7 @@
// FMODloopButton // FMODloopButton
// //
this.FMODloopButton.Appearance = System.Windows.Forms.Appearance.Button; this.FMODloopButton.Appearance = System.Windows.Forms.Appearance.Button;
this.FMODloopButton.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.FMODloopButton.Location = new System.Drawing.Point(399, 303); this.FMODloopButton.Location = new System.Drawing.Point(399, 303);
this.FMODloopButton.Name = "FMODloopButton"; this.FMODloopButton.Name = "FMODloopButton";
this.FMODloopButton.Size = new System.Drawing.Size(55, 46); this.FMODloopButton.Size = new System.Drawing.Size(55, 46);
@ -884,6 +905,7 @@
// //
// FMODstopButton // FMODstopButton
// //
this.FMODstopButton.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.FMODstopButton.Location = new System.Drawing.Point(338, 303); this.FMODstopButton.Location = new System.Drawing.Point(338, 303);
this.FMODstopButton.Name = "FMODstopButton"; this.FMODstopButton.Name = "FMODstopButton";
this.FMODstopButton.Size = new System.Drawing.Size(55, 46); this.FMODstopButton.Size = new System.Drawing.Size(55, 46);
@ -894,6 +916,7 @@
// //
// FMODpauseButton // FMODpauseButton
// //
this.FMODpauseButton.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.FMODpauseButton.Location = new System.Drawing.Point(277, 303); this.FMODpauseButton.Location = new System.Drawing.Point(277, 303);
this.FMODpauseButton.Name = "FMODpauseButton"; this.FMODpauseButton.Name = "FMODpauseButton";
this.FMODpauseButton.Size = new System.Drawing.Size(55, 46); this.FMODpauseButton.Size = new System.Drawing.Size(55, 46);
@ -904,6 +927,7 @@
// //
// FMODplayButton // FMODplayButton
// //
this.FMODplayButton.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.FMODplayButton.Location = new System.Drawing.Point(216, 303); this.FMODplayButton.Location = new System.Drawing.Point(216, 303);
this.FMODplayButton.Name = "FMODplayButton"; this.FMODplayButton.Name = "FMODplayButton";
this.FMODplayButton.Size = new System.Drawing.Size(55, 46); this.FMODplayButton.Size = new System.Drawing.Size(55, 46);
@ -945,7 +969,7 @@
// textPreviewBox // textPreviewBox
// //
this.textPreviewBox.Dock = System.Windows.Forms.DockStyle.Fill; this.textPreviewBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.textPreviewBox.Font = new System.Drawing.Font("Consolas", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.textPreviewBox.Font = new System.Drawing.Font("Consolas", 9.75F);
this.textPreviewBox.Location = new System.Drawing.Point(0, 0); this.textPreviewBox.Location = new System.Drawing.Point(0, 0);
this.textPreviewBox.Multiline = true; this.textPreviewBox.Multiline = true;
this.textPreviewBox.Name = "textPreviewBox"; this.textPreviewBox.Name = "textPreviewBox";
@ -1080,17 +1104,6 @@
this.showOriginalFileToolStripMenuItem.Visible = false; this.showOriginalFileToolStripMenuItem.Visible = false;
this.showOriginalFileToolStripMenuItem.Click += new System.EventHandler(this.showOriginalFileToolStripMenuItem_Click); this.showOriginalFileToolStripMenuItem.Click += new System.EventHandler(this.showOriginalFileToolStripMenuItem_Click);
// //
// sceneTreeView
//
this.sceneTreeView.CheckBoxes = true;
this.sceneTreeView.Dock = System.Windows.Forms.DockStyle.Fill;
this.sceneTreeView.HideSelection = false;
this.sceneTreeView.Location = new System.Drawing.Point(0, 20);
this.sceneTreeView.Name = "sceneTreeView";
this.sceneTreeView.Size = new System.Drawing.Size(472, 587);
this.sceneTreeView.TabIndex = 1;
this.sceneTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.sceneTreeView_AfterCheck);
//
// AssetStudioGUIForm // AssetStudioGUIForm
// //
this.AllowDrop = true; this.AllowDrop = true;

View File

@ -1579,7 +1579,7 @@ namespace AssetStudioGUI
return selectedAssets; return selectedAssets;
} }
private void filterExludeMode_CheckedChanged(object sender, EventArgs e) private void filterExcludeMode_CheckedChanged(object sender, EventArgs e)
{ {
FilterAssetList(); FilterAssetList();
} }

View File

@ -1,22 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows</TargetFrameworks>
<Version>0.16.47.1</Version> <Version>0.16.48.1</Version>
<Copyright>Copyright © Perfare 2018-2022</Copyright> <Copyright>Copyright © Perfare 2018-2022</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="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" /> <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup Condition=" !$(TargetFramework.Contains('windows')) ">
<ProjectReference Include="..\AssetStudio.PInvoke\AssetStudio.PInvoke.csproj" /> <PackageReference Include="Kyaru.Texture2DDecoder.Linux" Version="0.1.0" />
<ProjectReference Include="..\AssetStudioFBXWrapper\AssetStudioFBXWrapper.csproj" /> <PackageReference Include="Kyaru.Texture2DDecoder.macOS" Version="0.1.0" />
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj" /> </ItemGroup>
<ProjectReference Include="..\Texture2DDecoderWrapper\Texture2DDecoderWrapper.csproj" />
</ItemGroup> <ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
<PackageReference Include="Kyaru.Texture2DDecoder.Windows" Version="0.1.0" />
<PackageReference Include="Kyaru.Texture2DDecoder">
<Version>0.17.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetStudio.PInvoke\AssetStudio.PInvoke.csproj" />
<ProjectReference Include="..\AssetStudioFBXWrapper\AssetStudioFBXWrapper.csproj" />
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<ProjectReference Include="..\Texture2DDecoderWrapper\Texture2DDecoderWrapper.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -10,7 +10,10 @@ using System;
using System.Text; using System.Text;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Collections.Generic; using System.Collections.Generic;
#if NETFRAMEWORK
using AssetStudio.PInvoke; using AssetStudio.PInvoke;
#endif
namespace FMOD namespace FMOD
{ {
@ -749,10 +752,12 @@ namespace FMOD
*/ */
public struct Factory public struct Factory
{ {
#if NETFRAMEWORK
static Factory() static Factory()
{ {
DllLoader.PreloadDll(VERSION.dll); DllLoader.PreloadDll(VERSION.dll);
} }
#endif
public static RESULT System_Create(out System system) public static RESULT System_Create(out System system)
{ {

View File

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

View File

@ -1,16 +1,20 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#if NETFRAMEWORK
using AssetStudio.PInvoke; using AssetStudio.PInvoke;
#endif
namespace Texture2DDecoder namespace Texture2DDecoder
{ {
public static unsafe partial class TextureDecoder public static unsafe partial class TextureDecoder
{ {
#if NETFRAMEWORK
static TextureDecoder() static TextureDecoder()
{ {
DllLoader.PreloadDll(T2DDll.DllName); DllLoader.PreloadDll(T2DDll.DllName);
} }
#endif
public static bool DecodeDXT1(byte[] data, int width, int height, byte[] image) public static bool DecodeDXT1(byte[] data, int width, int height, byte[] image)
{ {