This commit is contained in:
Razmoth
2023-01-06 22:33:59 +04:00
parent a3cf868dfb
commit 2b31232b30
178 changed files with 5213 additions and 23780 deletions

View File

@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>0.18.60</Version> <Version>0.80.30</Version>
<AssemblyVersion>0.18.60</AssemblyVersion> <AssemblyVersion>0.80.30</AssemblyVersion>
<FileVersion>0.18.60</FileVersion> <FileVersion>0.80.30</FileVersion>
<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

@@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
namespace AssetStudio.PInvoke namespace AssetStudio.PInvoke
{ {
public static class DllLoader public static partial class DllLoader
{ {
public static void PreloadDll(string dllName) public static void PreloadDll(string dllName)
@@ -37,7 +37,7 @@ namespace AssetStudio.PInvoke
return directedDllDir; return directedDllDir;
} }
private static class Win32 private static partial class Win32
{ {
internal static void LoadDll(string dllDir, string dllName) internal static void LoadDll(string dllDir, string dllName)
@@ -46,9 +46,9 @@ namespace AssetStudio.PInvoke
var directedDllPath = Path.Combine(dllDir, dllFileName); var directedDllPath = Path.Combine(dllDir, dllFileName);
// Specify SEARCH_DLL_LOAD_DIR to load dependent libraries located in the same platform-specific directory. // Specify SEARCH_DLL_LOAD_DIR to load dependent libraries located in the same platform-specific directory.
var hLibrary = LoadLibraryEx(directedDllPath, IntPtr.Zero, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); var hLibrary = LoadLibraryEx(directedDllPath, nint.Zero, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
if (hLibrary == IntPtr.Zero) if (hLibrary == nint.Zero)
{ {
var errorCode = Marshal.GetLastWin32Error(); var errorCode = Marshal.GetLastWin32Error();
var exception = new Win32Exception(errorCode); var exception = new Win32Exception(errorCode);
@@ -59,15 +59,15 @@ namespace AssetStudio.PInvoke
// HMODULE LoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); // HMODULE LoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
// HMODULE LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); // HMODULE LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
[DllImport("kernel32.dll", SetLastError = true)] [LibraryImport("kernel32.dll", EntryPoint = "LoadLibraryExA", SetLastError = true, StringMarshalling = StringMarshalling.Utf8)]
private static extern IntPtr LoadLibraryEx(string lpLibFileName, IntPtr hFile, uint dwFlags); private static partial IntPtr LoadLibraryEx(string lpLibFileName, IntPtr hFile, uint dwFlags);
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 private static partial class Posix
{ {
internal static void LoadDll(string dllDir, string dllName) internal static void LoadDll(string dllDir, string dllName)
@@ -93,7 +93,7 @@ namespace AssetStudio.PInvoke
const int ldFlags = RTLD_NOW | RTLD_GLOBAL; const int ldFlags = RTLD_NOW | RTLD_GLOBAL;
var hLibrary = DlOpen(directedDllPath, ldFlags); var hLibrary = DlOpen(directedDllPath, ldFlags);
if (hLibrary == IntPtr.Zero) if (hLibrary == nint.Zero)
{ {
var pErrStr = DlError(); var pErrStr = DlError();
// `PtrToStringAnsi` always uses the specific constructor of `String` (see dotnet/core#2325), // `PtrToStringAnsi` always uses the specific constructor of `String` (see dotnet/core#2325),
@@ -107,12 +107,12 @@ namespace AssetStudio.PInvoke
// OSX and most Linux OS use LP64 so `int` is still 32-bit even on 64-bit platforms. // 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); // void *dlopen(const char *filename, int flag);
[DllImport("libdl", EntryPoint = "dlopen")] [LibraryImport("libdl", EntryPoint = "dlopen", StringMarshalling = StringMarshalling.Utf8)]
private static extern IntPtr DlOpen([MarshalAs(UnmanagedType.LPStr)] string fileName, int flags); private static partial nint DlOpen(string fileName, int flags);
// char *dlerror(void); // char *dlerror(void);
[DllImport("libdl", EntryPoint = "dlerror")] [LibraryImport("libdl", EntryPoint = "dlerror")]
private static extern IntPtr DlError(); private static partial nint DlError();
private const int RTLD_LAZY = 0x1; private const int RTLD_LAZY = 0x1;
private const int RTLD_NOW = 0x2; private const int RTLD_NOW = 0x2;

View File

@@ -1,100 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace AssetStudio.PInvoke
{
// Generally the technique from Steamworks.NET
public class Utf8StringHandle : SafeHandleZeroOrMinusOneIsInvalid
{
static Utf8StringHandle()
{
Utf8 = new UTF8Encoding(false);
}
public Utf8StringHandle(string str)
: base(true)
{
IntPtr buffer;
if (str == null)
{
buffer = IntPtr.Zero;
}
else
{
if (str.Length == 0)
{
buffer = Marshal.AllocHGlobal(1);
unsafe
{
*(byte*)buffer = 0;
}
}
else
{
var strlen = Utf8.GetByteCount(str);
var strBuffer = new byte[strlen + 1];
Utf8.GetBytes(str, 0, str.Length, strBuffer, 0);
buffer = Marshal.AllocHGlobal(strBuffer.Length);
Marshal.Copy(strBuffer, 0, buffer, strBuffer.Length);
}
}
SetHandle(buffer);
}
public static string ReadUtf8StringFromPointer(IntPtr lpstr)
{
if (lpstr == IntPtr.Zero || lpstr == new IntPtr(-1))
{
return null;
}
var byteCount = 0;
unsafe
{
var p = (byte*)lpstr.ToPointer();
while (*p != 0)
{
byteCount += 1;
p += 1;
}
}
if (byteCount == 0)
{
return string.Empty;
}
var strBuffer = new byte[byteCount];
Marshal.Copy(lpstr, strBuffer, 0, byteCount);
var str = Utf8.GetString(strBuffer);
return str;
}
protected override bool ReleaseHandle()
{
if (!IsInvalid)
{
Marshal.FreeHGlobal(handle);
}
return true;
}
private static readonly UTF8Encoding Utf8;
}
}

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 17 # Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148 VisualStudioVersion = 17.4.33205.214
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
@@ -16,16 +16,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioGUI", "AssetStud
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioUtility", "AssetStudioUtility\AssetStudioUtility.csproj", "{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioUtility", "AssetStudioUtility\AssetStudioUtility.csproj", "{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Texture2DDecoderWrapper", "Texture2DDecoderWrapper\Texture2DDecoderWrapper.csproj", "{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}"
ProjectSection(ProjectDependencies) = postProject
{29356642-C46E-4144-83D8-22DC09D0D7FD} = {29356642-C46E-4144-83D8-22DC09D0D7FD}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssetStudioFBXNative", "AssetStudioFBXNative\AssetStudioFBXNative.vcxproj", "{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssetStudioFBXNative", "AssetStudioFBXNative\AssetStudioFBXNative.vcxproj", "{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Texture2DDecoderNative", "Texture2DDecoderNative\Texture2DDecoderNative.vcxproj", "{29356642-C46E-4144-83D8-22DC09D0D7FD}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioCLI", "AssetStudioCLI\AssetStudioCLI.csproj", "{D35262CD-E063-4203-A9D2-C1BB82B6C598}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioCLI", "AssetStudioCLI\AssetStudioCLI.csproj", "{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -97,18 +90,6 @@ Global
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|x64.Build.0 = Release|Any CPU {65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|x64.Build.0 = Release|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|x86.ActiveCfg = Release|Any CPU {65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|x86.ActiveCfg = Release|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|x86.Build.0 = Release|Any CPU {65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|x86.Build.0 = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|x64.ActiveCfg = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|x64.Build.0 = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|x86.ActiveCfg = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|x86.Build.0 = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|Any CPU.Build.0 = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|x64.ActiveCfg = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|x64.Build.0 = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|x86.ActiveCfg = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|x86.Build.0 = Release|Any CPU
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|Any CPU.ActiveCfg = Debug|Win32 {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|Any CPU.ActiveCfg = Debug|Win32
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|Any CPU.Build.0 = Debug|Win32 {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|Any CPU.Build.0 = Debug|Win32
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|x64.ActiveCfg = Debug|x64 {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|x64.ActiveCfg = Debug|x64
@@ -121,30 +102,18 @@ Global
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x64.Build.0 = Release|x64 {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x64.Build.0 = Release|x64
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x86.ActiveCfg = Release|Win32 {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x86.ActiveCfg = Release|Win32
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x86.Build.0 = Release|Win32 {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x86.Build.0 = Release|Win32
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|Any CPU.ActiveCfg = Debug|Win32 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|Any CPU.Build.0 = Debug|Win32 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x64.ActiveCfg = Debug|x64 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|x64.ActiveCfg = Debug|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x64.Build.0 = Debug|x64 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|x64.Build.0 = Debug|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x86.ActiveCfg = Debug|Win32 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|x86.ActiveCfg = Debug|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x86.Build.0 = Debug|Win32 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|x86.Build.0 = Debug|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|Any CPU.ActiveCfg = Release|Win32 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|Any CPU.Build.0 = Release|Win32 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|Any CPU.Build.0 = Release|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x64.ActiveCfg = Release|x64 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|x64.ActiveCfg = Release|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x64.Build.0 = Release|x64 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|x64.Build.0 = Release|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.ActiveCfg = Release|Win32 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|x86.ActiveCfg = Release|Any CPU
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.Build.0 = Release|Win32 {D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|x86.Build.0 = Release|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Debug|x64.ActiveCfg = Debug|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Debug|x64.Build.0 = Debug|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Debug|x86.ActiveCfg = Debug|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Debug|x86.Build.0 = Debug|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Release|Any CPU.Build.0 = Release|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Release|x64.ActiveCfg = Release|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Release|x64.Build.0 = Release|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Release|x86.ActiveCfg = Release|Any CPU
{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -2,10 +2,10 @@
{ {
public class AssetEntry public class AssetEntry
{ {
public string Name; public string Name { get; set; }
public string Container; public string Container { get; set; }
public string Source; public string Source { get; set; }
public long PathID; public long PathID { get; set; }
public ClassIDType Type; public ClassIDType Type { get; set; }
} }
} }

View File

@@ -0,0 +1,10 @@
namespace AssetStudio
{
public enum AssetGroupOption
{
ByType,
ByContainer,
BySource,
None
}
}

View File

@@ -1,17 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Version>0.18.60</Version> <Version>0.80.30</Version>
<AssemblyVersion>0.18.60</AssemblyVersion> <AssemblyVersion>0.80.30</AssemblyVersion>
<FileVersion>0.18.60</FileVersion> <FileVersion>0.80.30</FileVersion>
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright> <Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="K4os.Compression.LZ4" Version="1.2.16" /> <PackageReference Include="K4os.Compression.LZ4" Version="1.3.4-beta" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta2" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="Keys.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>

436
AssetStudio/AssetsHelper.cs Normal file
View File

@@ -0,0 +1,436 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Globalization;
using System.Xml.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
namespace AssetStudio
{
public static class AssetsHelper
{
public const string CABMapName = "Maps";
public static CancellationTokenSource tokenSource = new CancellationTokenSource();
public static AssetsManager assetsManager = new AssetsManager() { Silent = true, SkipProcess = true, ResolveDependencies = false };
public static string BaseFolder = "";
public static Dictionary<string, Entry> CABMap = new Dictionary<string, Entry>(StringComparer.OrdinalIgnoreCase);
public static Dictionary<string, HashSet<long>> Offsets = new Dictionary<string, HashSet<long>>();
public static void Clear()
{
CABMap.Clear();
Offsets.Clear();
BaseFolder = "";
tokenSource.Dispose();
tokenSource = new CancellationTokenSource();
GC.WaitForPendingFinalizers();
GC.Collect();
}
public static string[] GetMaps()
{
Directory.CreateDirectory(CABMapName);
var files = Directory.GetFiles(CABMapName, "*.bin", SearchOption.TopDirectoryOnly);
return files.Select(x => Path.GetFileNameWithoutExtension(x)).ToArray();
}
public static void BuildCABMap(string[] files, string mapName, string baseFolder, Game game)
{
Logger.Info($"Processing...");
try
{
CABMap.Clear();
BaseFolder = baseFolder;
assetsManager.Game = game;
for (int i = 0; i < files.Length; i++)
{
var file = files[i];
assetsManager.LoadFiles(file);
if (assetsManager.assetsFileList.Count > 0)
{
var relativePath = Path.GetRelativePath(BaseFolder, file);
foreach (var assetsFile in assetsManager.assetsFileList)
{
if (tokenSource.IsCancellationRequested)
{
Logger.Info("Building CABMap has been aborted !!");
return;
}
var dependencies = assetsFile.m_Externals.Select(x => x.fileName).ToArray();
var entry = new Entry()
{
Path = relativePath,
Offset = assetsFile.offset,
Dependencies = dependencies
};
CABMap.TryAdd(assetsFile.fileName, new());
CABMap[assetsFile.fileName] = entry;
}
Logger.Info($"Processed {Path.GetFileName(file)}");
}
assetsManager.Clear();
}
CABMap = CABMap.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value, StringComparer.OrdinalIgnoreCase);
var outputFile = new FileInfo(Path.Combine(CABMapName, $"{mapName}.bin"));
outputFile.Directory.Create();
using (var binaryFile = outputFile.Create())
using (var writer = new BinaryWriter(binaryFile))
{
writer.Write(BaseFolder);
writer.Write(CABMap.Count);
foreach (var kv in CABMap)
{
writer.Write(kv.Key);
writer.Write(kv.Value.Path);
writer.Write(kv.Value.Offset);
writer.Write(kv.Value.Dependencies.Length);
foreach (var cab in kv.Value.Dependencies)
{
writer.Write(cab);
}
}
}
Logger.Info($"CABMap build successfully !!");
}
catch (Exception e)
{
Logger.Warning($"CABMap was not build, {e.Message}{e.StackTrace}");
}
}
public static void LoadMap(string mapName)
{
Logger.Info($"Loading {mapName}");
try
{
CABMap.Clear();
using (var fs = File.OpenRead(Path.Combine(CABMapName, $"{mapName}.bin")))
using (var reader = new BinaryReader(fs))
{
BaseFolder = reader.ReadString();
var count = reader.ReadInt32();
for (int i = 0; i < count; i++)
{
var cab = reader.ReadString();
var path = reader.ReadString();
var offset = reader.ReadInt64();
var depCount = reader.ReadInt32();
var dependencies = new List<string>();
for (int j = 0; j < depCount; j++)
{
var dependancy = reader.ReadString();
dependencies.Add(dependancy);
}
var entry = new Entry()
{
Path = path,
Offset = offset,
Dependencies = dependencies.ToArray()
};
CABMap.Add(cab, entry);
}
}
Logger.Info($"Loaded {mapName} !!");
}
catch (Exception e)
{
Logger.Warning($"{mapName} was not loaded, {e.Message}");
}
}
public static AssetEntry[] BuildAssetMap(string[] files, Game game, ClassIDType[] typeFilters = null, Regex[] nameFilters = null, Regex[] containerFilters = null)
{
assetsManager.Game = game;
var assets = new List<AssetEntry>();
for (int i = 0; i < files.Length; i++)
{
var file = files[i];
assetsManager.LoadFiles(file);
if (assetsManager.assetsFileList.Count > 0)
{
var containers = new List<(PPtr<Object>, string)>();
var mihoyoBinDataNames = new List<(PPtr<Object>, string)>();
var objectAssetItemDic = new Dictionary<Object, AssetEntry>();
var animators = new List<(PPtr<Object>, AssetEntry)>();
foreach (var assetsFile in assetsManager.assetsFileList)
{
assetsFile.m_Objects = ObjectInfo.Filter(assetsFile.m_Objects);
foreach (var objInfo in assetsFile.m_Objects)
{
var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objInfo, game);
var obj = new Object(objectReader);
var asset = new AssetEntry()
{
Source = file,
PathID = objectReader.m_PathID,
Type = objectReader.type,
Container = ""
};
var exportable = true;
switch (objectReader.type)
{
case ClassIDType.AssetBundle:
var assetBundle = new AssetBundle(objectReader);
foreach (var m_Container in 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++)
{
containers.Add((assetBundle.m_PreloadTable[k], m_Container.Key));
}
}
obj = null;
asset.Name = assetBundle.m_Name;
exportable = false;
break;
case ClassIDType.GameObject:
var gameObject = new GameObject(objectReader);
obj = gameObject;
asset.Name = gameObject.m_Name;
exportable = false;
break;
case ClassIDType.Shader:
asset.Name = objectReader.ReadAlignedString();
if (string.IsNullOrEmpty(asset.Name))
{
var m_parsedForm = new SerializedShader(objectReader);
asset.Name = m_parsedForm.m_Name;
}
break;
case ClassIDType.Animator:
var component = new PPtr<Object>(objectReader);
animators.Add((component, asset));
break;
case ClassIDType.MiHoYoBinData:
var MiHoYoBinData = new MiHoYoBinData(objectReader);
obj = MiHoYoBinData;
exportable = MiHoYoBinData.Exportable;
break;
case ClassIDType.IndexObject:
var indexObject = new IndexObject(objectReader);
obj = null;
foreach (var index in indexObject.AssetMap)
{
mihoyoBinDataNames.Add((index.Value.Object, index.Key));
}
asset.Name = "IndexObject";
break;
default:
asset.Name = objectReader.ReadAlignedString();
break;
}
if (obj != null)
{
objectAssetItemDic.Add(obj, asset);
assetsFile.AddObject(obj);
}
var isMatchRegex = nameFilters.IsNullOrEmpty() || nameFilters.Any(x => x.IsMatch(asset.Name) || asset.Type == ClassIDType.Animator);
var isFilteredType = typeFilters.IsNullOrEmpty() || typeFilters.Contains(asset.Type) || asset.Type == ClassIDType.Animator;
if (isMatchRegex && isFilteredType && exportable)
{
assets.Add(asset);
}
}
}
foreach ((var pptr, var asset) in animators)
{
if (pptr.TryGet<GameObject>(out var gameObject) && (nameFilters.IsNullOrEmpty() || nameFilters.Any(x => x.IsMatch(gameObject.m_Name))) && (typeFilters.IsNullOrEmpty() || typeFilters.Contains(asset.Type)))
{
asset.Name = gameObject.m_Name;
}
}
foreach((var pptr, var name) in mihoyoBinDataNames)
{
if (pptr.TryGet<MiHoYoBinData>(out var miHoYoBinData))
{
var asset = objectAssetItemDic[miHoYoBinData];
if (int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var hash))
{
asset.Name = name;
asset.Container = hash.ToString();
}
else asset.Name = $"BinFile #{asset.PathID}";
}
}
foreach ((var pptr, var container) in containers)
{
if (pptr.TryGet(out var obj))
{
var item = objectAssetItemDic[obj];
if (containerFilters.IsNullOrEmpty() || containerFilters.Any(x => x.IsMatch(container)))
{
item.Container = container;
}
else
{
assets.Remove(item);
}
}
}
Logger.Info($"Processed {Path.GetFileName(file)}");
}
assetsManager.Clear();
}
if (game.Type.IsGISubGroup() && assets.Count > 0)
{
Logger.Info("Updating Containers...");
foreach (var asset in assets)
{
if (int.TryParse(asset.Container, out var value))
{
var last = unchecked((uint)value);
var path = ResourceIndex.GetAssetPath(last);
if (!string.IsNullOrEmpty(path))
{
asset.Container = path;
if (asset.Type == ClassIDType.MiHoYoBinData)
{
asset.Name = Path.GetFileNameWithoutExtension(path);
}
}
}
}
Logger.Info("Updated !!");
}
return assets.ToArray();
}
public static void ExportAssetsMap(AssetEntry[] toExportAssets, string name, string savePath, ExportListType exportListType)
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Progress.Reset();
string filename;
switch (exportListType)
{
case ExportListType.XML:
filename = Path.Combine(savePath, $"{name}.xml");
var doc = new XDocument(
new XElement("Assets",
new XAttribute("filename", filename),
new XAttribute("createdAt", DateTime.UtcNow.ToString("s")),
toExportAssets.Select(
asset => new XElement("Asset",
new XElement("Name", asset.Name),
new XElement("Container", asset.Container),
new XElement("Type", new XAttribute("id", (int)asset.Type), asset.Type.ToString()),
new XElement("PathID", asset.PathID),
new XElement("Source", asset.Source)
)
)
)
);
doc.Save(filename);
break;
case ExportListType.JSON:
filename = Path.Combine(savePath, $"{name}.json");
using (StreamWriter file = File.CreateText(filename))
{
var serializer = new JsonSerializer() { Formatting = Formatting.Indented };
serializer.Converters.Add(new StringEnumConverter());
serializer.Serialize(file, toExportAssets);
}
break;
}
Logger.Info($"Finished exporting asset list with {toExportAssets.Length} items.");
Logger.Info($"AssetMap build successfully !!");
});
}
public static void AddCABOffsets(string[] path, List<string> cabs)
{
for (int i = 0; i < cabs.Count; i++)
{
var cab = cabs[i];
if (CABMap.TryGetValue(cab, out var entry))
{
if (!path.Contains(entry.Path))
{
var fullPath = Path.Combine(BaseFolder, entry.Path);
if (!Offsets.ContainsKey(fullPath))
{
Offsets.Add(fullPath, new HashSet<long>());
}
Offsets[fullPath].Add(entry.Offset);
}
foreach (var dep in entry.Dependencies)
{
if (!cabs.Contains(dep))
cabs.Add(dep);
}
}
}
}
public static bool FindCAB(string path, out List<string> cabs)
{
cabs = new List<string>();
var relativePath = Path.GetRelativePath(BaseFolder, path);
foreach (var kv in CABMap)
{
if (kv.Value.Path.Equals(relativePath))
{
cabs.Add(kv.Key);
}
}
return cabs.Count != 0;
}
public static string[] ProcessFiles(string[] files)
{
foreach (var file in files)
{
if (!Offsets.ContainsKey(file))
{
Offsets.Add(file, new HashSet<long>());
}
if (FindCAB(file, out var cabs))
{
AddCABOffsets(files, cabs);
}
}
return Offsets.Keys.ToArray();
}
public static string[] ProcessDependencies(string[] files)
{
if (CABMap.Count == 0)
{
Logger.Warning("CABMap is not build, skip resolving dependencies...");
}
else
{
Logger.Info("Resolving Dependencies...");
files = ProcessFiles(files);
}
return files;
}
public record Entry
{
public string Path { get; set; }
public long Offset { get; set; }
public string[] Dependencies { get; set; }
}
}
}

View File

@@ -1,9 +1,11 @@
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using static AssetStudio.ImportHelper; using static AssetStudio.ImportHelper;
namespace AssetStudio namespace AssetStudio
@@ -11,8 +13,11 @@ namespace AssetStudio
public class AssetsManager public class AssetsManager
{ {
public Game Game; public Game Game;
public bool ResolveDependancies; public bool Silent = false;
public bool SkipProcess = false;
public bool ResolveDependencies = false;
public string SpecifyUnityVersion; public string SpecifyUnityVersion;
public CancellationTokenSource tokenSource = new CancellationTokenSource();
public List<SerializedFile> assetsFileList = new List<SerializedFile>(); public List<SerializedFile> assetsFileList = new List<SerializedFile>();
internal Dictionary<string, int> assetsFileIndexCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); internal Dictionary<string, int> assetsFileIndexCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
@@ -20,19 +25,49 @@ namespace AssetStudio
private List<string> importFiles = new List<string>(); private List<string> importFiles = new List<string>();
private HashSet<string> importFilesHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private HashSet<string> importFilesHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private HashSet<string> noexistFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private HashSet<string> assetsFileListHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private HashSet<string> assetsFileListHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
public void LoadFiles(params string[] files) public void LoadFiles(params string[] files)
{ {
if (ResolveDependancies) if (Silent)
files = CABManager.ProcessDependencies(files); {
Load(files); Logger.Silent = true;
Progress.Silent = true;
}
var path = Path.GetDirectoryName(Path.GetFullPath(files[0]));
MergeSplitAssets(path);
var toReadFile = ProcessingSplitFiles(files.ToList());
if (ResolveDependencies)
toReadFile = AssetsHelper.ProcessDependencies(toReadFile);
Load(toReadFile);
if (Silent)
{
Logger.Silent = false;
Progress.Silent = false;
}
} }
public void LoadFolder(string path) public void LoadFolder(string path)
{ {
var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories).ToArray(); if (Silent)
Load(files); {
Logger.Silent = true;
Progress.Silent = true;
}
MergeSplitAssets(path, true);
var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories).ToList();
var toReadFile = ProcessingSplitFiles(files);
Load(toReadFile);
if (Silent)
{
Logger.Silent = false;
Progress.Silent = false;
}
} }
private void Load(string[] files) private void Load(string[] files)
@@ -49,20 +84,29 @@ namespace AssetStudio
{ {
LoadFile(importFiles[i]); LoadFile(importFiles[i]);
Progress.Report(i + 1, importFiles.Count); Progress.Report(i + 1, importFiles.Count);
if (tokenSource.IsCancellationRequested)
{
Logger.Info("Loading files has been aborted !!");
break;
}
} }
importFiles.Clear(); importFiles.Clear();
importFilesHash.Clear(); importFilesHash.Clear();
noexistFiles.Clear();
assetsFileListHash.Clear(); assetsFileListHash.Clear();
CABManager.offsets.Clear();
ReadAssets(); if (!SkipProcess && !tokenSource.IsCancellationRequested)
ProcessAssets(); {
ReadAssets();
ProcessAssets();
}
} }
private void LoadFile(string fullName) private void LoadFile(string fullName)
{ {
var reader = new FileReader(fullName, Game); var reader = new FileReader(fullName);
reader = reader.PreProcessing(Game);
LoadFile(reader); LoadFile(reader);
} }
@@ -76,9 +120,6 @@ namespace AssetStudio
case FileType.BundleFile: case FileType.BundleFile:
LoadBundleFile(reader); LoadBundleFile(reader);
break; break;
case FileType.GameFile:
LoadGameFile(reader);
break;
case FileType.WebFile: case FileType.WebFile:
LoadWebFile(reader); LoadWebFile(reader);
break; break;
@@ -91,6 +132,15 @@ namespace AssetStudio
case FileType.ZipFile: case FileType.ZipFile:
LoadZipFile(reader); LoadZipFile(reader);
break; break;
case FileType.BlockFile:
LoadBlockFile(reader);
break;
case FileType.BlkFile:
LoadBlkFile(reader);
break;
case FileType.Mhy0File:
LoadMhy0File(reader);
break;
} }
} }
@@ -98,13 +148,46 @@ namespace AssetStudio
{ {
if (!assetsFileListHash.Contains(reader.FileName)) if (!assetsFileListHash.Contains(reader.FileName))
{ {
Logger.Info($"Loading {reader.FileName}"); Logger.Info($"Loading {reader.FullPath}");
try try
{ {
var assetsFile = new SerializedFile(reader, this, reader.FullPath); var assetsFile = new SerializedFile(reader, this);
CheckStrippedVersion(assetsFile); CheckStrippedVersion(assetsFile);
assetsFileList.Add(assetsFile); assetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName); assetsFileListHash.Add(assetsFile.fileName);
if (ResolveDependencies)
{
foreach (var sharedFile in assetsFile.m_Externals)
{
var sharedFileName = sharedFile.fileName;
if (!importFilesHash.Contains(sharedFileName))
{
var sharedFilePath = Path.Combine(Path.GetDirectoryName(reader.FullPath), sharedFileName);
if (!noexistFiles.Contains(sharedFilePath))
{
if (!File.Exists(sharedFilePath))
{
var findFiles = Directory.GetFiles(Path.GetDirectoryName(reader.FullPath), sharedFileName, SearchOption.AllDirectories);
if (findFiles.Length > 0)
{
sharedFilePath = findFiles[0];
}
}
if (File.Exists(sharedFilePath))
{
importFiles.Add(sharedFilePath);
importFilesHash.Add(sharedFileName);
}
else
{
noexistFiles.Add(sharedFilePath);
}
}
}
}
}
} }
catch (Exception e) catch (Exception e)
{ {
@@ -119,7 +202,7 @@ namespace AssetStudio
} }
} }
private void LoadAssetsFromMemory(FileReader reader, string originalPath, string unityVersion = null) private void LoadAssetsFromMemory(FileReader reader, string originalPath, string unityVersion = null, long originalOffset = 0)
{ {
if (!assetsFileListHash.Contains(reader.FileName)) if (!assetsFileListHash.Contains(reader.FileName))
{ {
@@ -127,6 +210,7 @@ namespace AssetStudio
{ {
var assetsFile = new SerializedFile(reader, this); var assetsFile = new SerializedFile(reader, this);
assetsFile.originalPath = originalPath; assetsFile.originalPath = originalPath;
assetsFile.offset = originalOffset;
if (!string.IsNullOrEmpty(unityVersion) && assetsFile.header.m_Version < SerializedFileFormatVersion.Unknown_7) if (!string.IsNullOrEmpty(unityVersion) && assetsFile.header.m_Version < SerializedFileFormatVersion.Unknown_7)
{ {
assetsFile.SetVersion(unityVersion); assetsFile.SetVersion(unityVersion);
@@ -145,19 +229,19 @@ namespace AssetStudio
Logger.Info($"Skipping {originalPath} ({reader.FileName})"); Logger.Info($"Skipping {originalPath} ({reader.FileName})");
} }
private void LoadBundleFile(FileReader reader, string originalPath = null) private void LoadBundleFile(FileReader reader, string originalPath = null, long originalOffset = 0)
{ {
Logger.Info("Loading " + reader.FullPath); Logger.Info("Loading " + reader.FullPath);
try try
{ {
var bundleFile = new BundleFile(reader); var bundleFile = new BundleFile(reader, Game);
foreach (var file in bundleFile.FileList) foreach (var file in bundleFile.fileList)
{ {
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName); var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
var subReader = new FileReader(dummyPath, file.stream); var subReader = new FileReader(dummyPath, file.stream);
if (subReader.FileType == FileType.AssetsFile) if (subReader.FileType == FileType.AssetsFile)
{ {
LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision); LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision, originalOffset);
} }
else else
{ {
@@ -165,6 +249,10 @@ namespace AssetStudio
} }
} }
} }
catch (InvalidCastException)
{
Logger.Error($"Game type mismatch, Expected {nameof(Mr0k)} but got {Game.Name} ({Game.GetType().Name}) !!");
}
catch (Exception e) catch (Exception e)
{ {
var str = $"Error while reading bundle file {reader.FullPath}"; var str = $"Error while reading bundle file {reader.FullPath}";
@@ -316,37 +404,132 @@ namespace AssetStudio
reader.Dispose(); reader.Dispose();
} }
} }
private void LoadBlockFile(FileReader reader)
private void LoadGameFile(FileReader reader)
{ {
Logger.Info("Loading " + reader.FileName); Logger.Info("Loading " + reader.FullPath);
try try
{ {
reader.BundlePos = CABManager.offsets.TryGetValue(reader.FullPath, out var list) ? list.ToArray() : Array.Empty<long>(); var offsets = AssetsHelper.Offsets.TryGetValue(reader.FullPath, out var list) ? list.ToArray() : Array.Empty<long>();
var gameFile = new GameFile(reader); using var stream = new BlockStream(reader.BaseStream, 0);
foreach (var bundle in gameFile.Bundles) if (!offsets.IsNullOrEmpty())
{ {
foreach (var file in bundle.Value) foreach (var offset in offsets)
{ {
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName); stream.Offset = offset;
var cabReader = new FileReader(dummyPath, file.stream, Game); var dummyPath = Path.Combine("//?/block:/", reader.FileName, offset.ToString("X8"));
if (cabReader.FileType == FileType.AssetsFile) var subReader = new FileReader(dummyPath, stream, true);
{ LoadBundleFile(subReader, reader.FullPath, offset);
var assetsFile = new SerializedFile(cabReader, this, reader.FullPath);
CheckStrippedVersion(assetsFile);
assetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName);
}
else
{
resourceFileReaders[file.fileName] = cabReader; //TODO
}
} }
} }
else
{
do
{
stream.Offset = stream.RelativePosition;
var dummyPath = Path.Combine("//?/block:/", reader.FileName, stream.RelativePosition.ToString("X8"));
var subReader = new FileReader(dummyPath, stream, true);
LoadBundleFile(subReader, reader.FullPath, stream.RelativePosition);
} while (stream.Remaining > 0);
}
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Error($"Error while reading file {reader.FileName}", e); Logger.Error($"Error while reading block file {reader.FileName}", e);
}
finally
{
reader.Dispose();
}
}
private void LoadBlkFile(FileReader reader)
{
Logger.Info("Loading " + reader.FullPath);
try
{
var offsets = AssetsHelper.Offsets.TryGetValue(reader.FullPath, out var list) ? list.ToArray() : Array.Empty<long>();
using var stream = BlkUtils.Decrypt(reader, (Blk)Game);
if (!offsets.IsNullOrEmpty())
{
foreach (var offset in offsets)
{
stream.Offset = offset;
var dummyPath = Path.Combine("//?/blk:/", reader.FileName, offset.ToString("X8"));
var subReader = new FileReader(dummyPath, stream, true);
switch (subReader.FileType)
{
case FileType.BundleFile:
LoadBundleFile(subReader, reader.FullPath, offset);
break;
case FileType.Mhy0File:
LoadMhy0File(subReader, reader.FullPath, offset);
break;
}
}
}
else
{
do
{
stream.Offset = stream.RelativePosition;
var dummyPath = Path.Combine("//?/blk:/", reader.FileName, stream.RelativePosition.ToString("X8"));
var subReader = new FileReader(dummyPath, stream, true);
switch (subReader.FileType)
{
case FileType.BundleFile:
LoadBundleFile(subReader, reader.FullPath, stream.RelativePosition);
break;
case FileType.Mhy0File:
LoadMhy0File(subReader, reader.FullPath, stream.RelativePosition);
break;
}
} while (stream.Remaining > 0);
}
}
catch (InvalidCastException)
{
Logger.Error($"Game type mismatch, Expected {nameof(Blk)} but got {Game.Name} ({Game.GetType().Name}) !!");
}
catch (Exception e)
{
Logger.Error($"Error while reading blk file {reader.FileName}", e);
}
finally
{
reader.Dispose();
}
}
private void LoadMhy0File(FileReader reader, string originalPath = null, long originalOffset = 0)
{
Logger.Info("Loading " + reader.FullPath);
try
{
var mhy0File = new Mhy0File(reader, reader.FullPath, (Mhy0)Game);
foreach (var file in mhy0File.fileList)
{
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
var cabReader = new FileReader(dummyPath, file.stream);
if (cabReader.FileType == FileType.AssetsFile)
{
LoadAssetsFromMemory(cabReader, originalPath ?? reader.FullPath, mhy0File.m_Header.unityRevision, originalOffset);
}
else
{
resourceFileReaders[file.fileName] = cabReader; //TODO
}
}
}
catch (InvalidCastException)
{
Logger.Error($"Game type mismatch, Expected {nameof(Mhy0)} but got {Game.Name} ({Game.GetType().Name}) !!");
}
catch (Exception e)
{
var str = $"Error while reading mhy0 file {reader.FullPath}";
if (originalPath != null)
{
str += $" from {Path.GetFileName(originalPath)}";
}
Logger.Error(str, e);
} }
finally finally
{ {
@@ -382,6 +565,12 @@ namespace AssetStudio
resourceFileReaders.Clear(); resourceFileReaders.Clear();
assetsFileIndexCache.Clear(); assetsFileIndexCache.Clear();
tokenSource.Dispose();
tokenSource = new CancellationTokenSource();
GC.WaitForPendingFinalizers();
GC.Collect();
} }
private void ReadAssets() private void ReadAssets()
@@ -395,7 +584,12 @@ namespace AssetStudio
{ {
foreach (var objectInfo in assetsFile.m_Objects) foreach (var objectInfo in assetsFile.m_Objects)
{ {
var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objectInfo); if (tokenSource.IsCancellationRequested)
{
Logger.Info("Reading assets has been aborted !!");
return;
}
var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objectInfo, Game);
try try
{ {
Object obj; Object obj;
@@ -444,7 +638,8 @@ namespace AssetStudio
obj = new MeshFilter(objectReader); obj = new MeshFilter(objectReader);
break; break;
case ClassIDType.MeshRenderer: case ClassIDType.MeshRenderer:
if (!Renderer.Parsable) continue; if (Renderer.Skipped)
goto default;
obj = new MeshRenderer(objectReader); obj = new MeshRenderer(objectReader);
break; break;
case ClassIDType.MiHoYoBinData: case ClassIDType.MiHoYoBinData:
@@ -466,11 +661,11 @@ namespace AssetStudio
obj = new RectTransform(objectReader); obj = new RectTransform(objectReader);
break; break;
case ClassIDType.Shader: case ClassIDType.Shader:
if (!Shader.Parsable) continue;
obj = new Shader(objectReader); obj = new Shader(objectReader);
break; break;
case ClassIDType.SkinnedMeshRenderer: case ClassIDType.SkinnedMeshRenderer:
if (!Renderer.Parsable) continue; if (Renderer.Skipped)
goto default;
obj = new SkinnedMeshRenderer(objectReader); obj = new SkinnedMeshRenderer(objectReader);
break; break;
case ClassIDType.Sprite: case ClassIDType.Sprite:
@@ -519,12 +714,20 @@ namespace AssetStudio
private void ProcessAssets() private void ProcessAssets()
{ {
if (tokenSource.IsCancellationRequested)
return;
Logger.Info("Process Assets..."); Logger.Info("Process Assets...");
foreach (var assetsFile in assetsFileList) foreach (var assetsFile in assetsFileList)
{ {
foreach (var obj in assetsFile.Objects) foreach (var obj in assetsFile.Objects)
{ {
if (tokenSource.IsCancellationRequested)
{
Logger.Info("Processing assets has been aborted !!");
return;
}
if (obj is GameObject m_GameObject) if (obj is GameObject m_GameObject)
{ {
foreach (var pptr in m_GameObject.m_Components) foreach (var pptr in m_GameObject.m_Components)
@@ -556,7 +759,7 @@ namespace AssetStudio
} }
} }
else if (obj is SpriteAtlas m_SpriteAtlas) else if (obj is SpriteAtlas m_SpriteAtlas)
{ {
if (m_SpriteAtlas.m_RenderDataMap.Count > 0) if (m_SpriteAtlas.m_RenderDataMap.Count > 0)
{ {
foreach (var m_PackedSprite in m_SpriteAtlas.m_PackedSprites) foreach (var m_PackedSprite in m_SpriteAtlas.m_PackedSprites)

View File

@@ -0,0 +1,59 @@
using System;
using System.IO;
namespace AssetStudio
{
public class BlockStream : Stream
{
private readonly Stream _baseStream;
private readonly long _origin;
private long _startPosition;
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => false;
public override long Length => _baseStream.Length - _startPosition;
public override long Position
{
get => _baseStream.Position - _startPosition;
set => Seek(value, SeekOrigin.Begin);
}
public long Offset
{
get => _startPosition;
set => _startPosition = value + _origin;
}
public long RelativePosition => _baseStream.Position - _origin;
public long Remaining => Length - Position;
public BlockStream(Stream stream, long offset)
{
_baseStream = stream;
_origin = offset;
_startPosition = offset;
Seek(0, SeekOrigin.Begin);
}
public override long Seek(long offset, SeekOrigin origin)
{
var target = origin switch
{
SeekOrigin.Begin => offset + _startPosition,
SeekOrigin.Current => offset + _baseStream.Position,
SeekOrigin.End => offset + Length,
_ => throw new NotSupportedException()
};
_baseStream.Seek(target, SeekOrigin.Begin);
return Position;
}
public override int Read(byte[] buffer, int offset, int count) => _baseStream.Read(buffer, offset, count);
public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
public override void SetLength(long value) => throw new NotImplementedException();
public override void Flush() => throw new NotImplementedException();
}
}

View File

@@ -1,7 +1,9 @@
using K4os.Compression.LZ4; using K4os.Compression.LZ4;
using System; using System;
using System.Data;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
namespace AssetStudio namespace AssetStudio
{ {
@@ -12,14 +14,16 @@ namespace AssetStudio
BlocksAndDirectoryInfoCombined = 0x40, BlocksAndDirectoryInfoCombined = 0x40,
BlocksInfoAtTheEnd = 0x80, BlocksInfoAtTheEnd = 0x80,
OldWebPluginCompatibility = 0x100, OldWebPluginCompatibility = 0x100,
BlockInfoNeedPaddingAtStart = 0x200 BlockInfoNeedPaddingAtStart = 0x200,
CNUnityEncryption = 0x400
} }
[Flags] [Flags]
public enum StorageBlockFlags public enum StorageBlockFlags
{ {
CompressionTypeMask = 0x3f, CompressionTypeMask = 0x3f,
Streamed = 0x40 Streamed = 0x40,
CNUnity = 0x100
} }
public enum CompressionType public enum CompressionType
@@ -31,6 +35,7 @@ namespace AssetStudio
Lzham, Lzham,
Lz4Mr0k Lz4Mr0k
} }
public class BundleFile public class BundleFile
{ {
public class Header public class Header
@@ -60,46 +65,19 @@ namespace AssetStudio
public string path; public string path;
} }
private Game Game;
private CNUnity CNUnity;
public Header m_Header; public Header m_Header;
private StorageBlock[] m_BlocksInfo;
private Node[] m_DirectoryInfo; private Node[] m_DirectoryInfo;
private StorageBlock[] m_BlocksInfo;
public StreamFile[] FileList; public StreamFile[] fileList;
public BundleFile(FileReader reader) public BundleFile(FileReader reader, Game game)
{ {
m_Header = new Header(); Game = game;
m_Header.signature = reader.ReadStringToNull(); m_Header = ReadBundleHeader(reader);
if (reader.Game.Name == "BH3")
{
m_Header.version = 6;
m_Header.unityVersion = "5.x.x";
m_Header.unityRevision = "2017.4.18f1";
}
else if (reader.Game.Name == "SR_CB2" || reader.Game.Name == "SR_CB3")
{
var readHeader = m_Header.signature != "ENCR";
if (!readHeader)
{
m_Header.version = 7;
m_Header.unityVersion = "5.x.x";
m_Header.unityRevision = "2019.4.32f1";
}
else
{
m_Header.version = reader.ReadUInt32();
m_Header.unityVersion = reader.ReadStringToNull();
m_Header.unityRevision = reader.ReadStringToNull();
}
}
else
{
m_Header.version = reader.ReadUInt32();
m_Header.unityVersion = reader.ReadStringToNull();
m_Header.unityRevision = reader.ReadStringToNull();
}
switch (m_Header.signature) switch (m_Header.signature)
{ {
case "UnityArchive": case "UnityArchive":
@@ -120,10 +98,6 @@ namespace AssetStudio
case "UnityFS": case "UnityFS":
case "ENCR": case "ENCR":
ReadHeader(reader); ReadHeader(reader);
if (reader.Game.Name == "ZZZ_CB1")
{
reader.AlignStream(0x10);
}
ReadBlocksInfoAndDirectory(reader); ReadBlocksInfoAndDirectory(reader);
using (var blocksStream = CreateBlocksStream(reader.FullPath)) using (var blocksStream = CreateBlocksStream(reader.FullPath))
{ {
@@ -134,6 +108,54 @@ namespace AssetStudio
} }
} }
private Header ReadBundleHeader(FileReader reader)
{
Header header = new Header();
header.signature = reader.ReadStringToNull(20);
switch (header.signature)
{
case "UnityFS":
if (Game.Type.IsBH3())
{
var version = reader.ReadUInt32();
if (version > 11)
{
XORShift128.InitSeed(version);
header.version = 6;
header.unityVersion = "5.x.x";
header.unityRevision = "2017.4.18f1";
}
else
{
reader.Position -= 4;
goto default;
}
}
else
{
header.version = reader.ReadUInt32();
header.unityVersion = reader.ReadStringToNull();
header.unityRevision = reader.ReadStringToNull();
}
break;
case "ENCR":
header.version = 6; // is 7 but does not have uncompressedDataHash
header.unityVersion = "5.x.x";
header.unityRevision = "2019.4.32f1";
break;
default:
if (Game.Type.IsNaraka())
{
header.signature = "UnityFS";
goto case "UnityFS";
}
break;
}
return header;
}
private void ReadHeaderAndBlocksInfo(EndianBinaryReader reader) private void ReadHeaderAndBlocksInfo(EndianBinaryReader reader)
{ {
if (m_Header.version >= 4) if (m_Header.version >= 4)
@@ -221,12 +243,12 @@ namespace AssetStudio
public void ReadFiles(Stream blocksStream, string path) public void ReadFiles(Stream blocksStream, string path)
{ {
FileList = new StreamFile[m_DirectoryInfo.Length]; fileList = new StreamFile[m_DirectoryInfo.Length];
for (int i = 0; i < m_DirectoryInfo.Length; i++) for (int i = 0; i < m_DirectoryInfo.Length; i++)
{ {
var node = m_DirectoryInfo[i]; var node = m_DirectoryInfo[i];
var file = new StreamFile(); var file = new StreamFile();
FileList[i] = file; fileList[i] = file;
file.path = node.path; file.path = node.path;
file.fileName = Path.GetFileName(node.path); file.fileName = Path.GetFileName(node.path);
if (node.size >= int.MaxValue) if (node.size >= int.MaxValue)
@@ -247,42 +269,74 @@ namespace AssetStudio
} }
} }
private void DecryptHeader(int key) private void DecryptHeader()
{ {
var rand = new XORShift128(); m_Header.flags ^= (ArchiveFlags)XORShift128.NextDecryptInt();
rand.InitSeed(key); m_Header.size ^= XORShift128.NextDecryptLong();
m_Header.flags ^= (ArchiveFlags)rand.NextDecryptInt(); m_Header.uncompressedBlocksInfoSize ^= XORShift128.NextDecryptUInt();
m_Header.size ^= rand.NextDecryptLong(); m_Header.compressedBlocksInfoSize ^= XORShift128.NextDecryptUInt();
m_Header.uncompressedBlocksInfoSize ^= rand.NextDecryptUInt(); XORShift128.Init = false;
m_Header.compressedBlocksInfoSize ^= rand.NextDecryptUInt();
} }
private void ReadHeader(EndianBinaryReader reader) private void ReadHeader(EndianBinaryReader reader)
{ {
if (reader.Game.Name == "BH3") if (Game.Type.IsBH3() && XORShift128.Init)
{ {
var key = reader.ReadInt32();
m_Header.flags = (ArchiveFlags)reader.ReadUInt32(); m_Header.flags = (ArchiveFlags)reader.ReadUInt32();
m_Header.size = reader.ReadInt64(); m_Header.size = reader.ReadInt64();
m_Header.uncompressedBlocksInfoSize = reader.ReadUInt32(); m_Header.uncompressedBlocksInfoSize = reader.ReadUInt32();
m_Header.compressedBlocksInfoSize = reader.ReadUInt32(); m_Header.compressedBlocksInfoSize = reader.ReadUInt32();
DecryptHeader(key); DecryptHeader();
var encUnityVersion = reader.ReadStringToNull(); var encUnityVersion = reader.ReadStringToNull();
var encUnityRevision = reader.ReadStringToNull(); var encUnityRevision = reader.ReadStringToNull();
return;
} }
else
m_Header.size = reader.ReadInt64();
m_Header.compressedBlocksInfoSize = reader.ReadUInt32();
m_Header.uncompressedBlocksInfoSize = reader.ReadUInt32();
m_Header.flags = (ArchiveFlags)reader.ReadUInt32();
if (m_Header.signature != "UnityFS" && !Game.Type.IsSRGroup())
{ {
m_Header.size = reader.ReadInt64(); reader.ReadByte();
m_Header.compressedBlocksInfoSize = reader.ReadUInt32(); }
m_Header.uncompressedBlocksInfoSize = reader.ReadUInt32();
m_Header.flags = (ArchiveFlags)reader.ReadUInt32(); if (Game.Type.IsNaraka())
{
m_Header.compressedBlocksInfoSize -= 0xCA;
m_Header.uncompressedBlocksInfoSize -= 0xCA;
} }
} }
private void ReadBlocksInfoAndDirectory(EndianBinaryReader reader) private void ReadBlocksInfoAndDirectory(EndianBinaryReader reader)
{ {
if (Game.Type.IsCNUnity())
{
ArchiveFlags mask;
var version = ParseVersion();
//Flag changed it in these versions
if (version[0] < 2020 || //2020 and earlier
(version[0] == 2020 && version[1] == 3 && version[2] <= 34) || //2020.3.34 and earlier
(version[0] == 2021 && version[1] == 3 && version[2] <= 2) || //2021.3.2 and earlier
(version[0] == 2022 && version[1] == 3 && version[2] <= 1)) //2022.3.1 and earlier
{
mask = ArchiveFlags.BlockInfoNeedPaddingAtStart;
}
else
{
mask = ArchiveFlags.CNUnityEncryption;
}
if ((m_Header.flags & mask) != 0)
{
CNUnity = new CNUnity(reader);
}
}
byte[] blocksInfoBytes; byte[] blocksInfoBytes;
if (m_Header.version >= 7 && reader.Game.Name != "SR_CB2" && reader.Game.Name != "SR_CB3") if (m_Header.version >= 7 && !Game.Type.IsSRGroup())
{ {
reader.AlignStream(16); reader.AlignStream(16);
} }
@@ -298,6 +352,7 @@ namespace AssetStudio
blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize); blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
} }
MemoryStream blocksInfoUncompresseddStream; MemoryStream blocksInfoUncompresseddStream;
var blocksInfoBytesSpan = blocksInfoBytes.AsSpan();
var uncompressedSize = m_Header.uncompressedBlocksInfoSize; var uncompressedSize = m_Header.uncompressedBlocksInfoSize;
var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask); var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask);
switch (compressionType) //kArchiveCompressionTypeMask switch (compressionType) //kArchiveCompressionTypeMask
@@ -321,7 +376,7 @@ namespace AssetStudio
case CompressionType.Lz4HC: //LZ4HC case CompressionType.Lz4HC: //LZ4HC
{ {
var uncompressedBytes = new byte[uncompressedSize]; var uncompressedBytes = new byte[uncompressedSize];
var numWrite = LZ4Codec.Decode(blocksInfoBytes, uncompressedBytes); var numWrite = LZ4Codec.Decode(blocksInfoBytesSpan, uncompressedBytes);
if (numWrite != uncompressedSize) if (numWrite != uncompressedSize)
{ {
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
@@ -330,10 +385,9 @@ namespace AssetStudio
break; break;
} }
case CompressionType.Lz4Mr0k: //Lz4Mr0k case CompressionType.Lz4Mr0k: //Lz4Mr0k
var blocksInfoSize = blocksInfoBytes.Length; if (Mr0kUtils.IsMr0k(blocksInfoBytesSpan))
if (Mr0k.IsMr0k(blocksInfoBytes))
{ {
Mr0k.Decrypt(ref blocksInfoBytes, ref blocksInfoSize); blocksInfoBytesSpan = Mr0kUtils.Decrypt(blocksInfoBytesSpan, (Mr0k)Game).ToArray();
} }
goto case CompressionType.Lz4HC; goto case CompressionType.Lz4HC;
default: default:
@@ -341,7 +395,7 @@ namespace AssetStudio
} }
using (var blocksInfoReader = new EndianBinaryReader(blocksInfoUncompresseddStream)) using (var blocksInfoReader = new EndianBinaryReader(blocksInfoUncompresseddStream))
{ {
if ((reader.Game.Name != "SR_CB2" && reader.Game.Name != "SR_CB3") || m_Header.signature != "ENCR") if (m_Header.version >= 7 || !Game.Type.IsSRGroup())
{ {
var uncompressedDataHash = blocksInfoReader.ReadBytes(16); var uncompressedDataHash = blocksInfoReader.ReadBytes(16);
} }
@@ -370,12 +424,17 @@ namespace AssetStudio
}; };
} }
} }
if (!Game.Type.IsCNUnity() && (m_Header.flags & ArchiveFlags.BlockInfoNeedPaddingAtStart) != 0)
{
reader.AlignStream(16);
}
} }
private void ReadBlocks(EndianBinaryReader reader, Stream blocksStream) private void ReadBlocks(EndianBinaryReader reader, Stream blocksStream)
{ {
foreach (var blockInfo in m_BlocksInfo) for (int i = 0; i < m_BlocksInfo.Length; i++)
{ {
var blockInfo = m_BlocksInfo[i];
var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask); var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask);
switch (compressionType) //kStorageBlockCompressionTypeMask switch (compressionType) //kStorageBlockCompressionTypeMask
{ {
@@ -394,20 +453,28 @@ namespace AssetStudio
case CompressionType.Lz4Mr0k: //Lz4Mr0k case CompressionType.Lz4Mr0k: //Lz4Mr0k
{ {
var compressedSize = (int)blockInfo.compressedSize; var compressedSize = (int)blockInfo.compressedSize;
var compressedBytes = new byte[compressedSize]; var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
reader.Read(compressedBytes, 0, compressedSize); reader.Read(compressedBytes, 0, compressedSize);
if (compressionType == CompressionType.Lz4Mr0k && Mr0k.IsMr0k(compressedBytes)) var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
if (compressionType == CompressionType.Lz4Mr0k && Mr0kUtils.IsMr0k(compressedBytes))
{ {
Mr0k.Decrypt(ref compressedBytes, ref compressedSize); compressedBytesSpan = Mr0kUtils.Decrypt(compressedBytesSpan, (Mr0k)Game);
}
if (Game.Type.IsCNUnity() && (blockInfo.flags & StorageBlockFlags.CNUnity) != 0)
{
CNUnity.DecryptBlock(compressedBytesSpan, compressedSize, i);
} }
var uncompressedSize = (int)blockInfo.uncompressedSize; var uncompressedSize = (int)blockInfo.uncompressedSize;
var uncompressedBytes = new byte[uncompressedSize]; var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
var numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize); var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
var numWrite = LZ4Codec.Decode(compressedBytesSpan, uncompressedBytesSpan);
if (numWrite != uncompressedSize) if (numWrite != uncompressedSize)
{ {
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
} }
blocksStream.Write(uncompressedBytes, 0, uncompressedSize); blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
break; break;
} }
default: default:
@@ -416,5 +483,11 @@ namespace AssetStudio
} }
blocksStream.Position = 0; blocksStream.Position = 0;
} }
public int[] ParseVersion()
{
var versionSplit = Regex.Replace(m_Header.unityRevision, @"\D", ".").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
return versionSplit.Select(int.Parse).ToArray();
}
} }
} }

View File

@@ -1,207 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
namespace AssetStudio
{
public static class CABManager
{
public static Dictionary<string, Entry> CABMap = new Dictionary<string, Entry>(StringComparer.OrdinalIgnoreCase);
public static Dictionary<string, HashSet<long>> offsets = new Dictionary<string, HashSet<long>>();
public static void BuildMap(List<string> files, Game game)
{
Logger.Info($"Building {game.Name}Map");
try
{
int collisions = 0;
CABMap.Clear();
Progress.Reset();
for (int i = 0; i < files.Count; i++)
{
var file = files[i];
var reader = new FileReader(file, game);
var gameFile = new GameFile(reader);
reader.Dispose();
foreach (var bundle in gameFile.Bundles)
{
foreach (var cab in bundle.Value)
{
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), cab.fileName);
using (var cabReader = new FileReader(dummyPath, cab.stream))
{
if (cabReader.FileType == FileType.AssetsFile)
{
if (CABMap.ContainsKey(cab.path))
{
collisions++;
continue;
}
var assetsFile = new SerializedFile(cabReader, null, reader.FullPath);
var dependencies = assetsFile.m_Externals.Select(x => x.fileName).ToList();
CABMap.Add(cab.path, new Entry(file, bundle.Key, dependencies));
}
}
}
}
Logger.Info($"[{i + 1}/{files.Count}] Processed {Path.GetFileName(file)}");
Progress.Report(i + 1, files.Count);
}
CABMap = CABMap.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value);
var outputFile = new FileInfo($"Maps/{game.Name}Map.bin");
if (!outputFile.Directory.Exists)
outputFile.Directory.Create();
using (var binaryFile = outputFile.Create())
using (var writer = new BinaryWriter(binaryFile))
{
writer.Write(CABMap.Count);
foreach (var cab in CABMap)
{
writer.Write(cab.Key);
writer.Write(cab.Value.Path);
writer.Write(cab.Value.Offset);
writer.Write(cab.Value.Dependencies.Count);
foreach (var dependancy in cab.Value.Dependencies)
{
writer.Write(dependancy);
}
}
}
Logger.Info($"{game.Name}Map build successfully, {collisions} collisions found !!");
}
catch (Exception e)
{
Logger.Warning($"{game.Name}Map was not build, {e.Message}");
}
}
public static void LoadMap(Game game)
{
Logger.Info($"Loading {game.Name}Map");
try
{
CABMap.Clear();
using (var binaryFile = File.OpenRead($"Maps/{game.Name}Map.bin"))
using (var reader = new BinaryReader(binaryFile))
{
var count = reader.ReadInt32();
for (int i = 0; i < count; i++)
{
var cab = reader.ReadString();
var path = reader.ReadString();
var offset = reader.ReadInt64();
var depCount = reader.ReadInt32();
var dependencies = new List<string>();
for (int j = 0; j < depCount; j++)
{
var dependancy = reader.ReadString();
dependencies.Add(dependancy);
}
CABMap.Add(cab, new Entry(path, offset, dependencies));
}
}
Logger.Info($"Loaded {game.Name}Map !!");
}
catch (Exception e)
{
Logger.Warning($"{game.Name}Map was not loaded, {e.Message}");
}
}
public static void AddCABOffsets(string[] path, List<string> cabs)
{
for (int i = 0; i < cabs.Count; i++)
{
var cab = cabs[i];
if (CABMap.TryGetValue(cab, out var entry))
{
if (!path.Contains(entry.Path))
{
if (!offsets.ContainsKey(entry.Path))
{
offsets.Add(entry.Path, new HashSet<long>());
}
offsets[entry.Path].Add(entry.Offset);
}
foreach (var dep in entry.Dependencies)
{
if (!cabs.Contains(dep))
cabs.Add(dep);
}
}
}
}
public static bool FindCAB(string path, out List<string> cabs)
{
cabs = CABMap.Where(x => x.Value.Path.Contains(path)).Select(x => x.Key).ToList();
return cabs.Count != 0;
}
public static string[] ProcessFiles(string[] files)
{
foreach (var file in files)
{
if (!offsets.ContainsKey(file))
{
offsets.Add(file, new HashSet<long>());
}
if (FindCAB(file, out var cabs))
{
AddCABOffsets(files, cabs);
}
}
return offsets.Keys.ToArray();
}
public static string[] ProcessDependencies(string[] files)
{
if (CABMap.Count == 0)
{
Logger.Warning("CABMap is not build, skip resolving dependencies...");
}
else
{
Logger.Info("Resolving Dependencies...");
var file = files.FirstOrDefault();
var supportedExtensions = GameManager.GetGames().Select(x => x.Extension).ToList();
if (supportedExtensions.Contains(Path.GetExtension(file)))
{
files = ProcessFiles(files);
}
}
return files;
}
}
public class Entry : IComparable<Entry>
{
public string Path;
public long Offset;
public List<string> Dependencies;
public Entry(string path, long offset, List<string> dependencies)
{
Path = path;
Offset = offset;
Dependencies = dependencies;
}
public int CompareTo(Entry other)
{
if (other == null) return 1;
int result;
if (other == null)
throw new ArgumentException("Object is not an Entry");
result = Path.CompareTo(other.Path);
if (result == 0)
result = Offset.CompareTo(other.Offset);
return result;
}
}
}

View File

@@ -19,17 +19,5 @@ namespace AssetStudio
m_Animations[i] = new PPtr<AnimationClip>(reader); m_Animations[i] = new PPtr<AnimationClip>(reader);
} }
} }
public bool IsContainsAnimationClip(AnimationClip clip)
{
foreach (PPtr<AnimationClip> ptr in m_Animations)
{
if (ptr.TryGet(out var animationClip) && animationClip.Equals(clip))
{
return true;
}
}
return false;
}
} }
} }

View File

@@ -1,12 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Runtime.InteropServices;
namespace AssetStudio namespace AssetStudio
{ {
public class Keyframe<T> : IYAMLExportable public class Keyframe<T>
where T : struct, IYAMLExportable
{ {
public float time; public float time;
public T value; public T value;
@@ -15,7 +15,6 @@ namespace AssetStudio
public int weightedMode; public int weightedMode;
public T inWeight; public T inWeight;
public T outWeight; public T outWeight;
public int tangentMode;
public Keyframe(ObjectReader reader, Func<T> readerFunc) public Keyframe(ObjectReader reader, Func<T> readerFunc)
@@ -31,46 +30,9 @@ namespace AssetStudio
outWeight = readerFunc(); outWeight = readerFunc();
} }
} }
public Keyframe(float time, T value, T weight) : this(time, value, default, default, weight)
{
tangentMode = 0;
}
public Keyframe(float time, T value, T inSlope, T outSlope, T weight)
{
this.time = time;
this.value = value;
this.inSlope = inSlope;
this.outSlope = outSlope;
weightedMode = 0;
inWeight = weight;
outWeight = weight;
tangentMode = 1;
}
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.AddSerializedVersion(2);
node.Add(nameof(time), time);
node.Add(nameof(value), value.ExportYAML());
node.Add(nameof(inSlope), inSlope.ExportYAML());
node.Add(nameof(outSlope), outSlope.ExportYAML());
node.Add(nameof(tangentMode), tangentMode);
node.Add(nameof(weightedMode), weightedMode);
node.Add(nameof(inWeight), inWeight.ExportYAML());
node.Add(nameof(outWeight), outWeight.ExportYAML());
return node;
}
public static Float DefaultFloatWeight => 1.0f / 3.0f;
public static Vector3 DefaultVector3Weight => new Vector3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f);
public static Quaternion DefaultQuaternionWeight => new Quaternion(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f);
} }
public class AnimationCurve<T> : IYAMLExportable public class AnimationCurve<T>
where T : struct, IYAMLExportable
{ {
public Keyframe<T>[] m_Curve; public Keyframe<T>[] m_Curve;
public int m_PreInfinity; public int m_PreInfinity;
@@ -94,40 +56,9 @@ namespace AssetStudio
m_RotationOrder = reader.ReadInt32(); m_RotationOrder = reader.ReadInt32();
} }
} }
public AnimationCurve()
{
m_PreInfinity = 2;
m_PostInfinity = 2;
m_RotationOrder = 4;
m_Curve = Array.Empty<Keyframe<T>>();
}
public AnimationCurve(List<Keyframe<T>> keyframes)
{
m_PreInfinity = 2;
m_PostInfinity = 2;
m_RotationOrder = 4;
m_Curve = new Keyframe<T>[keyframes.Count];
for (int i = 0; i < keyframes.Count; i++)
{
m_Curve[i] = keyframes[i];
}
}
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.AddSerializedVersion(2);
node.Add(nameof(m_Curve), m_Curve.ExportYAML());
node.Add(nameof(m_PreInfinity), m_PreInfinity);
node.Add(nameof(m_PostInfinity), m_PostInfinity);
node.Add(nameof(m_RotationOrder), m_RotationOrder);
return node;
}
} }
public class QuaternionCurve : IYAMLExportable public class QuaternionCurve
{ {
public AnimationCurve<Quaternion> curve; public AnimationCurve<Quaternion> curve;
public string path; public string path;
@@ -137,79 +68,9 @@ namespace AssetStudio
curve = new AnimationCurve<Quaternion>(reader, reader.ReadQuaternion); curve = new AnimationCurve<Quaternion>(reader, reader.ReadQuaternion);
path = reader.ReadAlignedString(); path = reader.ReadAlignedString();
} }
public QuaternionCurve(string path)
{
curve = new AnimationCurve<Quaternion>();
this.path = path;
}
public QuaternionCurve(QuaternionCurve copy, List<Keyframe<Quaternion>> keyframes) : this(copy.path, keyframes) { }
public QuaternionCurve(string path, List<Keyframe<Quaternion>> keyframes)
{
curve = new AnimationCurve<Quaternion>(keyframes);
this.path = path;
}
public YAMLNode ExportYAML()
{
YAMLMappingNode node = new YAMLMappingNode();
node.Add(nameof(curve), curve.ExportYAML());
node.Add(nameof(path), path);
return node;
}
public override bool Equals(object obj)
{
if (obj is QuaternionCurve quaternionCurve)
{
return path == quaternionCurve.path;
}
return false;
}
public override int GetHashCode()
{
int hash = 199;
unchecked
{
hash = 617 + hash * path.GetHashCode();
}
return hash;
}
} }
public class ACLClip public class PackedFloatVector
{
public byte[] m_ClipData;
public uint[] m_ClipDataUint;
public uint m_CurveCount;
public uint m_ConstCurveCount;
public ACLClip(ObjectReader reader)
{
if (reader.Game.Name == "SR_CB2" || reader.Game.Name == "SR_CB3")
{
m_ClipDataUint = reader.ReadUInt32Array();
}
else
{
m_ClipData = reader.ReadUInt8Array();
reader.AlignStream();
}
m_CurveCount = reader.ReadUInt32();
if (reader.Game.Name == "SR_CB2" || reader.Game.Name == "SR_CB3")
{
m_ConstCurveCount = reader.ReadUInt32();
}
}
public bool IsSet => m_ClipDataUint != null && m_ClipDataUint.Length > 0 || m_ClipData != null && m_ClipData.Length > 0;
}
public class PackedFloatVector : IYAMLExportable
{ {
public uint m_NumItems; public uint m_NumItems;
public float m_Range; public float m_Range;
@@ -231,17 +92,6 @@ namespace AssetStudio
reader.AlignStream(); reader.AlignStream();
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Add(nameof(m_NumItems), m_NumItems);
node.Add(nameof(m_Range), m_Range);
node.Add(nameof(m_Start), m_Start);
node.Add(nameof(m_Data), m_Data.ExportYAML());
node.Add(nameof(m_BitSize), m_BitSize);
return node;
}
public float[] UnpackFloats(int itemCountInChunk, int chunkStride, int start = 0, int numChunks = -1) public float[] UnpackFloats(int itemCountInChunk, int chunkStride, int start = 0, int numChunks = -1)
{ {
int bitPos = m_BitSize * start; int bitPos = m_BitSize * start;
@@ -281,7 +131,7 @@ namespace AssetStudio
} }
} }
public class PackedIntVector : IYAMLExportable public class PackedIntVector
{ {
public uint m_NumItems; public uint m_NumItems;
public byte[] m_Data; public byte[] m_Data;
@@ -299,15 +149,6 @@ namespace AssetStudio
reader.AlignStream(); reader.AlignStream();
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Add(nameof(m_NumItems), m_NumItems);
node.Add(nameof(m_Data), m_Data.ExportYAML());
node.Add(nameof(m_BitSize), m_BitSize);
return node;
}
public int[] UnpackInts() public int[] UnpackInts()
{ {
var data = new int[m_NumItems]; var data = new int[m_NumItems];
@@ -335,7 +176,7 @@ namespace AssetStudio
} }
} }
public class PackedQuatVector : IYAMLExportable public class PackedQuatVector
{ {
public uint m_NumItems; public uint m_NumItems;
public byte[] m_Data; public byte[] m_Data;
@@ -350,14 +191,6 @@ namespace AssetStudio
reader.AlignStream(); reader.AlignStream();
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Add(nameof(m_NumItems), m_NumItems);
node.Add(nameof(m_Data), m_Data.ExportYAML());
return node;
}
public Quaternion[] UnpackQuats() public Quaternion[] UnpackQuats()
{ {
var data = new Quaternion[m_NumItems]; var data = new Quaternion[m_NumItems];
@@ -423,7 +256,7 @@ namespace AssetStudio
} }
} }
public class CompressedAnimationCurve : IYAMLExportable public class CompressedAnimationCurve
{ {
public string m_Path; public string m_Path;
public PackedIntVector m_Times; public PackedIntVector m_Times;
@@ -441,21 +274,9 @@ namespace AssetStudio
m_PreInfinity = reader.ReadInt32(); m_PreInfinity = reader.ReadInt32();
m_PostInfinity = reader.ReadInt32(); m_PostInfinity = reader.ReadInt32();
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Add(nameof(m_Path), m_Path);
node.Add(nameof(m_Times), m_Times.ExportYAML());
node.Add(nameof(m_Values), m_Values.ExportYAML());
node.Add(nameof(m_Slopes), m_Slopes.ExportYAML());
node.Add(nameof(m_PreInfinity), m_PreInfinity);
node.Add(nameof(m_PostInfinity), m_PostInfinity);
return node;
}
} }
public class Vector3Curve : IYAMLExportable public class Vector3Curve
{ {
public AnimationCurve<Vector3> curve; public AnimationCurve<Vector3> curve;
public string path; public string path;
@@ -465,51 +286,11 @@ namespace AssetStudio
curve = new AnimationCurve<Vector3>(reader, reader.ReadVector3); curve = new AnimationCurve<Vector3>(reader, reader.ReadVector3);
path = reader.ReadAlignedString(); path = reader.ReadAlignedString();
} }
public Vector3Curve(Vector3Curve copy, List<Keyframe<Vector3>> keyframes)
: this(copy.path, keyframes) { }
public Vector3Curve(string path)
{
curve = new AnimationCurve<Vector3>();
this.path = path;
}
public Vector3Curve(string path, List<Keyframe<Vector3>> keyframes)
{
curve = new AnimationCurve<Vector3>(keyframes);
this.path = path;
}
public YAMLNode ExportYAML()
{
YAMLMappingNode node = new YAMLMappingNode();
node.Add(nameof(curve), curve.ExportYAML());
node.Add(nameof(path), path);
return node;
}
public override bool Equals(object obj)
{
if (obj is Vector3Curve vector3Curve)
{
return path == vector3Curve.path;
}
return false;
}
public override int GetHashCode()
{
int hash = 577;
unchecked
{
hash = 419 + hash * path.GetHashCode();
}
return hash;
}
} }
public class FloatCurve : IYAMLExportable public class FloatCurve
{ {
public AnimationCurve<Float> curve; public AnimationCurve<float> curve;
public string attribute; public string attribute;
public string path; public string path;
public ClassIDType classID; public ClassIDType classID;
@@ -518,64 +299,15 @@ namespace AssetStudio
public FloatCurve(ObjectReader reader) public FloatCurve(ObjectReader reader)
{ {
curve = new AnimationCurve<Float>(reader, reader.ReadFloat); curve = new AnimationCurve<float>(reader, reader.ReadSingle);
attribute = reader.ReadAlignedString(); attribute = reader.ReadAlignedString();
path = reader.ReadAlignedString(); path = reader.ReadAlignedString();
classID = (ClassIDType)reader.ReadInt32(); classID = (ClassIDType)reader.ReadInt32();
script = new PPtr<MonoScript>(reader); script = new PPtr<MonoScript>(reader);
} }
public FloatCurve(FloatCurve copy, List<Keyframe<Float>> keyframes) : this(copy.path, copy.attribute, copy.classID, copy.script, keyframes) { }
public FloatCurve(string path, string attribute, ClassIDType classID, PPtr<MonoScript> script)
{
curve = new AnimationCurve<Float>();
this.attribute = attribute;
this.path = path;
this.classID = classID;
this.script = script;
}
public FloatCurve(string path, string attribute, ClassIDType classID, PPtr<MonoScript> script, List<Keyframe<Float>> keyframes)
: this(path, attribute, classID, script)
{
curve = new AnimationCurve<Float>(keyframes);
this.attribute = attribute;
this.path = path;
this.classID = classID;
this.script = script;
}
public YAMLNode ExportYAML()
{
YAMLMappingNode node = new YAMLMappingNode();
node.Add(nameof(curve), curve.ExportYAML());
node.Add(nameof(attribute), attribute);
node.Add(nameof(path), path);
node.Add(nameof(classID), (int)classID);
node.Add(nameof(script), script.ExportYAML());
return node;
}
public override bool Equals(object obj)
{
if (obj is FloatCurve floatCurve)
{
return attribute == floatCurve.attribute && path == floatCurve.path && classID == floatCurve.classID;
}
return false;
}
public override int GetHashCode()
{
int hash = 17;
unchecked
{
hash = hash * 23 + path.GetHashCode();
}
return hash;
}
} }
public class PPtrKeyframe : IYAMLExportable public class PPtrKeyframe
{ {
public float time; public float time;
public PPtr<Object> value; public PPtr<Object> value;
@@ -586,27 +318,14 @@ namespace AssetStudio
time = reader.ReadSingle(); time = reader.ReadSingle();
value = new PPtr<Object>(reader); value = new PPtr<Object>(reader);
} }
public PPtrKeyframe(float time, PPtr<Object> script)
{
this.time = time;
value = script;
}
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Add(nameof(time), time);
node.Add(nameof(value), value.ExportYAML());
return node;
}
} }
public class PPtrCurve : IYAMLExportable public class PPtrCurve
{ {
public PPtrKeyframe[] curve; public PPtrKeyframe[] curve;
public string attribute; public string attribute;
public string path; public string path;
public ClassIDType classID; public int classID;
public PPtr<MonoScript> script; public PPtr<MonoScript> script;
@@ -621,63 +340,12 @@ namespace AssetStudio
attribute = reader.ReadAlignedString(); attribute = reader.ReadAlignedString();
path = reader.ReadAlignedString(); path = reader.ReadAlignedString();
classID = (ClassIDType)reader.ReadInt32(); classID = reader.ReadInt32();
script = new PPtr<MonoScript>(reader); script = new PPtr<MonoScript>(reader);
} }
public PPtrCurve(PPtrCurve copy, List<PPtrKeyframe> keyframes) : this(copy.path, copy.attribute, copy.classID, copy.script, keyframes) { }
public PPtrCurve(string path, string attribute, ClassIDType classID, PPtr<MonoScript> script)
{
this.attribute = attribute;
this.path = path;
this.classID = classID;
this.script = script;
}
public PPtrCurve(string path, string attribute, ClassIDType classID, PPtr<MonoScript> script, IReadOnlyList<PPtrKeyframe> keyframes) :
this(path, attribute, classID, script)
{
curve = new PPtrKeyframe[keyframes.Count];
for (int i = 0; i < keyframes.Count; i++)
{
curve[i] = keyframes[i];
}
}
public YAMLNode ExportYAML()
{
YAMLMappingNode node = new YAMLMappingNode();
node.Add(nameof(curve), curve.ExportYAML());
node.Add(nameof(attribute), attribute);
node.Add(nameof(path), path);
node.Add(nameof(classID), ((int)classID).ToString());
node.Add(nameof(script), script.ExportYAML());
return node;
}
public override bool Equals(object obj)
{
if (obj is PPtrCurve pptrCurve)
{
return this == pptrCurve;
}
return false;
}
public override int GetHashCode()
{
int hash = 113;
unchecked
{
hash = hash + 457 * attribute.GetHashCode();
hash = hash * 433 + path.GetHashCode();
hash = hash * 223 + classID.GetHashCode();
hash = hash * 911 + script.GetHashCode();
}
return hash;
}
} }
public class AABB : IYAMLExportable public class AABB
{ {
public Vector3 m_Center; public Vector3 m_Center;
public Vector3 m_Extent; public Vector3 m_Extent;
@@ -687,14 +355,6 @@ namespace AssetStudio
m_Center = reader.ReadVector3(); m_Center = reader.ReadVector3();
m_Extent = reader.ReadVector3(); m_Extent = reader.ReadVector3();
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Add(nameof(m_Center), m_Center.ExportYAML());
node.Add(nameof(m_Extent), m_Extent.ExportYAML());
return node;
}
} }
public class xform public class xform
@@ -796,6 +456,33 @@ namespace AssetStudio
} }
} }
public class ACLClip
{
public byte[] m_ClipData;
public uint m_CurveCount;
public uint m_ConstCurveCount;
public ACLClip(ObjectReader reader)
{
var byteCount = reader.ReadInt32();
if (reader.Game.Type.IsSRGroup())
{
byteCount *= 4;
}
m_ClipData = reader.ReadBytes(byteCount);
reader.AlignStream();
m_CurveCount = reader.ReadUInt32();
if (reader.Game.Type.IsSRGroup())
{
m_ConstCurveCount = reader.ReadUInt32();
}
}
}
public class StreamedClip public class StreamedClip
{ {
public uint[] data; public uint[] data;
@@ -816,7 +503,7 @@ namespace AssetStudio
public float outSlope; public float outSlope;
public float inSlope; public float inSlope;
public StreamedCurveKey(BinaryReader reader) public StreamedCurveKey(EndianBinaryReader reader)
{ {
index = reader.ReadInt32(); index = reader.ReadInt32();
coeff = reader.ReadSingleArray(4); coeff = reader.ReadSingleArray(4);
@@ -847,7 +534,7 @@ namespace AssetStudio
public float time; public float time;
public StreamedCurveKey[] keyList; public StreamedCurveKey[] keyList;
public StreamedFrame(BinaryReader reader) public StreamedFrame(EndianBinaryReader reader)
{ {
time = reader.ReadSingle(); time = reader.ReadSingle();
@@ -865,7 +552,7 @@ namespace AssetStudio
var frameList = new List<StreamedFrame>(); var frameList = new List<StreamedFrame>();
var buffer = new byte[data.Length * 4]; var buffer = new byte[data.Length * 4];
Buffer.BlockCopy(data, 0, buffer, 0, buffer.Length); Buffer.BlockCopy(data, 0, buffer, 0, buffer.Length);
using (var reader = new BinaryReader(new MemoryStream(buffer))) using (var reader = new EndianBinaryReader(new MemoryStream(buffer), EndianType.LittleEndian))
{ {
while (reader.BaseStream.Position < reader.BaseStream.Length) while (reader.BaseStream.Position < reader.BaseStream.Length)
{ {
@@ -959,10 +646,10 @@ namespace AssetStudio
public class Clip public class Clip
{ {
public ACLClip m_ACLClip;
public StreamedClip m_StreamedClip; public StreamedClip m_StreamedClip;
public DenseClip m_DenseClip; public DenseClip m_DenseClip;
public ConstantClip m_ConstantClip; public ConstantClip m_ConstantClip;
public ACLClip m_ACLClip;
public ValueArrayConstant m_Binding; public ValueArrayConstant m_Binding;
public Clip(ObjectReader reader) public Clip(ObjectReader reader)
@@ -970,7 +657,7 @@ namespace AssetStudio
var version = reader.version; var version = reader.version;
m_StreamedClip = new StreamedClip(reader); m_StreamedClip = new StreamedClip(reader);
m_DenseClip = new DenseClip(reader); m_DenseClip = new DenseClip(reader);
if (reader.Game.Name == "SR_CB2" || reader.Game.Name == "SR_CB3") if (reader.Game.Type.IsSRGroup())
{ {
m_ACLClip = new ACLClip(reader); m_ACLClip = new ACLClip(reader);
} }
@@ -978,7 +665,7 @@ namespace AssetStudio
{ {
m_ConstantClip = new ConstantClip(reader); m_ConstantClip = new ConstantClip(reader);
} }
if (reader.Game.Name != "SR_CB2" && reader.Game.Name != "SR_CB3" && reader.Game.Name != "TOT") if (reader.Game.Type.IsGIGroup() || reader.Game.Type.IsBH3() || reader.Game.Type.IsZZZCB1())
{ {
m_ACLClip = new ACLClip(reader); m_ACLClip = new ACLClip(reader);
} }
@@ -1045,7 +732,7 @@ namespace AssetStudio
} }
} }
public class ClipMuscleConstant : IYAMLExportable public class ClipMuscleConstant
{ {
public HumanPose m_DeltaPose; public HumanPose m_DeltaPose;
public xform m_StartX; public xform m_StartX;
@@ -1137,45 +824,23 @@ namespace AssetStudio
m_HeightFromFeet = reader.ReadBoolean(); m_HeightFromFeet = reader.ReadBoolean();
reader.AlignStream(); reader.AlignStream();
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.AddSerializedVersion(2);
node.Add(nameof(m_StartTime), m_StartTime);
node.Add(nameof(m_StopTime), m_StopTime);
node.Add(nameof(m_OrientationOffsetY), m_OrientationOffsetY);
node.Add(nameof(m_Level), m_Level);
node.Add(nameof(m_CycleOffset), m_CycleOffset);
node.Add(nameof(m_LoopTime), m_LoopTime);
node.Add(nameof(m_LoopBlend), m_LoopBlend);
node.Add(nameof(m_LoopBlendOrientation), m_LoopBlendOrientation);
node.Add(nameof(m_LoopBlendPositionY), m_LoopBlendPositionY);
node.Add(nameof(m_LoopBlendPositionXZ), m_LoopBlendPositionXZ);
node.Add(nameof(m_KeepOriginalOrientation), m_KeepOriginalOrientation);
node.Add(nameof(m_KeepOriginalPositionY), m_KeepOriginalPositionY);
node.Add(nameof(m_KeepOriginalPositionXZ), m_KeepOriginalPositionXZ);
node.Add(nameof(m_HeightFromFeet), m_HeightFromFeet);
node.Add(nameof(m_Mirror), m_Mirror);
return node;
}
} }
public class GenericBinding : IYAMLExportable public class GenericBinding
{ {
public int[] version;
public uint path; public uint path;
public uint attribute; public uint attribute;
public PPtr<Object> script; public PPtr<Object> script;
public ClassIDType typeID; public ClassIDType typeID;
public byte customType; public byte customType;
public byte isPPtrCurve; public byte isPPtrCurve;
public byte isIntCurve;
public GenericBinding() { } public GenericBinding() { }
public GenericBinding(ObjectReader reader) public GenericBinding(ObjectReader reader)
{ {
version = reader.version; var version = reader.version;
path = reader.ReadUInt32(); path = reader.ReadUInt32();
attribute = reader.ReadUInt32(); attribute = reader.ReadUInt32();
script = new PPtr<Object>(reader); script = new PPtr<Object>(reader);
@@ -1189,24 +854,15 @@ namespace AssetStudio
} }
customType = reader.ReadByte(); customType = reader.ReadByte();
isPPtrCurve = reader.ReadByte(); isPPtrCurve = reader.ReadByte();
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 1)) //2022.1 and up
{
isIntCurve = reader.ReadByte();
}
reader.AlignStream(); reader.AlignStream();
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Add(nameof(path), path);
node.Add(nameof(attribute), attribute);
node.Add(nameof(script), script.ExportYAML());
node.Add("classID", ((int)typeID).ToString());
node.Add(nameof(customType), ((int)customType).ToString());
node.Add(nameof(isPPtrCurve), isPPtrCurve);
return node;
}
public int GetDimension() => attribute == 2 ? 4 : 3;
} }
public class AnimationClipBindingConstant : IYAMLExportable
public class AnimationClipBindingConstant
{ {
public GenericBinding[] genericBindings; public GenericBinding[] genericBindings;
public PPtr<Object>[] pptrCurveMapping; public PPtr<Object>[] pptrCurveMapping;
@@ -1230,14 +886,6 @@ namespace AssetStudio
} }
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Add(nameof(genericBindings), genericBindings.ExportYAML());
node.Add(nameof(pptrCurveMapping), pptrCurveMapping.ExportYAML());
return node;
}
public GenericBinding FindBinding(int index) public GenericBinding FindBinding(int index)
{ {
int curves = 0; int curves = 0;
@@ -1274,7 +922,7 @@ namespace AssetStudio
} }
} }
public class AnimationEvent : IYAMLExportable public class AnimationEvent
{ {
public float time; public float time;
public string functionName; public string functionName;
@@ -1299,19 +947,6 @@ namespace AssetStudio
} }
messageOptions = reader.ReadInt32(); messageOptions = reader.ReadInt32();
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Add(nameof(time), time);
node.Add(nameof(functionName), functionName);
node.Add(nameof(data), data);
node.Add(nameof(objectReferenceParameter), objectReferenceParameter.ExportYAML());
node.Add(nameof(floatParameter), floatParameter);
node.Add(nameof(intParameter), intParameter);
node.Add(nameof(messageOptions), messageOptions);
return node;
}
} }
public enum AnimationType public enum AnimationType
@@ -1321,7 +956,7 @@ namespace AssetStudio
Humanoid = 3 Humanoid = 3
}; };
public sealed class AnimationClip : NamedObject, IYAMLExportable public sealed class AnimationClip : NamedObject
{ {
public AnimationType m_AnimationType; public AnimationType m_AnimationType;
public bool m_Legacy; public bool m_Legacy;
@@ -1339,9 +974,6 @@ namespace AssetStudio
public AABB m_Bounds; public AABB m_Bounds;
public uint m_MuscleClipSize; public uint m_MuscleClipSize;
public ClipMuscleConstant m_MuscleClip; public ClipMuscleConstant m_MuscleClip;
public byte[] m_AclClipData;
public GenericBinding[] m_AclBindings;
public KeyValuePair<float, float> m_AclRange;
public AnimationClipBindingConstant m_ClipBindingConstant; public AnimationClipBindingConstant m_ClipBindingConstant;
public AnimationEvent[] m_Events; public AnimationEvent[] m_Events;
@@ -1363,10 +995,6 @@ namespace AssetStudio
m_Legacy = true; m_Legacy = true;
} }
m_Compressed = reader.ReadBoolean(); m_Compressed = reader.ReadBoolean();
if (m_Compressed && reader.Game.Name != "ToT")
{
m_Compressed = false;
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3))//4.3 and up if (version[0] > 4 || (version[0] == 4 && version[1] >= 3))//4.3 and up
{ {
m_UseHighQualityCurve = reader.ReadBoolean(); m_UseHighQualityCurve = reader.ReadBoolean();
@@ -1438,19 +1066,19 @@ namespace AssetStudio
m_MuscleClipSize = reader.ReadUInt32(); m_MuscleClipSize = reader.ReadUInt32();
m_MuscleClip = new ClipMuscleConstant(reader); m_MuscleClip = new ClipMuscleConstant(reader);
} }
if (reader.Game.Type.IsSRGroup())
{
var m_AclClipData = reader.ReadUInt8Array();
var aclBindingsCount = reader.ReadInt32();
var m_AclBindings = new GenericBinding[aclBindingsCount];
for (int i = 0; i < aclBindingsCount; i++)
{
m_AclBindings[i] = new GenericBinding(reader);
}
var m_AclRange = new KeyValuePair<float, float>(reader.ReadSingle(), reader.ReadSingle());
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
{ {
if (reader.Game.Name == "SR_CB2" || reader.Game.Name == "SR_CB3")
{
m_AclClipData = reader.ReadUInt8Array();
var aclBindingsCount = reader.ReadInt32();
m_AclBindings = new GenericBinding[aclBindingsCount];
for (int i = 0; i < aclBindingsCount; i++)
{
m_AclBindings[i] = new GenericBinding(reader);
}
m_AclRange = new KeyValuePair<float, float>(reader.ReadSingle(), reader.ReadSingle());
}
m_ClipBindingConstant = new AnimationClipBindingConstant(reader); m_ClipBindingConstant = new AnimationClipBindingConstant(reader);
} }
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 3)) //2018.3 and up if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 3)) //2018.3 and up
@@ -1470,158 +1098,5 @@ namespace AssetStudio
reader.AlignStream(); reader.AlignStream();
} }
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Add(nameof(m_Name), m_Name);
node.AddSerializedVersion(6);
node.Add(nameof(m_Legacy), m_Legacy);
node.Add(nameof(m_Compressed), m_Compressed);
node.Add(nameof(m_UseHighQualityCurve), m_UseHighQualityCurve);
node.Add(nameof(m_RotationCurves), m_RotationCurves.ExportYAML());
node.Add(nameof(m_CompressedRotationCurves), m_CompressedRotationCurves.ExportYAML());
node.Add(nameof(m_EulerCurves), m_EulerCurves.ExportYAML());
node.Add(nameof(m_PositionCurves), m_PositionCurves.ExportYAML());
node.Add(nameof(m_ScaleCurves), m_ScaleCurves.ExportYAML());
node.Add(nameof(m_FloatCurves), m_FloatCurves.ExportYAML());
node.Add(nameof(m_PPtrCurves), m_PPtrCurves.ExportYAML());
node.Add(nameof(m_SampleRate), m_SampleRate);
node.Add(nameof(m_WrapMode), m_WrapMode);
node.Add(nameof(m_Bounds), m_Bounds.ExportYAML());
node.Add(nameof(m_ClipBindingConstant), m_ClipBindingConstant.ExportYAML());
node.Add("m_AnimationClipSettings", m_MuscleClip.ExportYAML());
node.Add(nameof(m_Events), m_Events.ExportYAML());
return node;
}
public Dictionary<uint, string> FindTOS()
{
var tos = new Dictionary<uint, string>() { { 0, string.Empty } };
foreach (var asset in assetsFile.assetsManager.assetsFileList.SelectMany(x => x.Objects).OrderBy(x => x.type).ToArray())
{
switch (asset.type)
{
case ClassIDType.Avatar:
var avatar = asset as Avatar;
if (AddAvatarTOS(avatar, tos))
{
return tos;
}
break;
case ClassIDType.Animator:
var animator = asset as Animator;
if (IsAnimatorContainsClip(animator))
{
if (AddAnimatorTOS(animator, tos))
{
return tos;
}
}
break;
case ClassIDType.Animation:
var animation = asset as Animation;
if (IsAnimationContainsClip(animation))
{
if (AddAnimationTOS(animation, tos))
{
return tos;
}
}
break;
}
}
return tos;
}
public IEnumerable<GameObject> FindRoots()
{
foreach (var asset in assetsFile.assetsManager.assetsFileList.SelectMany(x => x.Objects))
{
switch (asset.type)
{
case ClassIDType.Animator:
Animator animator = (Animator)asset;
if (IsAnimatorContainsClip(animator))
{
if (animator.m_GameObject.TryGet(out var go))
{
yield return go;
}
}
break;
case ClassIDType.Animation:
Animation animation = (Animation)asset;
if (IsAnimationContainsClip(animation))
{
if (animation.m_GameObject.TryGet(out var go))
{
yield return go;
}
}
break;
}
}
yield break;
}
private bool IsAnimatorContainsClip(Animator animator)
{
if (animator.m_Controller.TryGet(out var runtime))
{
return runtime.IsContainsAnimationClip(this);
}
else
{
return false;
}
}
private bool IsAnimationContainsClip(Animation animation)
{
return animation.IsContainsAnimationClip(this);
}
private bool AddAvatarTOS(Avatar avatar, Dictionary<uint, string> tos)
{
return AddTOS(avatar.m_TOS.ToDictionary(x => x.Key, x => x.Value), tos);
}
private bool AddAnimatorTOS(Animator animator, Dictionary<uint, string> tos)
{
if (animator.m_Avatar.TryGet(out var avatar))
{
if (AddAvatarTOS(avatar, tos))
{
return true;
}
}
Dictionary<uint, string> animatorTOS = animator.BuildTOS();
return AddTOS(animatorTOS, tos);
}
private bool AddAnimationTOS(Animation animation, Dictionary<uint, string> tos)
{
if (animation.m_GameObject.TryGet(out var go))
{
Dictionary<uint, string> animationTOS = go.BuildTOS();
return AddTOS(animationTOS, tos);
}
return false;
}
private bool AddTOS(Dictionary<uint, string> src, Dictionary<uint, string> dest)
{
int tosCount = m_ClipBindingConstant.genericBindings.Length;
for (int i = 0; i < tosCount; i++)
{
ref GenericBinding binding = ref m_ClipBindingConstant.genericBindings[i];
if (src.TryGetValue(binding.path, out string path))
{
dest[binding.path] = path;
if (dest.Count == tosCount)
{
return true;
}
}
}
return false;
}
} }
} }

View File

@@ -15,7 +15,7 @@ namespace AssetStudio
{ {
m_Avatar = new PPtr<Avatar>(reader); m_Avatar = new PPtr<Avatar>(reader);
m_Controller = new PPtr<RuntimeAnimatorController>(reader); m_Controller = new PPtr<RuntimeAnimatorController>(reader);
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3") if (reader.Game.Type.IsGISubGroup())
{ {
var m_FBIKAvatar = new PPtr<Object>(reader); //FBIKAvatar placeholder var m_FBIKAvatar = new PPtr<Object>(reader); //FBIKAvatar placeholder
} }
@@ -67,31 +67,5 @@ namespace AssetStudio
reader.AlignStream(); reader.AlignStream();
} }
} }
public Dictionary<uint, string> BuildTOS()
{
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3))
{
if (m_HasTransformHierarchy)
{
if (m_GameObject.TryGet(out var go))
{
return go.BuildTOS();
}
}
else
{
return new Dictionary<uint, string>() { { 0, string.Empty } };
}
}
else
{
if (m_GameObject.TryGet(out var go))
{
return go.BuildTOS();
}
}
return null;
}
} }
} }

View File

@@ -580,6 +580,7 @@ namespace AssetStudio
public sealed class AnimatorController : RuntimeAnimatorController public sealed class AnimatorController : RuntimeAnimatorController
{ {
public Dictionary<uint, string> m_TOS;
public PPtr<AnimationClip>[] m_AnimationClips; public PPtr<AnimationClip>[] m_AnimationClips;
public AnimatorController(ObjectReader reader) : base(reader) public AnimatorController(ObjectReader reader) : base(reader)
@@ -588,7 +589,7 @@ namespace AssetStudio
var m_Controller = new ControllerConstant(reader); var m_Controller = new ControllerConstant(reader);
int tosSize = reader.ReadInt32(); int tosSize = reader.ReadInt32();
var m_TOS = new Dictionary<uint, string>(tosSize); m_TOS = new Dictionary<uint, string>(tosSize);
for (int i = 0; i < tosSize; i++) for (int i = 0; i < tosSize; i++)
{ {
m_TOS.Add(reader.ReadUInt32(), reader.ReadAlignedString()); m_TOS.Add(reader.ReadUInt32(), reader.ReadAlignedString());
@@ -601,17 +602,5 @@ namespace AssetStudio
m_AnimationClips[i] = new PPtr<AnimationClip>(reader); m_AnimationClips[i] = new PPtr<AnimationClip>(reader);
} }
} }
public override bool IsContainsAnimationClip(AnimationClip clip)
{
foreach (PPtr<AnimationClip> ptr in m_AnimationClips)
{
if (ptr.TryGet(out var animationClip) && animationClip.Equals(clip))
{
return true;
}
}
return false;
}
} }
} }

View File

@@ -33,26 +33,5 @@ namespace AssetStudio
m_Clips[i] = new AnimationClipOverride(reader); m_Clips[i] = new AnimationClipOverride(reader);
} }
} }
public override bool IsContainsAnimationClip(AnimationClip clip)
{
AnimationClip animationClip;
foreach (AnimationClipOverride overClip in m_Clips)
{
if (overClip.m_OriginalClip.TryGet(out animationClip) && animationClip.Equals(clip))
{
return true;
}
else if (overClip.m_OverrideClip.TryGet(out animationClip) && animationClip.Equals(clip))
{
return true;
}
}
if (m_Controller.TryGet(out var baseController))
{
return baseController.IsContainsAnimationClip(clip);
}
return false;
}
} }
} }

View File

@@ -1,5 +1,8 @@
using Newtonsoft.Json; using System;
using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio namespace AssetStudio
{ {
@@ -19,76 +22,64 @@ namespace AssetStudio
public sealed class AssetBundle : NamedObject public sealed class AssetBundle : NamedObject
{ {
public static bool Exportable; public PPtr<Object>[] m_PreloadTable;
public KeyValuePair<string, AssetInfo>[] m_Container;
public PPtr<Object>[] PreloadTable; //public AssetInfo m_MainAsset;
public KeyValuePair<string, AssetInfo>[] Container; //public uint m_RuntimeComaptability;
public AssetInfo MainAsset; //public string m_AssetBundleName;
public uint RuntimeComaptability; //public string[] m_Dependencies;
public string AssetBundleName; //public bool m_IsStreamedSceneAssetBundle;
public int DependencyCount; //public int m_ExplicitDataLayout;
public string[] Dependencies; //public int m_PathFlags;
public bool IsStreamedScenessetBundle; //public KeyValuePair<string, string>[] m_SceneHashes;
public int ExplicitDataLayout;
public int PathFlags;
public int SceneHashCount;
public KeyValuePair<string, string>[] SceneHashes;
public AssetBundle(ObjectReader reader) : base(reader) public AssetBundle(ObjectReader reader) : base(reader)
{ {
var m_PreloadTableSize = reader.ReadInt32(); var m_PreloadTableSize = reader.ReadInt32();
PreloadTable = new PPtr<Object>[m_PreloadTableSize]; m_PreloadTable = new PPtr<Object>[m_PreloadTableSize];
for (int i = 0; i < m_PreloadTableSize; i++) for (int i = 0; i < m_PreloadTableSize; i++)
{ {
PreloadTable[i] = new PPtr<Object>(reader); m_PreloadTable[i] = new PPtr<Object>(reader);
} }
var m_ContainerSize = reader.ReadInt32(); var m_ContainerSize = reader.ReadInt32();
Container = new KeyValuePair<string, AssetInfo>[m_ContainerSize]; m_Container = new KeyValuePair<string, AssetInfo>[m_ContainerSize];
for (int i = 0; i < m_ContainerSize; i++) for (int i = 0; i < m_ContainerSize; i++)
{ {
Container[i] = new KeyValuePair<string, AssetInfo>(reader.ReadAlignedString(), new AssetInfo(reader)); m_Container[i] = new KeyValuePair<string, AssetInfo>(reader.ReadAlignedString(), new AssetInfo(reader));
} }
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3") //if (reader.Game.Type.IsMhyGroup())
{ //{
MainAsset = new AssetInfo(reader); // m_MainAsset = new AssetInfo(reader);
RuntimeComaptability = reader.ReadUInt32(); // m_RuntimeComaptability = reader.ReadUInt32();
} // m_AssetBundleName = reader.ReadAlignedString();
// var dependencyCount = reader.ReadInt32();
AssetBundleName = reader.ReadAlignedString(); // m_Dependencies = new string[dependencyCount];
DependencyCount = reader.ReadInt32(); // for (int k = 0; k < dependencyCount; k++)
Dependencies = new string[DependencyCount]; // {
for (int k = 0; k < DependencyCount; k++) // m_Dependencies[k] = reader.ReadAlignedString();
{ // }
Dependencies[k] = reader.ReadAlignedString(); // if (reader.Game.Type.IsGIGroup())
} // {
if (reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2") // m_IsStreamedSceneAssetBundle = reader.ReadBoolean();
{ // reader.AlignStream();
IsStreamedScenessetBundle = reader.ReadBoolean(); // if (reader.Game.Type.IsGICB3() || reader.Game.Type.IsGI())
reader.AlignStream(); // {
PathFlags = reader.ReadInt32(); // m_ExplicitDataLayout = reader.ReadInt32();
} // }
else if (reader.Game.Name == "GI_CB3") // m_PathFlags = reader.ReadInt32();
{ // if (reader.Game.Type.IsGI())
IsStreamedScenessetBundle = reader.ReadBoolean(); // {
reader.AlignStream(); // var sceneHashCount = reader.ReadInt32();
ExplicitDataLayout = reader.ReadInt32(); // m_SceneHashes = new KeyValuePair<string, string>[sceneHashCount];
PathFlags = reader.ReadInt32(); // for (int l = 0; l < sceneHashCount; l++)
} // {
else if (reader.Game.Name == "GI") // m_SceneHashes[l] = new KeyValuePair<string, string>(reader.ReadAlignedString(), reader.ReadAlignedString());
{ // }
IsStreamedScenessetBundle = reader.ReadBoolean(); // }
reader.AlignStream(); // }
ExplicitDataLayout = reader.ReadInt32(); //}
PathFlags = reader.ReadInt32();
SceneHashCount = reader.ReadInt32();
SceneHashes = new KeyValuePair<string, string>[SceneHashCount];
for (int l = 0; l < SceneHashCount; l++)
{
SceneHashes[l] = new KeyValuePair<string, string>(reader.ReadAlignedString(), reader.ReadAlignedString());
}
}
} }
} }
} }

View File

@@ -1,5 +1,4 @@
using SevenZip; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -10,8 +9,6 @@ namespace AssetStudio
{ {
public PPtr<Component>[] m_Components; public PPtr<Component>[] m_Components;
public string m_Name; public string m_Name;
public uint m_Tag;
public bool m_IsActive;
public Transform m_Transform; public Transform m_Transform;
public MeshRenderer m_MeshRenderer; public MeshRenderer m_MeshRenderer;
@@ -35,67 +32,6 @@ namespace AssetStudio
var m_Layer = reader.ReadInt32(); var m_Layer = reader.ReadInt32();
m_Name = reader.ReadAlignedString(); m_Name = reader.ReadAlignedString();
m_Tag = reader.ReadUInt16();
m_IsActive = reader.ReadBoolean();
}
public Transform GetTransform()
{
foreach (PPtr<Component> ptr in FetchComponents())
{
if (!ptr.TryGet(out var comp))
{
continue;
}
if (comp.type == ClassIDType.Transform)
{
return comp as Transform;
}
}
throw new Exception("Can't find transform component");
}
private List<PPtr<Component>> FetchComponents()
{
return m_Components.ToList();
}
public T FindComponent<T>()
where T : Component
{
foreach (PPtr<Component> ptr in FetchComponents())
{
// component could has not impelemented asset type
if (ptr.TryGet(out var comp) && comp is T t)
{
return t;
}
}
return null;
}
public Dictionary<uint, string> BuildTOS()
{
Dictionary<uint, string> tos = new Dictionary<uint, string>() { { 0, string.Empty } };
BuildTOS(this, string.Empty, tos);
return tos;
}
private void BuildTOS(GameObject parent, string parentPath, Dictionary<uint, string> tos)
{
Transform transform = parent.GetTransform();
foreach (PPtr<Transform> childPtr in transform.m_Children)
{
if (childPtr.TryGet(out var childTransform))
{
if (childTransform.m_GameObject.TryGet(out var child))
{
string path = parentPath != string.Empty ? parentPath + '/' + child.m_Name : child.m_Name;
var pathHash = CRC.CalculateDigestUTF8(path);
tos[pathHash] = path;
BuildTOS(child, path, tos);
}
}
}
} }
} }
} }

View File

@@ -1,5 +1,4 @@
using Newtonsoft.Json; using System.Collections.Generic;
using System.Collections.Generic;
namespace AssetStudio namespace AssetStudio
{ {
@@ -10,7 +9,6 @@ namespace AssetStudio
public Index(ObjectReader reader) public Index(ObjectReader reader)
{ {
Object = new PPtr<Object>(reader); Object = new PPtr<Object>(reader);
Size = reader.ReadUInt64(); Size = reader.ReadUInt64();
} }
@@ -18,25 +16,16 @@ namespace AssetStudio
public sealed class IndexObject : NamedObject public sealed class IndexObject : NamedObject
{ {
public static bool Exportable;
public int Count; public int Count;
public Dictionary<string, Index> AssetMap; public KeyValuePair<string, Index>[] AssetMap;
public Dictionary<long, string> Names = new Dictionary<long, string>();
public IndexObject(ObjectReader reader) : base(reader) public IndexObject(ObjectReader reader) : base(reader)
{ {
Count = reader.ReadInt32(); Count = reader.ReadInt32();
AssetMap = new Dictionary<string, Index>(Count); AssetMap = new KeyValuePair<string, Index>[Count];
for (int i = 0; i < Count; i++) for (int i = 0; i < Count; i++)
{ {
var key = reader.ReadAlignedString(); AssetMap[i] = new KeyValuePair<string, Index>(reader.ReadAlignedString(), new Index(reader));
var value = new Index(reader);
AssetMap.Add(key, value);
if (value.Object.m_FileID == 0)
Names.Add(value.Object.m_PathID, key);
} }
} }
} }

View File

@@ -1,7 +1,4 @@
using Newtonsoft.Json; using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using SevenZip;
using System.Collections.Generic;
namespace AssetStudio namespace AssetStudio
{ {
@@ -21,94 +18,52 @@ namespace AssetStudio
public class UnityPropertySheet public class UnityPropertySheet
{ {
private const string HDRPostfixName = "_HDR"; public KeyValuePair<string, UnityTexEnv>[] m_TexEnvs;
private const string STPostfixName = "_ST"; public KeyValuePair<string, int>[] m_Ints;
private const string TexelSizePostfixName = "_TexelSize"; public KeyValuePair<string, float>[] m_Floats;
public KeyValuePair<string, Color>[] m_Colors;
public Dictionary<string, UnityTexEnv> m_TexEnvs;
public Dictionary<string, int> m_Ints;
public Dictionary<string, float> m_Floats;
public Dictionary<string, Color> m_Colors;
public UnityPropertySheet(ObjectReader reader) public UnityPropertySheet(ObjectReader reader)
{ {
var version = reader.version; var version = reader.version;
int m_TexEnvsSize = reader.ReadInt32(); int m_TexEnvsSize = reader.ReadInt32();
m_TexEnvs = new Dictionary<string, UnityTexEnv>(m_TexEnvsSize); m_TexEnvs = new KeyValuePair<string, UnityTexEnv>[m_TexEnvsSize];
for (int i = 0; i < m_TexEnvsSize; i++) for (int i = 0; i < m_TexEnvsSize; i++)
{ {
m_TexEnvs.Add(reader.ReadAlignedString(), new UnityTexEnv(reader)); m_TexEnvs[i] = new KeyValuePair<string, UnityTexEnv>(reader.ReadAlignedString(), new UnityTexEnv(reader));
} }
if (version[0] >= 2021) //2021.1 and up if (version[0] >= 2021) //2021.1 and up
{ {
int m_IntsSize = reader.ReadInt32(); int m_IntsSize = reader.ReadInt32();
m_Ints = new Dictionary<string, int>(m_IntsSize); m_Ints = new KeyValuePair<string, int>[m_IntsSize];
for (int i = 0; i < m_IntsSize; i++) for (int i = 0; i < m_IntsSize; i++)
{ {
m_Ints.Add(reader.ReadAlignedString(), reader.ReadInt32()); m_Ints[i] = new KeyValuePair<string, int>(reader.ReadAlignedString(), reader.ReadInt32());
} }
} }
int m_FloatsSize = reader.ReadInt32(); int m_FloatsSize = reader.ReadInt32();
m_Floats = new Dictionary<string, float>(m_FloatsSize); m_Floats = new KeyValuePair<string, float>[m_FloatsSize];
for (int i = 0; i < m_FloatsSize; i++) for (int i = 0; i < m_FloatsSize; i++)
{ {
m_Floats.Add(reader.ReadAlignedString(), reader.ReadSingle()); m_Floats[i] = new KeyValuePair<string, float>(reader.ReadAlignedString(), reader.ReadSingle());
} }
int m_ColorsSize = reader.ReadInt32(); int m_ColorsSize = reader.ReadInt32();
m_Colors = new Dictionary<string, Color>(m_ColorsSize); m_Colors = new KeyValuePair<string, Color>[m_ColorsSize];
for (int i = 0; i < m_ColorsSize; i++) for (int i = 0; i < m_ColorsSize; i++)
{ {
m_Colors.Add(reader.ReadAlignedString(), reader.ReadColor4()); m_Colors[i] = new KeyValuePair<string, Color>(reader.ReadAlignedString(), reader.ReadColor4());
} }
} }
public string FindPropertyNameByCRC28(uint crc)
{
foreach (var property in m_TexEnvs.Keys)
{
string hdrName = property + HDRPostfixName;
if (CRC.Verify28DigestUTF8(hdrName, crc))
{
return hdrName;
}
string stName = property + STPostfixName;
if (CRC.Verify28DigestUTF8(stName, crc))
{
return stName;
}
string texelName = property + TexelSizePostfixName;
if (CRC.Verify28DigestUTF8(texelName, crc))
{
return texelName;
}
}
foreach (var property in m_Floats.Keys)
{
if (CRC.Verify28DigestUTF8(property, crc))
{
return property;
}
}
foreach (var property in m_Colors.Keys)
{
if (CRC.Verify28DigestUTF8(property, crc))
{
return property;
}
}
return null;
}
} }
public sealed class Material : NamedObject public sealed class Material : NamedObject
{ {
public PPtr<Shader> m_Shader; public PPtr<Shader> m_Shader;
public UnityPropertySheet m_SavedProperties; public UnityPropertySheet m_SavedProperties;
public Dictionary<string, string> m_StringTagMap;
public Material(ObjectReader reader) : base(reader) public Material(ObjectReader reader) : base(reader)
{ {
@@ -119,16 +74,25 @@ namespace AssetStudio
var m_ShaderKeywords = reader.ReadStringArray(); var m_ShaderKeywords = reader.ReadStringArray();
} }
if (version[0] >= 5) //5.0 and up if (version[0] > 2021 || (version[0] == 2021 && version[1] >= 3)) //2021.3 and up
{
var m_ValidKeywords = reader.ReadStringArray();
var m_InvalidKeywords = reader.ReadStringArray();
}
else if (version[0] >= 5) //5.0 ~ 2021.2
{ {
var m_ShaderKeywords = reader.ReadAlignedString(); var m_ShaderKeywords = reader.ReadAlignedString();
}
if (version[0] >= 5) //5.0 and up
{
var m_LightmapFlags = reader.ReadUInt32(); var m_LightmapFlags = reader.ReadUInt32();
} }
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up
{ {
var m_EnableInstancingVariants = reader.ReadBoolean(); var m_EnableInstancingVariants = reader.ReadBoolean();
var m_DoubleSidedGI = reader.ReadBoolean(); //2017 and up //var m_DoubleSidedGI = a_Stream.ReadBoolean(); //2017 and up
reader.AlignStream(); reader.AlignStream();
} }
@@ -140,15 +104,18 @@ namespace AssetStudio
if (version[0] > 5 || (version[0] == 5 && version[1] >= 1)) //5.1 and up if (version[0] > 5 || (version[0] == 5 && version[1] >= 1)) //5.1 and up
{ {
var stringTagMapSize = reader.ReadInt32(); var stringTagMapSize = reader.ReadInt32();
m_StringTagMap = new Dictionary<string, string>(stringTagMapSize);
for (int i = 0; i < stringTagMapSize; i++) for (int i = 0; i < stringTagMapSize; i++)
{ {
var first = reader.ReadAlignedString(); var first = reader.ReadAlignedString();
var second = reader.ReadAlignedString(); var second = reader.ReadAlignedString();
m_StringTagMap.Add(first, second);
} }
} }
if (reader.Game.Type.IsNaraka())
{
var value = reader.ReadInt32();
}
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up
{ {
var disabledShaderPasses = reader.ReadStringArray(); var disabledShaderPasses = reader.ReadStringArray();
@@ -158,10 +125,5 @@ namespace AssetStudio
//vector m_BuildTextureStacks 2020 and up //vector m_BuildTextureStacks 2020 and up
} }
public string FindPropertyNameByCRC28(uint crc)
{
return m_SavedProperties.FindPropertyNameByCRC28(crc);
}
} }
} }

View File

@@ -1,12 +1,9 @@
using SevenZip; using System;
using System; using System.Buffers.Binary;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using System.Xml.Linq;
namespace AssetStudio namespace AssetStudio
{ {
@@ -15,7 +12,7 @@ namespace AssetStudio
public Vector3 m_Min; public Vector3 m_Min;
public Vector3 m_Max; public Vector3 m_Max;
public MinMaxAABB(BinaryReader reader) public MinMaxAABB(EndianBinaryReader reader)
{ {
m_Min = reader.ReadVector3(); m_Min = reader.ReadVector3();
m_Max = reader.ReadVector3(); m_Max = reader.ReadVector3();
@@ -300,7 +297,6 @@ namespace AssetStudio
public class MeshBlendShape public class MeshBlendShape
{ {
public string name;
public uint firstVertex; public uint firstVertex;
public uint vertexCount; public uint vertexCount;
public bool hasNormals; public bool hasNormals;
@@ -312,7 +308,7 @@ namespace AssetStudio
if (version[0] == 4 && version[1] < 3) //4.3 down if (version[0] == 4 && version[1] < 3) //4.3 down
{ {
name = reader.ReadAlignedString(); var name = reader.ReadAlignedString();
} }
firstVertex = reader.ReadUInt32(); firstVertex = reader.ReadUInt32();
vertexCount = reader.ReadUInt32(); vertexCount = reader.ReadUInt32();
@@ -328,11 +324,6 @@ namespace AssetStudio
reader.AlignStream(); reader.AlignStream();
} }
} }
public bool IsCRCMatch(uint digest)
{
return CRC.VerifyDigestUTF8(name, digest);
}
} }
public class MeshBlendShapeChannel public class MeshBlendShapeChannel
@@ -404,20 +395,9 @@ namespace AssetStudio
} }
} }
} }
public string FindShapeNameByCRC(uint crc)
{
foreach (var blendChannel in channels)
{
if (blendChannel.nameHash == crc)
{
return blendChannel.name;
}
}
return null;
}
} }
public enum GfxPrimitiveType : int public enum GfxPrimitiveType
{ {
Triangles = 0, Triangles = 0,
TriangleStrip = 1, TriangleStrip = 1,
@@ -560,7 +540,7 @@ namespace AssetStudio
var m_StreamCompression = reader.ReadByte(); var m_StreamCompression = reader.ReadByte();
} }
var m_IsReadable = reader.ReadBoolean(); var m_IsReadable = reader.ReadBoolean();
if (reader.Game.Name == "BH3") if (reader.Game.Type.IsBH3())
{ {
var m_IsHighPrecisionPosition = reader.ReadBoolean(); var m_IsHighPrecisionPosition = reader.ReadBoolean();
var m_IsHighPrecisionTangent = reader.ReadBoolean(); var m_IsHighPrecisionTangent = reader.ReadBoolean();
@@ -568,9 +548,9 @@ namespace AssetStudio
} }
var m_KeepVertices = reader.ReadBoolean(); var m_KeepVertices = reader.ReadBoolean();
var m_KeepIndices = reader.ReadBoolean(); var m_KeepIndices = reader.ReadBoolean();
reader.AlignStream();
} }
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3") reader.AlignStream();
if (reader.Game.Type.IsGISubGroup())
{ {
var m_PackSkinDataToUV2UV3 = reader.ReadBoolean(); var m_PackSkinDataToUV2UV3 = reader.ReadBoolean();
reader.AlignStream(); reader.AlignStream();
@@ -665,7 +645,7 @@ namespace AssetStudio
m_CompressedMesh = new CompressedMesh(reader); m_CompressedMesh = new CompressedMesh(reader);
} }
var m_LocalAABB = new AABB(reader); reader.Position += 24; //AABB m_LocalAABB
if (version[0] < 3 || (version[0] == 3 && version[1] <= 4)) //3.4.2 and earlier if (version[0] < 3 || (version[0] == 3 && version[1] <= 4)) //3.4.2 and earlier
{ {
@@ -683,19 +663,24 @@ namespace AssetStudio
int m_MeshUsageFlags = reader.ReadInt32(); int m_MeshUsageFlags = reader.ReadInt32();
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 1)) //2022.1 and up
{
int m_CookingOptions = reader.ReadInt32();
}
if (version[0] >= 5) //5.0 and up if (version[0] >= 5) //5.0 and up
{ {
var m_BakedConvexCollisionMesh = reader.ReadUInt8Array(); var m_BakedConvexCollisionMesh = reader.ReadUInt8Array();
reader.AlignStream(); reader.AlignStream();
var m_BakedTriangleCollisionMesh = reader.ReadUInt8Array(); var m_BakedTriangleCollisionMesh = reader.ReadUInt8Array();
reader.AlignStream(); reader.AlignStream();
if (reader.Game.Name == "BH3") if (reader.Game.Type.IsBH3())
{ {
var m_MeshOptimized = reader.ReadBoolean(); var m_MeshOptimized = reader.ReadBoolean();
} }
} }
if (reader.Game.Name == "ZZZ_CB1") if (reader.Game.Type.IsZZZCB1())
{ {
var m_CloseMeshDynamicCompression = reader.ReadBoolean(); var m_CloseMeshDynamicCompression = reader.ReadBoolean();
reader.AlignStream(); reader.AlignStream();
@@ -705,21 +690,20 @@ namespace AssetStudio
var m_CompressLevelTexCoordinates = reader.ReadInt32(); var m_CompressLevelTexCoordinates = reader.ReadInt32();
} }
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3" if (reader.Game.Type.IsGIGroup() || version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
|| version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
{ {
var m_MeshMetrics = new float[2]; var m_MeshMetrics = new float[2];
m_MeshMetrics[0] = reader.ReadSingle(); m_MeshMetrics[0] = reader.ReadSingle();
m_MeshMetrics[1] = reader.ReadSingle(); m_MeshMetrics[1] = reader.ReadSingle();
} }
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3") if (reader.Game.Type.IsGIGroup())
{ {
var m_MetricsDirty = reader.ReadBoolean(); var m_MetricsDirty = reader.ReadBoolean();
reader.AlignStream(); reader.AlignStream();
var m_CloseMeshDynamicCompression = reader.ReadBoolean(); var m_CloseMeshDynamicCompression = reader.ReadBoolean();
reader.AlignStream(); reader.AlignStream();
if (reader.Game.Name != "GI_CB1") if (!reader.Game.Type.IsGICB1() && !reader.Game.Type.IsGIPack())
{ {
var m_IsStreamingMesh = reader.ReadBoolean(); var m_IsStreamingMesh = reader.ReadBoolean();
reader.AlignStream(); reader.AlignStream();
@@ -830,13 +814,9 @@ namespace AssetStudio
m_UV1 = componentsFloatArray; m_UV1 = componentsFloatArray;
break; break;
case 6: //kShaderChannelTexCoord2 case 6: //kShaderChannelTexCoord2
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3")
continue;
m_UV2 = componentsFloatArray; m_UV2 = componentsFloatArray;
break; break;
case 7: //kShaderChannelTexCoord3 case 7: //kShaderChannelTexCoord3
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3")
continue;
m_UV3 = componentsFloatArray; m_UV3 = componentsFloatArray;
break; break;
case 8: //kShaderChannelTexCoord4 case 8: //kShaderChannelTexCoord4
@@ -900,8 +880,6 @@ namespace AssetStudio
m_UV1 = componentsFloatArray; m_UV1 = componentsFloatArray;
break; break;
case 5: case 5:
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3")
continue;
if (version[0] >= 5) //kShaderChannelTexCoord2 if (version[0] >= 5) //kShaderChannelTexCoord2
{ {
m_UV2 = componentsFloatArray; m_UV2 = componentsFloatArray;
@@ -912,8 +890,6 @@ namespace AssetStudio
} }
break; break;
case 6: //kShaderChannelTexCoord3 case 6: //kShaderChannelTexCoord3
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3")
continue;
m_UV3 = componentsFloatArray; m_UV3 = componentsFloatArray;
break; break;
case 7: //kShaderChannelTangent case 7: //kShaderChannelTangent
@@ -1255,25 +1231,6 @@ namespace AssetStudio
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
} }
public string FindBlendShapeNameByCRC(uint crc)
{
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3))
{
return m_Shapes.FindShapeNameByCRC(crc);
}
else
{
foreach (var blendShape in m_Shapes.shapes)
{
if (blendShape.IsCRCMatch(crc))
{
return blendShape.name;
}
}
return null;
}
}
} }
public static class MeshHelper public static class MeshHelper
@@ -1419,7 +1376,7 @@ namespace AssetStudio
switch (format) switch (format)
{ {
case VertexFormat.Float: case VertexFormat.Float:
result[i] = BitConverter.ToSingle(inputBytes, i * 4); result[i] = BinaryPrimitives.ReadSingleLittleEndian(inputBytes.AsSpan(i * 4));
break; break;
case VertexFormat.Float16: case VertexFormat.Float16:
result[i] = Half.ToHalf(inputBytes, i * 2); result[i] = Half.ToHalf(inputBytes, i * 2);
@@ -1431,10 +1388,10 @@ namespace AssetStudio
result[i] = Math.Max((sbyte)inputBytes[i] / 127f, -1f); result[i] = Math.Max((sbyte)inputBytes[i] / 127f, -1f);
break; break;
case VertexFormat.UNorm16: case VertexFormat.UNorm16:
result[i] = BitConverter.ToUInt16(inputBytes, i * 2) / 65535f; result[i] = BinaryPrimitives.ReadUInt16LittleEndian(inputBytes.AsSpan(i * 2)) / 65535f;
break; break;
case VertexFormat.SNorm16: case VertexFormat.SNorm16:
result[i] = Math.Max(BitConverter.ToInt16(inputBytes, i * 2) / 32767f, -1f); result[i] = Math.Max(BinaryPrimitives.ReadInt16LittleEndian(inputBytes.AsSpan(i * 2)) / 32767f, -1f);
break; break;
} }
} }
@@ -1456,11 +1413,11 @@ namespace AssetStudio
break; break;
case VertexFormat.UInt16: case VertexFormat.UInt16:
case VertexFormat.SInt16: case VertexFormat.SInt16:
result[i] = BitConverter.ToInt16(inputBytes, i * 2); result[i] = BinaryPrimitives.ReadInt16LittleEndian(inputBytes.AsSpan(i * 2));
break; break;
case VertexFormat.UInt32: case VertexFormat.UInt32:
case VertexFormat.SInt32: case VertexFormat.SInt32:
result[i] = BitConverter.ToInt32(inputBytes, i * 4); result[i] = BinaryPrimitives.ReadInt32LittleEndian(inputBytes.AsSpan(i * 4));
break; break;
} }
} }

View File

@@ -7,9 +7,10 @@ namespace AssetStudio
{ {
public sealed class MeshRenderer : Renderer public sealed class MeshRenderer : Renderer
{ {
public PPtr<Mesh> m_AdditionalVertexStreams;
public MeshRenderer(ObjectReader reader) : base(reader) public MeshRenderer(ObjectReader reader) : base(reader)
{ {
var m_AdditionalVertexStreams = new PPtr<Mesh>(reader); m_AdditionalVertexStreams = new PPtr<Mesh>(reader);
} }
} }
} }

View File

@@ -1,8 +1,8 @@
using System; using System;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text.RegularExpressions;
namespace AssetStudio namespace AssetStudio
{ {
@@ -12,17 +12,57 @@ namespace AssetStudio
Bytes, Bytes,
JSON JSON
} }
public sealed class MiHoYoBinData : Object public sealed partial class MiHoYoBinData : Object
{ {
public static bool doXOR; public static bool Exportable;
public static bool Encrypted;
public static byte Key; public static byte Key;
public byte[] RawData; public byte[] RawData;
public byte[] Data public MiHoYoBinData(ObjectReader reader) : base(reader)
{
var length = reader.ReadInt32();
RawData = reader.ReadBytes(length);
}
public string AsString => Type switch
{
MiHoYoBinDataType.JSON => JToken.Parse(DataStr).ToString(Formatting.Indented),
MiHoYoBinDataType.Bytes => Chars().Replace(DataStr, string.Empty),
_ => "",
};
public new object Dump() => Type switch
{
MiHoYoBinDataType.JSON => AsString,
MiHoYoBinDataType.Bytes => Data,
_ => null,
};
private string DataStr => Encoding.UTF8.GetString(Data);
public MiHoYoBinDataType Type
{ {
get get
{ {
if (doXOR) try
{
var asToken = JToken.Parse(DataStr);
if (asToken.Type == JTokenType.Object || asToken.Type == JTokenType.Array)
return MiHoYoBinDataType.JSON;
}
catch (Exception)
{
return MiHoYoBinDataType.Bytes;
}
return MiHoYoBinDataType.None;
}
}
private byte[] Data
{
get
{
if (Encrypted)
{ {
byte[] bytes = new byte[RawData.Length]; byte[] bytes = new byte[RawData.Length];
for (int i = 0; i < RawData.Length; i++) for (int i = 0; i < RawData.Length; i++)
@@ -35,59 +75,7 @@ namespace AssetStudio
} }
} }
public string Str [GeneratedRegex("[^\\u0020-\\u007E]")]
{ private static partial Regex Chars();
get
{
var str = Encoding.UTF8.GetString(Data);
switch (Type)
{
case MiHoYoBinDataType.JSON:
return JToken.Parse(str).ToString(Formatting.Indented);
case MiHoYoBinDataType.Bytes:
return Regex.Replace(str, @"[^\u0020-\u007E]", string.Empty);
default:
return "";
}
}
}
public MiHoYoBinDataType Type
{
get
{
try
{
var str = Encoding.UTF8.GetString(Data);
var asToken = JToken.Parse(str);
if (asToken.Type == JTokenType.Object || asToken.Type == JTokenType.Array)
return MiHoYoBinDataType.JSON;
}
catch (Exception)
{
return MiHoYoBinDataType.Bytes;
}
return MiHoYoBinDataType.None;
}
}
public MiHoYoBinData(ObjectReader reader) : base(reader)
{
var length = reader.ReadInt32();
RawData = reader.ReadBytes(length);
}
public new dynamic Dump()
{
switch (Type)
{
case MiHoYoBinDataType.JSON:
return Str;
case MiHoYoBinDataType.Bytes:
return Data;
default:
return null;
}
}
} }
} }

View File

@@ -1,9 +1,11 @@
using System; using System;
using System.IO;
using System.Collections.Generic;
using static AssetStudio.AssetsHelper;
namespace AssetStudio namespace AssetStudio
{ {
public sealed class PPtr<T> : IYAMLExportable public sealed class PPtr<T> where T : Object
where T : Object
{ {
public int m_FileID; public int m_FileID;
public long m_PathID; public long m_PathID;
@@ -18,21 +20,6 @@ namespace AssetStudio
assetsFile = reader.assetsFile; assetsFile = reader.assetsFile;
} }
public PPtr(int fileID, long pathID, SerializedFile assetsFile)
{
m_FileID = fileID;
m_PathID = pathID;
this.assetsFile = assetsFile;
}
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Style = MappingStyle.Flow;
node.Add("fileID", m_FileID);
return node;
}
private bool TryGetAssetsFile(out SerializedFile result) private bool TryGetAssetsFile(out SerializedFile result)
{ {
result = null; result = null;
@@ -142,11 +129,6 @@ namespace AssetStudio
m_PathID = m_Object.m_PathID; m_PathID = m_Object.m_PathID;
} }
public PPtr<T2> CastTo<T2>() where T2 : Object
{
return new PPtr<T2>(m_FileID, m_PathID, assetsFile);
}
public bool IsNull => m_PathID == 0 || m_FileID < 0; public bool IsNull => m_PathID == 0 || m_FileID < 0;
} }
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -20,11 +19,13 @@ namespace AssetStudio
public abstract class Renderer : Component public abstract class Renderer : Component
{ {
public static bool Parsable; public static bool Skipped;
public PPtr<Material>[] m_Materials; public PPtr<Material>[] m_Materials;
public StaticBatchInfo m_StaticBatchInfo; public StaticBatchInfo m_StaticBatchInfo;
public uint[] m_SubsetIndices; public uint[] m_SubsetIndices;
private bool isNewHeader = false; private bool isNewHeader = false;
protected Renderer(ObjectReader reader) : base(reader) protected Renderer(ObjectReader reader) : base(reader)
{ {
if (version[0] < 5) //5.0 down if (version[0] < 5) //5.0 down
@@ -38,7 +39,7 @@ namespace AssetStudio
{ {
if (version[0] > 5 || (version[0] == 5 && version[1] >= 4)) //5.4 and up if (version[0] > 5 || (version[0] == 5 && version[1] >= 4)) //5.4 and up
{ {
if (reader.Game.Name == "GI") if (reader.Game.Type.IsGI())
{ {
CheckHeader(reader); CheckHeader(reader);
} }
@@ -48,39 +49,44 @@ namespace AssetStudio
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
{ {
var m_DynamicOccludee = reader.ReadByte(); var m_DynamicOccludee = reader.ReadByte();
if (reader.Game.Name == "BH3") }
if (reader.Game.Type.IsBH3())
{
var m_AllowHalfResolution = reader.ReadByte();
}
if (reader.Game.Type.IsGIGroup())
{
var m_ReceiveDecals = reader.ReadByte();
var m_EnableShadowCulling = reader.ReadByte();
var m_EnableGpuQuery = reader.ReadByte();
var m_AllowHalfResolution = reader.ReadByte();
if (!reader.Game.Type.IsGICB1())
{ {
var m_AllowHalfResolution = reader.ReadByte(); if (reader.Game.Type.IsGI())
}
else if (reader.Game.Name == "GI_CB3")
{
var m_ReceiveDecals = reader.ReadByte();
var m_EnableShadowCulling = reader.ReadByte();
var m_EnableGpuQuery = reader.ReadByte();
var m_AllowHalfResolution = reader.ReadByte();
var m_IsRainOccluder = reader.ReadByte();
var m_IsDynamicAOOccluder = reader.ReadByte();
var m_IsDynamic = reader.ReadByte();
}
else if (reader.Game.Name == "GI")
{
var m_ReceiveDecals = reader.ReadByte();
var m_EnableShadowCulling = reader.ReadByte();
var m_EnableGpuQuery = reader.ReadByte();
var m_AllowHalfResolution = reader.ReadByte();
var m_IsRainOccluder = reader.ReadByte();
var m_IsDynamicAOOccluder = reader.ReadByte();
var m_IsCloudObject = reader.ReadByte();
var m_IsInteriorVolume = reader.ReadByte();
var m_IsDynamic = reader.ReadByte();
var m_UseTessellation = reader.ReadByte();
var m_IsTerrainTessInfo = reader.ReadByte();
if (isNewHeader)
{ {
var m_UseVertexLightInForward = reader.ReadByte(); var m_AllowPerMaterialProp = isNewHeader ? reader.ReadByte() : 0;
var m_CombineSubMeshInGeoPass = reader.ReadByte(); }
var m_AllowPerMaterialProp = reader.ReadByte(); var m_IsRainOccluder = reader.ReadByte();
var m_IsHQDynamicAOOccluder = reader.ReadByte(); if (!reader.Game.Type.IsGICB2())
{
var m_IsDynamicAOOccluder = reader.ReadByte();
if (reader.Game.Type.IsGI())
{
var m_IsHQDynamicAOOccluder = reader.ReadByte();
var m_IsCloudObject = reader.ReadByte();
var m_IsInteriorVolume = reader.ReadByte();
}
}
if (!reader.Game.Type.IsGIPack())
{
var m_IsDynamic = reader.ReadByte();
}
if (reader.Game.Type.IsGI())
{
var m_UseTessellation = reader.ReadByte();
var m_IsTerrainTessInfo = isNewHeader ? reader.ReadByte() : 0;
var m_UseVertexLightInForward = isNewHeader ? reader.ReadByte() : 0;
var m_CombineSubMeshInGeoPass = isNewHeader ? reader.ReadByte() : 0;
} }
} }
} }
@@ -88,22 +94,6 @@ namespace AssetStudio
{ {
var m_StaticShadowCaster = reader.ReadByte(); var m_StaticShadowCaster = reader.ReadByte();
} }
if (reader.Game.Name == "GI_CB2")
{
var m_ReceiveDecals = reader.ReadByte();
var m_EnableShadowCulling = reader.ReadByte();
var m_EnableGpuQuery = reader.ReadByte();
var m_AllowHalfResolution = reader.ReadByte();
var m_IsRainOccluder = reader.ReadByte();
var m_IsDynamic = reader.ReadByte();
}
if (reader.Game.Name == "GI_CB1")
{
var m_ReceiveDecals = reader.ReadByte();
var m_EnableShadowCulling = reader.ReadByte();
var m_EnableGpuQuery = reader.ReadByte();
var m_AllowHalfResolution = reader.ReadByte();
}
var m_MotionVectors = reader.ReadByte(); var m_MotionVectors = reader.ReadByte();
var m_LightProbeUsage = reader.ReadByte(); var m_LightProbeUsage = reader.ReadByte();
var m_ReflectionProbeUsage = reader.ReadByte(); var m_ReflectionProbeUsage = reader.ReadByte();
@@ -115,7 +105,7 @@ namespace AssetStudio
{ {
var m_RayTraceProcedural = reader.ReadByte(); var m_RayTraceProcedural = reader.ReadByte();
} }
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB3") if (reader.Game.Type.IsGI() || reader.Game.Type.IsGICB3() || reader.Game.Type.IsGICB3Pre())
{ {
var m_MeshShowQuality = reader.ReadByte(); var m_MeshShowQuality = reader.ReadByte();
} }
@@ -140,12 +130,11 @@ namespace AssetStudio
var m_RendererPriority = reader.ReadInt32(); var m_RendererPriority = reader.ReadInt32();
} }
var m_LightmapIndex = reader.ReadInt16(); var m_LightmapIndex = reader.ReadUInt16();
var m_LightmapIndexDynamic = reader.ReadInt16(); var m_LightmapIndexDynamic = reader.ReadUInt16();
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3") if (reader.Game.Type.IsGIGroup() && (m_LightmapIndex != 0xFFFF || m_LightmapIndexDynamic != 0xFFFF))
{ {
if (m_LightmapIndex != -1 || m_LightmapIndexDynamic != -1) throw new Exception("Not Supported !! skipping....");
throw new Exception("Not Supported !! skipping....");
} }
} }
@@ -159,7 +148,7 @@ namespace AssetStudio
var m_LightmapTilingOffsetDynamic = reader.ReadVector4(); var m_LightmapTilingOffsetDynamic = reader.ReadVector4();
} }
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3") if (reader.Game.Type.IsGIGroup())
{ {
var m_ViewDistanceRatio = reader.ReadSingle(); var m_ViewDistanceRatio = reader.ReadSingle();
var m_ShaderLODDistanceRatio = reader.ReadSingle(); var m_ShaderLODDistanceRatio = reader.ReadSingle();
@@ -188,7 +177,8 @@ namespace AssetStudio
var m_StaticBatchRoot = new PPtr<Transform>(reader); var m_StaticBatchRoot = new PPtr<Transform>(reader);
} }
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3")
if (reader.Game.Type.IsGIGroup())
{ {
var m_MatLayers = reader.ReadInt32(); var m_MatLayers = reader.ReadInt32();
} }
@@ -219,21 +209,20 @@ namespace AssetStudio
} }
else else
{ {
var m_SortingLayerID = reader.ReadInt32(); var m_SortingLayerID = reader.ReadUInt32();
var m_SortingLayer = reader.ReadInt16();
} }
//SInt16 m_SortingLayer 5.6 and up //SInt16 m_SortingLayer 5.6 and up
var m_SortingOrder = reader.ReadInt16(); var m_SortingOrder = reader.ReadInt16();
reader.AlignStream(); reader.AlignStream();
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3" || reader.Game.Name == "BH3") if (reader.Game.Type.IsGIGroup() || reader.Game.Type.IsBH3())
{ {
var m_UseHighestMip = reader.ReadBoolean(); var m_UseHighestMip = reader.ReadBoolean();
reader.AlignStream(); reader.AlignStream();
} }
if (reader.Game.Name == "SR_CB3") if (reader.Game.Type.IsSRCB3())
{ {
var _RenderFlag = reader.ReadUInt32(); var RenderFlag = reader.ReadUInt32();
reader.AlignStream(); reader.AlignStream();
} }
} }
@@ -241,31 +230,14 @@ namespace AssetStudio
private void CheckHeader(ObjectReader reader) private void CheckHeader(ObjectReader reader)
{ {
short index = 0; short value = 0;
var pos = reader.Position; var pos = reader.Position;
while (index != -1 && reader.Position <= pos + 0x1A) while (value != -1 && reader.Position <= pos + 0x1A)
index = reader.ReadInt16(); {
value = reader.ReadInt16();
}
isNewHeader = (reader.Position - pos) == 0x1A; isNewHeader = (reader.Position - pos) == 0x1A;
reader.Position = pos; reader.Position = pos;
} }
public string FindMaterialPropertyNameByCRC28(uint crc)
{
foreach (PPtr<Material> materialPtr in m_Materials)
{
if (!materialPtr.TryGet(out var material))
{
continue;
}
string property = material.FindPropertyNameByCRC28(crc);
if (property == null)
{
continue;
}
return property;
}
return null;
}
} }
} }

View File

@@ -11,7 +11,5 @@ namespace AssetStudio
{ {
} }
public abstract bool IsContainsAnimationClip(AnimationClip clip);
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -9,7 +10,7 @@ namespace AssetStudio
{ {
public byte[] bytes; public byte[] bytes;
public Hash128(BinaryReader reader) public Hash128(EndianBinaryReader reader)
{ {
bytes = reader.ReadBytes(16); bytes = reader.ReadBytes(16);
} }
@@ -20,7 +21,7 @@ namespace AssetStudio
public MatrixParameter[] m_MatrixParams; public MatrixParameter[] m_MatrixParams;
public VectorParameter[] m_VectorParams; public VectorParameter[] m_VectorParams;
public StructParameter(BinaryReader reader) public StructParameter(EndianBinaryReader reader)
{ {
var m_NameIndex = reader.ReadInt32(); var m_NameIndex = reader.ReadInt32();
var m_Index = reader.ReadInt32(); var m_Index = reader.ReadInt32();
@@ -48,7 +49,7 @@ namespace AssetStudio
public uint sampler; public uint sampler;
public int bindPoint; public int bindPoint;
public SamplerParameter(BinaryReader reader) public SamplerParameter(EndianBinaryReader reader)
{ {
sampler = reader.ReadUInt32(); sampler = reader.ReadUInt32();
bindPoint = reader.ReadInt32(); bindPoint = reader.ReadInt32();
@@ -71,7 +72,7 @@ namespace AssetStudio
public string m_DefaultName; public string m_DefaultName;
public TextureDimension m_TexDim; public TextureDimension m_TexDim;
public SerializedTextureProperty(BinaryReader reader) public SerializedTextureProperty(EndianBinaryReader reader)
{ {
m_DefaultName = reader.ReadAlignedString(); m_DefaultName = reader.ReadAlignedString();
m_TexDim = (TextureDimension)reader.ReadInt32(); m_TexDim = (TextureDimension)reader.ReadInt32();
@@ -98,7 +99,7 @@ namespace AssetStudio
public float[] m_DefValue; public float[] m_DefValue;
public SerializedTextureProperty m_DefTexture; public SerializedTextureProperty m_DefTexture;
public SerializedProperty(BinaryReader reader) public SerializedProperty(EndianBinaryReader reader)
{ {
m_Name = reader.ReadAlignedString(); m_Name = reader.ReadAlignedString();
m_Description = reader.ReadAlignedString(); m_Description = reader.ReadAlignedString();
@@ -114,7 +115,7 @@ namespace AssetStudio
{ {
public SerializedProperty[] m_Props; public SerializedProperty[] m_Props;
public SerializedProperties(BinaryReader reader) public SerializedProperties(EndianBinaryReader reader)
{ {
int numProps = reader.ReadInt32(); int numProps = reader.ReadInt32();
m_Props = new SerializedProperty[numProps]; m_Props = new SerializedProperty[numProps];
@@ -130,7 +131,7 @@ namespace AssetStudio
public float val; public float val;
public string name; public string name;
public SerializedShaderFloatValue(BinaryReader reader) public SerializedShaderFloatValue(EndianBinaryReader reader)
{ {
val = reader.ReadSingle(); val = reader.ReadSingle();
name = reader.ReadAlignedString(); name = reader.ReadAlignedString();
@@ -147,7 +148,7 @@ namespace AssetStudio
public SerializedShaderFloatValue blendOpAlpha; public SerializedShaderFloatValue blendOpAlpha;
public SerializedShaderFloatValue colMask; public SerializedShaderFloatValue colMask;
public SerializedShaderRTBlendState(BinaryReader reader) public SerializedShaderRTBlendState(EndianBinaryReader reader)
{ {
srcBlend = new SerializedShaderFloatValue(reader); srcBlend = new SerializedShaderFloatValue(reader);
destBlend = new SerializedShaderFloatValue(reader); destBlend = new SerializedShaderFloatValue(reader);
@@ -166,7 +167,7 @@ namespace AssetStudio
public SerializedShaderFloatValue zFail; public SerializedShaderFloatValue zFail;
public SerializedShaderFloatValue comp; public SerializedShaderFloatValue comp;
public SerializedStencilOp(BinaryReader reader) public SerializedStencilOp(EndianBinaryReader reader)
{ {
pass = new SerializedShaderFloatValue(reader); pass = new SerializedShaderFloatValue(reader);
fail = new SerializedShaderFloatValue(reader); fail = new SerializedShaderFloatValue(reader);
@@ -183,7 +184,7 @@ namespace AssetStudio
public SerializedShaderFloatValue w; public SerializedShaderFloatValue w;
public string name; public string name;
public SerializedShaderVectorValue(BinaryReader reader) public SerializedShaderVectorValue(EndianBinaryReader reader)
{ {
x = new SerializedShaderFloatValue(reader); x = new SerializedShaderFloatValue(reader);
y = new SerializedShaderFloatValue(reader); y = new SerializedShaderFloatValue(reader);
@@ -281,7 +282,7 @@ namespace AssetStudio
public sbyte source; public sbyte source;
public sbyte target; public sbyte target;
public ShaderBindChannel(BinaryReader reader) public ShaderBindChannel(EndianBinaryReader reader)
{ {
source = reader.ReadSByte(); source = reader.ReadSByte();
target = reader.ReadSByte(); target = reader.ReadSByte();
@@ -293,7 +294,7 @@ namespace AssetStudio
public ShaderBindChannel[] m_Channels; public ShaderBindChannel[] m_Channels;
public uint m_SourceMap; public uint m_SourceMap;
public ParserBindChannels(BinaryReader reader) public ParserBindChannels(EndianBinaryReader reader)
{ {
int numChannels = reader.ReadInt32(); int numChannels = reader.ReadInt32();
m_Channels = new ShaderBindChannel[numChannels]; m_Channels = new ShaderBindChannel[numChannels];
@@ -315,7 +316,7 @@ namespace AssetStudio
public sbyte m_Type; public sbyte m_Type;
public sbyte m_Dim; public sbyte m_Dim;
public VectorParameter(BinaryReader reader) public VectorParameter(EndianBinaryReader reader)
{ {
m_NameIndex = reader.ReadInt32(); m_NameIndex = reader.ReadInt32();
m_Index = reader.ReadInt32(); m_Index = reader.ReadInt32();
@@ -334,7 +335,7 @@ namespace AssetStudio
public sbyte m_Type; public sbyte m_Type;
public sbyte m_RowCount; public sbyte m_RowCount;
public MatrixParameter(BinaryReader reader) public MatrixParameter(EndianBinaryReader reader)
{ {
m_NameIndex = reader.ReadInt32(); m_NameIndex = reader.ReadInt32();
m_Index = reader.ReadInt32(); m_Index = reader.ReadInt32();
@@ -428,6 +429,7 @@ namespace AssetStudio
if ((version[0] == 2020 && version[1] > 3) || if ((version[0] == 2020 && version[1] > 3) ||
(version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up (version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up
(version[0] > 2021) ||
(version[0] == 2021 && version[1] > 1) || (version[0] == 2021 && version[1] > 1) ||
(version[0] == 2021 && version[1] == 1 && version[2] >= 4)) //2021.1.4f1 and up (version[0] == 2021 && version[1] == 1 && version[2] >= 4)) //2021.1.4f1 and up
{ {
@@ -443,7 +445,7 @@ namespace AssetStudio
public int m_Index; public int m_Index;
public int m_OriginalIndex; public int m_OriginalIndex;
public UAVParameter(BinaryReader reader) public UAVParameter(EndianBinaryReader reader)
{ {
m_NameIndex = reader.ReadInt32(); m_NameIndex = reader.ReadInt32();
m_Index = reader.ReadInt32(); m_Index = reader.ReadInt32();
@@ -605,6 +607,7 @@ namespace AssetStudio
if ((version[0] == 2020 && version[1] > 3) || if ((version[0] == 2020 && version[1] > 3) ||
(version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up (version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up
(version[0] > 2021) ||
(version[0] == 2021 && version[1] > 1) || (version[0] == 2021 && version[1] > 1) ||
(version[0] == 2021 && version[1] == 1 && version[2] >= 1)) //2021.1.1f1 and up (version[0] == 2021 && version[1] == 1 && version[2] >= 1)) //2021.1.1f1 and up
{ {
@@ -690,6 +693,7 @@ namespace AssetStudio
{ {
public SerializedSubProgram[] m_SubPrograms; public SerializedSubProgram[] m_SubPrograms;
public SerializedProgramParameters m_CommonParameters; public SerializedProgramParameters m_CommonParameters;
public ushort[] m_SerializedKeywordStateMask;
public SerializedProgram(ObjectReader reader) public SerializedProgram(ObjectReader reader)
{ {
@@ -704,11 +708,18 @@ namespace AssetStudio
if ((version[0] == 2020 && version[1] > 3) || if ((version[0] == 2020 && version[1] > 3) ||
(version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up (version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up
(version[0] > 2021) ||
(version[0] == 2021 && version[1] > 1) || (version[0] == 2021 && version[1] > 1) ||
(version[0] == 2021 && version[1] == 1 && version[2] >= 1)) //2021.1.4f1 and up (version[0] == 2021 && version[1] == 1 && version[2] >= 1)) //2021.1.1f1 and up
{ {
m_CommonParameters = new SerializedProgramParameters(reader); m_CommonParameters = new SerializedProgramParameters(reader);
} }
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 1)) //2022.1 and up
{
m_SerializedKeywordStateMask = reader.ReadUInt16Array();
reader.AlignStream();
}
} }
} }
@@ -795,7 +806,7 @@ namespace AssetStudio
m_Name = reader.ReadAlignedString(); m_Name = reader.ReadAlignedString();
m_TextureName = reader.ReadAlignedString(); m_TextureName = reader.ReadAlignedString();
m_Tags = new SerializedTagMap(reader); m_Tags = new SerializedTagMap(reader);
if (version[0] > 2021 || (version[0] == 2021 && version[1] >= 2)) //2021.2 and up if (version[0] == 2021 && version[1] >= 2) //2021.2 ~2021.x
{ {
m_SerializedKeywordStateMask = reader.ReadUInt16Array(); m_SerializedKeywordStateMask = reader.ReadUInt16Array();
reader.AlignStream(); reader.AlignStream();
@@ -807,7 +818,7 @@ namespace AssetStudio
{ {
public KeyValuePair<string, string>[] tags; public KeyValuePair<string, string>[] tags;
public SerializedTagMap(BinaryReader reader) public SerializedTagMap(EndianBinaryReader reader)
{ {
int numTags = reader.ReadInt32(); int numTags = reader.ReadInt32();
tags = new KeyValuePair<string, string>[numTags]; tags = new KeyValuePair<string, string>[numTags];
@@ -843,7 +854,7 @@ namespace AssetStudio
public string from; public string from;
public string to; public string to;
public SerializedShaderDependency(BinaryReader reader) public SerializedShaderDependency(EndianBinaryReader reader)
{ {
from = reader.ReadAlignedString(); from = reader.ReadAlignedString();
to = reader.ReadAlignedString(); to = reader.ReadAlignedString();
@@ -855,7 +866,7 @@ namespace AssetStudio
public string customEditorName; public string customEditorName;
public string renderPipelineType; public string renderPipelineType;
public SerializedCustomEditorForRenderPipeline(BinaryReader reader) public SerializedCustomEditorForRenderPipeline(EndianBinaryReader reader)
{ {
customEditorName = reader.ReadAlignedString(); customEditorName = reader.ReadAlignedString();
renderPipelineType = reader.ReadAlignedString(); renderPipelineType = reader.ReadAlignedString();
@@ -953,7 +964,6 @@ namespace AssetStudio
public class Shader : NamedObject public class Shader : NamedObject
{ {
public static bool Parsable;
public byte[] m_Script; public byte[] m_Script;
//5.3 - 5.4 //5.3 - 5.4
public uint decompressedSize; public uint decompressedSize;
@@ -985,22 +995,22 @@ namespace AssetStudio
decompressedLengths = reader.ReadUInt32Array().Select(x => new[] { x }).ToArray(); decompressedLengths = reader.ReadUInt32Array().Select(x => new[] { x }).ToArray();
} }
compressedBlob = reader.ReadUInt8Array(); compressedBlob = reader.ReadUInt8Array();
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3") reader.AlignStream();
if (reader.Game.Type.IsGISubGroup())
{ {
if (BitConverter.ToInt32(compressedBlob, 0) == -1) if (BinaryPrimitives.ReadInt32LittleEndian(compressedBlob) == -1)
compressedBlob = reader.ReadUInt8Array(); {
compressedBlob = reader.ReadUInt8Array(); //blobDataBlocks
reader.AlignStream();
}
} }
else
{
reader.AlignStream();
}
var m_DependenciesCount = reader.ReadInt32(); var m_DependenciesCount = reader.ReadInt32();
for (int i = 0; i < m_DependenciesCount; i++) for (int i = 0; i < m_DependenciesCount; i++)
{ {
new PPtr<Shader>(reader); new PPtr<Shader>(reader);
} }
if (version[0] >= 2018) if (version[0] >= 2018)
{ {
var m_NonModifiableTexturesCount = reader.ReadInt32(); var m_NonModifiableTexturesCount = reader.ReadInt32();
@@ -1010,7 +1020,7 @@ namespace AssetStudio
new PPtr<Texture>(reader); new PPtr<Texture>(reader);
} }
} }
var m_ShaderIsBaked = reader.ReadBoolean(); var m_ShaderIsBaked = reader.ReadBoolean();
reader.AlignStream(); reader.AlignStream();
} }

View File

@@ -9,11 +9,12 @@ namespace AssetStudio
{ {
public PPtr<Mesh> m_Mesh; public PPtr<Mesh> m_Mesh;
public PPtr<Transform>[] m_Bones; public PPtr<Transform>[] m_Bones;
public PPtr<Transform> m_RootBone;
public float[] m_BlendShapeWeights; public float[] m_BlendShapeWeights;
public PPtr<Transform> m_RootBone;
public AABB m_AABB; public AABB m_AABB;
public bool m_DirtyAABB; public bool m_DirtyAABB;
public SkinnedMeshRenderer(ObjectReader reader) : base(reader) public SkinnedMeshRenderer(ObjectReader reader) : base(reader)
{ {
int m_Quality = reader.ReadInt32(); int m_Quality = reader.ReadInt32();
@@ -33,16 +34,19 @@ namespace AssetStudio
{ {
m_Bones[b] = new PPtr<Transform>(reader); m_Bones[b] = new PPtr<Transform>(reader);
} }
reader.AlignStream();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
{ {
m_BlendShapeWeights = reader.ReadSingleArray(); m_BlendShapeWeights = reader.ReadSingleArray();
} }
reader.AlignStream();
m_RootBone = new PPtr<Transform>(reader); if (reader.Game.Type.IsGIGroup())
m_AABB = new AABB(reader); {
m_DirtyAABB = reader.ReadBoolean(); m_RootBone = new PPtr<Transform>(reader);
reader.AlignStream(); m_AABB = new AABB(reader);
m_DirtyAABB = reader.ReadBoolean();
reader.AlignStream();
}
} }
} }
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.IO;
namespace AssetStudio namespace AssetStudio
{ {
@@ -134,9 +133,13 @@ namespace AssetStudio
ResourceReader resourceReader; ResourceReader resourceReader;
if (!string.IsNullOrEmpty(m_StreamData?.path)) if (!string.IsNullOrEmpty(m_StreamData?.path))
{
resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size); resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size);
}
else else
{
resourceReader = new ResourceReader(reader, reader.BaseStream.Position, image_data_size); resourceReader = new ResourceReader(reader, reader.BaseStream.Position, image_data_size);
}
image_data = resourceReader; image_data = resourceReader;
} }
} }
@@ -204,7 +207,7 @@ namespace AssetStudio
R8, R8,
ETC_RGB4Crunched, ETC_RGB4Crunched,
ETC2_RGBA8Crunched, ETC2_RGBA8Crunched,
R16_2, R16_Alt,
ASTC_HDR_4x4, ASTC_HDR_4x4,
ASTC_HDR_5x5, ASTC_HDR_5x5,
ASTC_HDR_6x6, ASTC_HDR_6x6,

View File

@@ -27,32 +27,5 @@ namespace AssetStudio
} }
m_Father = new PPtr<Transform>(reader); m_Father = new PPtr<Transform>(reader);
} }
public Transform FindChild(string path)
{
if (path.Length == 0)
{
return this;
}
return FindChild(path, 0);
}
private Transform FindChild(string path, int startIndex)
{
int separatorIndex = path.IndexOf('/', startIndex);
string childName = separatorIndex == -1 ?
path.Substring(startIndex, path.Length - startIndex) :
path.Substring(startIndex, separatorIndex - startIndex);
foreach (PPtr<Transform> childPtr in m_Children)
{
if(childPtr.TryGet(out var child))
{
if (child.m_GameObject.TryGet(out var childGO) && childGO.m_Name == childName)
{
return separatorIndex == -1 ? child : child.FindChild(path, separatorIndex + 1);
}
}
}
return null;
}
} }
} }

View File

@@ -8,7 +8,7 @@ namespace AssetStudio
public long m_Offset; //ulong public long m_Offset; //ulong
public long m_Size; //ulong public long m_Size; //ulong
public StreamedResource(BinaryReader reader) public StreamedResource(EndianBinaryReader reader)
{ {
m_Source = reader.ReadAlignedString(); m_Source = reader.ReadAlignedString();
m_Offset = reader.ReadInt64(); m_Offset = reader.ReadInt64();

View File

@@ -0,0 +1,59 @@
using System;
using System.Buffers.Binary;
namespace AssetStudio
{
public static class BlkUtils
{
private const int KeySize = 0x1000;
private const int SeedBlockSize = 0x800;
public static CryptoStream Decrypt(FileReader reader, Blk blk)
{
reader.Endian = EndianType.LittleEndian;
var signature = reader.ReadStringToNull();
var count = reader.ReadInt32();
var key = reader.ReadBytes(count);
reader.Position += count;
var seedSize = Math.Min(reader.ReadInt16(), blk.SBox.IsNullOrEmpty() ? SeedBlockSize : SeedBlockSize * 2);
if (!blk.SBox.IsNullOrEmpty() && blk.Type.IsGI())
{
for (int i = 0; i < 0x10; i++)
{
key[i] = blk.SBox[(i % 4 * 0x100) | key[i]];
}
}
AES.Decrypt(key, blk.ExpansionKey);
for (int i = 0; i < 0x10; i++)
{
key[i] ^= blk.InitVector[i];
}
ulong keySeed = ulong.MaxValue;
var dataPos = reader.Position;
for (int i = 0; i < seedSize; i += 8)
{
keySeed ^= reader.ReadUInt64();
}
reader.Position = dataPos;
var keyLow = BinaryPrimitives.ReadUInt64LittleEndian(key.AsSpan(0, 8));
var keyHigh = BinaryPrimitives.ReadUInt64LittleEndian(key.AsSpan(8, 8));
var seed = keyLow ^ keyHigh ^ keySeed ^ blk.InitSeed;
MT19937_64.Init(seed);
var xorpad = new byte[KeySize];
for (int i = 0; i < KeySize; i += 8)
{
BinaryPrimitives.WriteUInt64LittleEndian(xorpad.AsSpan(i, 8), MT19937_64.Int64());
}
return new CryptoStream(reader.BaseStream, xorpad);
}
}
}

View File

@@ -0,0 +1,164 @@
using System;
using System.Text;
using System.Security.Cryptography;
namespace AssetStudio
{
public class CNUnity
{
private const string Signature = "#$unity3dchina!@";
public static ICryptoTransform Encryptor;
public byte[] Index = new byte[0x10];
public byte[] Sub = new byte[0x10];
public CNUnity(EndianBinaryReader reader)
{
reader.ReadUInt32();
var infoBytes = reader.ReadBytes(0x10);
var infoKey = reader.ReadBytes(0x10);
reader.Position += 1;
var signatureBytes = reader.ReadBytes(0x10);
var signatureKey = reader.ReadBytes(0x10);
reader.Position += 1;
DecryptKey(signatureKey, signatureBytes);
var str = Encoding.UTF8.GetString(signatureBytes);
if (str != Signature)
throw new Exception("Invalid Signature !!");
DecryptKey(infoKey, infoBytes);
infoBytes = infoBytes.ToUInt4Array();
infoBytes.AsSpan(0, 0x10).CopyTo(Index);
var subBytes = infoBytes.AsSpan(0x10, 0x10);
for (var i = 0; i < subBytes.Length; i++)
{
var idx = (i % 4 * 4) + (i / 4);
Sub[idx] = subBytes[i];
}
}
public static bool SetKey(Entry entry)
{
try
{
using var aes = Aes.Create();
aes.Mode = CipherMode.ECB;
aes.Key = Encoding.UTF8.GetBytes(entry.Key);
Encryptor = aes.CreateEncryptor();
}
catch(Exception e)
{
Logger.Error($"[CNUnity] Invalid key !!\n{e.Message}");
return false;
}
return true;
}
public void DecryptBlock(Span<byte> bytes, int size, int index)
{
var offset = 0;
while (offset < size)
{
offset += Decrypt(bytes[offset..], index++, size - offset);
}
}
private void DecryptKey(byte[] key, byte[] data)
{
if (Encryptor != null)
{
key = Encryptor.TransformFinalBlock(key, 0, key.Length);
for (int i = 0; i < 0x10; i++)
data[i] ^= key[i];
}
}
private int DecryptByte(Span<byte> bytes, ref int offset, ref int index)
{
var b = Sub[((index >> 2) & 3) + 4] + Sub[index & 3] + Sub[((index >> 4) & 3) + 8] + Sub[((byte)index >> 6) + 12];
bytes[offset] = (byte)((Index[bytes[offset] & 0xF] - b) & 0xF | 0x10 * (Index[bytes[offset] >> 4] - b));
b = bytes[offset];
offset++;
index++;
return b;
}
private int Decrypt(Span<byte> bytes, int index, int remaining)
{
var offset = 0;
var curByte = DecryptByte(bytes, ref offset, ref index);
var byteHigh = curByte >> 4;
var byteLow = curByte & 0xF;
if (byteHigh == 0xF)
{
int b;
do
{
b = DecryptByte(bytes, ref offset, ref index);
byteHigh += b;
} while (b == 0xFF);
}
offset += byteHigh;
if (offset < remaining)
{
DecryptByte(bytes, ref offset, ref index);
DecryptByte(bytes, ref offset, ref index);
if (byteLow == 0xF)
{
int b;
do
{
b = DecryptByte(bytes, ref offset, ref index);
} while(b == 0xFF);
}
}
return offset;
}
public record Entry
{
public string Name { get; private set; }
public string Key { get; private set; }
public Entry(string name, string key)
{
Name = name;
Key = key;
}
public bool Validate()
{
try
{
var bytes = Encoding.UTF8.GetBytes(Key);
if (bytes.Length != 0x10)
{
Logger.Warning($"[CNUnity] {this} has invalid key, size should be 16 bytes, skipping...");
return false;
}
}
catch(Exception e)
{
Logger.Error($"[CNUnity] Error while adding {this}: {e.Message}");
return false;
}
return true;
}
public override string ToString() => $"{Name} ({Key})";
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,32 +1,28 @@
namespace AssetStudio namespace AssetStudio
{ {
public class MT19937_64 public static class MT19937_64
{ {
private const ulong N = 312; private const ulong N = 312;
private const ulong M = 156; private const ulong M = 156;
private const ulong MATRIX_A = 0xB5026F5AA96619E9L; private const ulong MATRIX_A = 0xB5026F5AA96619E9L;
private const ulong UPPER_MASK = 0xFFFFFFFF80000000; private const ulong UPPER_MASK = 0xFFFFFFFF80000000;
private const ulong LOWER_MASK = 0X7FFFFFFFUL; private const ulong LOWER_MASK = 0X7FFFFFFFUL;
private static ulong[] mt = new ulong[N + 1];
private static readonly ulong[] mt = new ulong[N + 1];
private static ulong mti = N + 1; private static ulong mti = N + 1;
public MT19937_64(ulong seed) public static void Init(ulong seed)
{
this.Seed(seed);
}
public void Seed(ulong seed)
{ {
mt[0] = seed; mt[0] = seed;
for (mti = 1; mti < N; mti++) for (mti = 1; mti < N; mti++)
{ {
mt[mti] = (6364136223846793005L * (mt[mti - 1] ^ (mt[mti - 1] >> 62)) + mti); mt[mti] = 6364136223846793005L * (mt[mti - 1] ^ (mt[mti - 1] >> 62)) + mti;
} }
} }
public ulong Int63() public static ulong Int64()
{ {
ulong x = 0; ulong x;
ulong[] mag01 = new ulong[2] { 0x0UL, MATRIX_A }; ulong[] mag01 = new ulong[2] { 0x0UL, MATRIX_A };
if (mti >= N) if (mti >= N)
@@ -34,7 +30,7 @@
ulong kk; ulong kk;
if (mti == N + 1) if (mti == N + 1)
{ {
Seed(5489UL); Init(5489UL);
} }
for (kk = 0; kk < (N - M); kk++) for (kk = 0; kk < (N - M); kk++)
{ {
@@ -60,9 +56,14 @@
return x; return x;
} }
public ulong IntN(ulong value) public static long Int63()
{ {
return Int63() % value; return (long)(Int64() >> 1);
}
public static ulong IntN(ulong value)
{
return (ulong)Int63() % value;
} }
} }
} }

View File

@@ -0,0 +1,72 @@
using System;
using System.Buffers;
using System.Buffers.Binary;
namespace AssetStudio
{
public static class Mr0kUtils
{
private const int BlockSize = 0x400;
private static readonly byte[] mr0kMagic = { 0x6D, 0x72, 0x30, 0x6B };
public static Span<byte> Decrypt(Span<byte> data, Mr0k mr0k)
{
var key1 = new byte[0x10];
var key2 = new byte[0x10];
var key3 = new byte[0x10];
data.Slice(4, 0x10).CopyTo(key1);
data.Slice(0x74, 0x10).CopyTo(key2);
data.Slice(0x84, 0x10).CopyTo(key3);
var encryptedBlockSize = Math.Min(0x10 * ((data.Length - 0x94) >> 7), BlockSize);
if (!mr0k.InitVector.IsNullOrEmpty())
{
for (int i = 0; i < mr0k.InitVector.Length; i++)
key2[i] ^= mr0k.InitVector[i];
}
if (!mr0k.SBox.IsNullOrEmpty())
{
for (int i = 0; i < 0x10; i++)
key1[i] = mr0k.SBox[(i % 4 * 0x100) | key1[i]];
}
AES.Decrypt(key1, mr0k.ExpansionKey);
AES.Decrypt(key3, mr0k.ExpansionKey);
for (int i = 0; i < key1.Length; i++)
{
key1[i] ^= key3[i];
}
key1.CopyTo(data.Slice(0x84, 0x10));
var seed1 = BinaryPrimitives.ReadUInt64LittleEndian(key2);
var seed2 = BinaryPrimitives.ReadUInt64LittleEndian(key3);
var seed = seed2 ^ seed1 ^ (seed1 + (uint)data.Length - 20);
var encryptedBlock = data.Slice(0x94, encryptedBlockSize);
var seedSpan = BitConverter.GetBytes(seed);
for (var i = 0; i < encryptedBlockSize; i++)
{
encryptedBlock[i] ^= (byte)(seedSpan[i % seedSpan.Length] ^ mr0k.BlockKey[i % mr0k.BlockKey.Length]);
}
data = data[0x14..];
if (!mr0k.PostKey.IsNullOrEmpty())
{
for (int i = 0; i < 0xC00; i++)
{
data[i] ^= mr0k.PostKey[i % mr0k.PostKey.Length];
}
}
return data;
}
public static bool IsMr0k(ReadOnlySpan<byte> data) => data[..4].SequenceEqual(mr0kMagic);
}
}

View File

@@ -3,40 +3,42 @@ using System.Buffers.Binary;
namespace AssetStudio namespace AssetStudio
{ {
public class XORShift128 public static class XORShift128
{ {
public uint x = 0, y = 0, z = 0, w = 0, initseed = 0; public static bool Init = false;
public static uint x = 0, y = 0, z = 0, w = 0, initseed = 0;
const long SEED = 0x61C8864E7A143579; const long SEED = 0x61C8864E7A143579;
const uint MT19937 = 0x6C078965; const uint MT19937 = 0x6C078965;
public void InitSeed(int seed) public static void InitSeed(uint seed)
{ {
initseed = (uint)seed; initseed = seed;
x = (uint)seed; x = seed;
y = MT19937 * x + 1; y = MT19937 * x + 1;
z = MT19937 * y + 1; z = MT19937 * y + 1;
w = MT19937 * z + 1; w = MT19937 * z + 1;
Init = true;
} }
public uint XORShift() public static uint XORShift()
{ {
uint t = x ^ (x << 11); uint t = x ^ (x << 11);
x = y; y = z; z = w; x = y; y = z; z = w;
return w = w ^ (w >> 19) ^ t ^ (t >> 8); return w = w ^ (w >> 19) ^ t ^ (t >> 8);
} }
public uint NextUInt32() public static uint NextUInt32()
{ {
return XORShift(); return XORShift();
} }
public int NextDecryptInt() => BinaryPrimitives.ReadInt32LittleEndian(NextDecrypt(4)); public static int NextDecryptInt() => BinaryPrimitives.ReadInt32LittleEndian(NextDecrypt(4));
public uint NextDecryptUInt() => BinaryPrimitives.ReadUInt32LittleEndian(NextDecrypt(4)); public static uint NextDecryptUInt() => BinaryPrimitives.ReadUInt32LittleEndian(NextDecrypt(4));
public long NextDecryptLong() => BinaryPrimitives.ReadInt64LittleEndian(NextDecrypt(8)); public static long NextDecryptLong() => BinaryPrimitives.ReadInt64LittleEndian(NextDecrypt(8));
public byte[] NextDecrypt(int size) public static byte[] NextDecrypt(int size)
{ {
var valueBytes = new byte[size]; var valueBytes = new byte[size];
var key = size * initseed - SEED; var key = size * initseed - SEED;

View File

@@ -0,0 +1,27 @@
using System.IO;
namespace AssetStudio
{
public class CryptoStream : BlockStream
{
private const long _dataPosition = 0x2A;
private readonly byte[] _xorpad;
public CryptoStream(Stream stream, byte[] xorpad) : base(stream, _dataPosition)
{
_xorpad = xorpad;
}
public override int Read(byte[] buffer, int offset, int count)
{
var pos = RelativePosition;
var read = base.Read(buffer, offset, count);
for (int i = 0; i < count; i++)
{
buffer[i] ^= _xorpad[pos++ % _xorpad.Length];
}
return read;
}
}
}

View File

@@ -1,6 +1,8 @@
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
namespace AssetStudio namespace AssetStudio
{ {
@@ -10,11 +12,10 @@ namespace AssetStudio
public EndianType Endian; public EndianType Endian;
public EndianBinaryReader(Stream stream, EndianType endian = EndianType.BigEndian, Game game = null) : base(stream) public EndianBinaryReader(Stream stream, EndianType endian = EndianType.BigEndian, bool leaveOpen = false) : base(stream, Encoding.UTF8, leaveOpen)
{ {
Endian = endian; Endian = endian;
buffer = new byte[8]; buffer = new byte[8];
Game = game;
} }
public long Position public long Position
@@ -23,8 +24,8 @@ namespace AssetStudio
set => BaseStream.Position = value; set => BaseStream.Position = value;
} }
public long[] BundlePos = Array.Empty<long>(); public long Length => BaseStream.Length;
public Game Game; public long Remaining => Length - Position;
public override short ReadInt16() public override short ReadInt16()
{ {
@@ -108,9 +109,179 @@ namespace AssetStudio
return base.ReadDouble(); return base.ReadDouble();
} }
public Float ReadFloat() public void AlignStream()
{ {
return new Float(ReadSingle()); AlignStream(4);
}
public void AlignStream(int alignment)
{
var pos = Position;
var mod = pos % alignment;
if (mod != 0)
{
Position += alignment - mod;
}
}
public string ReadAlignedString()
{
var length = ReadInt32();
if (length > 0 && length <= Remaining)
{
var stringData = ReadBytes(length);
var result = Encoding.UTF8.GetString(stringData);
AlignStream(4);
return result;
}
return "";
}
public string ReadStringToNull(int maxLength = 32767)
{
var bytes = new List<byte>();
int count = 0;
while (Remaining > 0 && count < maxLength)
{
var b = ReadByte();
if (b == 0)
{
break;
}
bytes.Add(b);
count++;
}
return Encoding.UTF8.GetString(bytes.ToArray());
}
public Quaternion ReadQuaternion()
{
return new Quaternion(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
}
public Vector2 ReadVector2()
{
return new Vector2(ReadSingle(), ReadSingle());
}
public Vector3 ReadVector3()
{
return new Vector3(ReadSingle(), ReadSingle(), ReadSingle());
}
public Vector4 ReadVector4()
{
return new Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
}
public Color ReadColor4()
{
return new Color(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
}
public Matrix4x4 ReadMatrix()
{
return new Matrix4x4(ReadSingleArray(16));
}
public int ReadMhy0Int()
{
var buffer = ReadBytes(6);
return buffer[2] | (buffer[4] << 8) | (buffer[0] << 0x10) | (buffer[5] << 0x18);
}
public uint ReadMhy0UInt()
{
var buffer = ReadBytes(7);
return (uint)(buffer[1] | (buffer[6] << 8) | (buffer[3] << 0x10) | (buffer[2] << 0x18));
}
public string ReadMhy0String()
{
var pos = BaseStream.Position;
var str = ReadStringToNull();
BaseStream.Position += 0x105 - (BaseStream.Position - pos);
return str;
}
private T[] ReadArray<T>(Func<T> del, int length)
{
var array = new T[length];
for (int i = 0; i < length; i++)
{
array[i] = del();
}
return array;
}
public bool[] ReadBooleanArray()
{
return ReadArray(ReadBoolean, ReadInt32());
}
public byte[] ReadUInt8Array()
{
return ReadBytes(ReadInt32());
}
public ushort[] ReadUInt16Array()
{
return ReadArray(ReadUInt16, ReadInt32());
}
public int[] ReadInt32Array()
{
return ReadArray(ReadInt32, ReadInt32());
}
public int[] ReadInt32Array(int length)
{
return ReadArray(ReadInt32, length);
}
public uint[] ReadUInt32Array()
{
return ReadArray(ReadUInt32, ReadInt32());
}
public uint[][] ReadUInt32ArrayArray()
{
return ReadArray(ReadUInt32Array, ReadInt32());
}
public uint[] ReadUInt32Array(int length)
{
return ReadArray(ReadUInt32, length);
}
public float[] ReadSingleArray()
{
return ReadArray(ReadSingle, ReadInt32());
}
public float[] ReadSingleArray(int length)
{
return ReadArray(ReadSingle, length);
}
public string[] ReadStringArray()
{
return ReadArray(ReadAlignedString, ReadInt32());
}
public Vector2[] ReadVector2Array()
{
return ReadArray(ReadVector2, ReadInt32());
}
public Vector4[] ReadVector4Array()
{
return ReadArray(ReadVector4, ReadInt32());
}
public Matrix4x4[] ReadMatrixArray()
{
return ReadArray(ReadMatrix, ReadInt32());
} }
} }
} }

View File

@@ -0,0 +1,18 @@
namespace AssetStudio
{
public enum ExportListType
{
XML,
JSON
}
public static class ExportListTypeExtensions
{
public static string GetExtension(this ExportListType type) => type switch
{
ExportListType.XML => ".xml",
ExportListType.JSON => ".json",
_ => throw new System.NotImplementedException(),
};
}
}

View File

@@ -1,192 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
namespace AssetStudio
{
public static class BinaryReaderExtensions
{
public static void AlignStream(this BinaryReader reader)
{
reader.AlignStream(4);
}
public static void AlignStream(this BinaryReader reader, int alignment)
{
var pos = reader.BaseStream.Position;
var mod = pos % alignment;
if (mod != 0)
{
reader.BaseStream.Position += alignment - mod;
}
}
public static string ReadAlignedString(this BinaryReader reader)
{
var length = reader.ReadInt32();
if (length > 0 && length <= reader.BaseStream.Length - reader.BaseStream.Position)
{
var stringData = reader.ReadBytes(length);
var result = Encoding.UTF8.GetString(stringData);
reader.AlignStream(4);
return result;
}
return "";
}
public static string ReadStringToNull(this BinaryReader reader, int maxLength = 32767)
{
var bytes = new List<byte>();
int count = 0;
while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength)
{
var b = reader.ReadByte();
if (b == 0)
{
break;
}
bytes.Add(b);
count++;
}
return Encoding.UTF8.GetString(bytes.ToArray());
}
public static Quaternion ReadQuaternion(this BinaryReader reader)
{
return new Quaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
}
public static Vector2 ReadVector2(this BinaryReader reader)
{
return new Vector2(reader.ReadSingle(), reader.ReadSingle());
}
public static Vector3 ReadVector3(this BinaryReader reader)
{
return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
}
public static Vector4 ReadVector4(this BinaryReader reader)
{
return new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
}
public static Color ReadColor4(this BinaryReader reader)
{
return new Color(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
}
public static Matrix4x4 ReadMatrix(this BinaryReader reader)
{
return new Matrix4x4(reader.ReadSingleArray(16));
}
public static int ReadMhy0Int1(this BinaryReader reader)
{
var buffer = reader.ReadBytes(7);
return buffer[1] | (buffer[6] << 8) | (buffer[3] << 0x10) | (buffer[2] << 0x18);
}
public static int ReadMhy0Int2(this BinaryReader reader)
{
var buffer = reader.ReadBytes(6);
return buffer[2] | (buffer[4] << 8) | (buffer[0] << 0x10) | (buffer[5] << 0x18);
}
public static string ReadMhy0String(this BinaryReader reader)
{
var bytes = reader.ReadBytes(0x100);
return Encoding.UTF8.GetString(bytes.TakeWhile(b => !b.Equals(0)).ToArray());
}
public static bool ReadMhy0Bool(this BinaryReader reader)
{
var value = reader.ReadMhy0Int2();
var bytes = BitConverter.GetBytes(value);
Array.Reverse(bytes);
return BitConverter.ToBoolean(bytes, 0);
}
private static T[] ReadArray<T>(Func<T> del, int length)
{
var array = new T[length];
for (int i = 0; i < length; i++)
{
array[i] = del();
}
return array;
}
public static bool[] ReadBooleanArray(this BinaryReader reader)
{
return ReadArray(reader.ReadBoolean, reader.ReadInt32());
}
public static byte[] ReadUInt8Array(this BinaryReader reader)
{
return reader.ReadBytes(reader.ReadInt32());
}
public static ushort[] ReadUInt16Array(this BinaryReader reader)
{
return ReadArray(reader.ReadUInt16, reader.ReadInt32());
}
public static int[] ReadInt32Array(this BinaryReader reader)
{
return ReadArray(reader.ReadInt32, reader.ReadInt32());
}
public static int[] ReadInt32Array(this BinaryReader reader, int length)
{
return ReadArray(reader.ReadInt32, length);
}
public static uint[] ReadUInt32Array(this BinaryReader reader)
{
return ReadArray(reader.ReadUInt32, reader.ReadInt32());
}
public static uint[][] ReadUInt32ArrayArray(this BinaryReader reader)
{
return ReadArray(reader.ReadUInt32Array, reader.ReadInt32());
}
public static uint[] ReadUInt32Array(this BinaryReader reader, int length)
{
return ReadArray(reader.ReadUInt32, length);
}
public static float[] ReadSingleArray(this BinaryReader reader)
{
return ReadArray(reader.ReadSingle, reader.ReadInt32());
}
public static float[] ReadSingleArray(this BinaryReader reader, int length)
{
return ReadArray(reader.ReadSingle, length);
}
public static string[] ReadStringArray(this BinaryReader reader)
{
return ReadArray(reader.ReadAlignedString, reader.ReadInt32());
}
public static Vector2[] ReadVector2Array(this BinaryReader reader)
{
return ReadArray(reader.ReadVector2, reader.ReadInt32());
}
public static Vector4[] ReadVector4Array(this BinaryReader reader)
{
return ReadArray(reader.ReadVector4, reader.ReadInt32());
}
public static Matrix4x4[] ReadMatrixArray(this BinaryReader reader)
{
return ReadArray(reader.ReadMatrix, reader.ReadInt32());
}
}
}

View File

@@ -1,27 +0,0 @@
using System;
using System.IO;
using System.Text;
namespace AssetStudio
{
public static class BinaryWriterExtensions
{
public static void AlignStream(this BinaryWriter writer, int alignment)
{
var pos = writer.BaseStream.Position;
var mod = pos % alignment;
if (mod != 0)
{
writer.Write(new byte[alignment - mod]);
}
}
public static void WriteAlignedString(this BinaryWriter writer, string str)
{
var bytes = Encoding.UTF8.GetBytes(str);
writer.Write(bytes.Length);
writer.Write(bytes);
writer.AlignStream(4);
}
}
}

View File

@@ -1,107 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace AssetStudio
{
public static class BitConverterExtensions
{
[StructLayout(LayoutKind.Explicit)]
private struct FloatUIntUnion
{
[FieldOffset(0)]
public uint Int;
[FieldOffset(0)]
public float Float;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ToUInt32(float value)
{
return new FloatUIntUnion { Float = value }.Int;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong ToUInt64(double value)
{
return unchecked((ulong)BitConverter.DoubleToInt64Bits(value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ToSingle(uint value)
{
return new FloatUIntUnion { Int = value }.Float;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double ToDouble(ulong value)
{
return BitConverter.Int64BitsToDouble(unchecked((long)value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(ushort value, byte[] buffer, int offset)
{
buffer[offset + 0] = unchecked((byte)(value >> 0));
buffer[offset + 1] = unchecked((byte)(value >> 8));
}
public static void GetBytes(uint value, byte[] buffer, int offset)
{
buffer[offset + 0] = unchecked((byte)(value >> 0));
buffer[offset + 1] = unchecked((byte)(value >> 8));
buffer[offset + 2] = unchecked((byte)(value >> 16));
buffer[offset + 3] = unchecked((byte)(value >> 24));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short Swap(short value)
{
return unchecked((short)(Swap(unchecked((ushort)value))));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort Swap(ushort value)
{
return unchecked((ushort)(value >> 8 | value << 8));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Swap(int value)
{
return unchecked((int)(Swap(unchecked((uint)value))));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Swap(uint value)
{
value = value >> 16 | value << 16;
return ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long Swap(long value)
{
return unchecked((long)(Swap(unchecked((ulong)value))));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Swap(ulong value)
{
value = value >> 32 | value << 32;
value = ((value & 0xFFFF0000FFFF0000) >> 16) | ((value & 0x0000FFFF0000FFFF) << 16);
return ((value & 0xFF00FF00FF00FF00) >> 8) | ((value & 0x00FF00FF00FF00FF) << 8);
}
public static int GetDigitsCount(uint value)
{
int count = 0;
while (value != 0)
{
value /= 10;
count++;
}
return count;
}
}
}

View File

@@ -0,0 +1,38 @@
using System.Text;
namespace AssetStudio
{
public static class ByteArrayExtensions
{
public static bool IsNullOrEmpty<T>(this T[] array) => array == null || array.Length == 0;
public static byte[] ToUInt4Array(this byte[] source) => ToUInt4Array(source, 0, source.Length);
public static byte[] ToUInt4Array(this byte[] source, int offset, int size)
{
var buffer = new byte[size * 2];
for (var i = 0; i < size; i++)
{
var idx = i * 2;
buffer[idx] = (byte)(source[offset + i] >> 4);
buffer[idx + 1] = (byte)(source[offset + i] & 0xF);
}
return buffer;
}
public static int Search(this byte[] src, string value, int offset = 0) => Search(src, Encoding.UTF8.GetBytes(value), offset);
public static int Search(this byte[] src, byte[] pattern, int offset)
{
int maxFirstCharSlot = src.Length - pattern.Length + 1;
for (int i = offset; i < maxFirstCharSlot; i++)
{
if (src[i] != pattern[0])
continue;
for (int j = pattern.Length - 1; j >= 1; j--)
{
if (src[i + j] != pattern[j]) break;
if (j == 1) return i;
}
}
return -1;
}
}
}

View File

@@ -1,79 +0,0 @@
namespace AssetStudio
{
public static class PrimitiveExtensions
{
public static int ParseDigit(this char _this)
{
return _this - '0';
}
public static string ToHexString(this byte _this)
{
return _this.ToString("x2");
}
public static string ToHexString(this short _this)
{
ushort value = unchecked((ushort)_this);
return ToHexString(value);
}
public static string ToHexString(this ushort _this)
{
ushort reverse = unchecked((ushort)(((0xFF00 & _this) >> 8) | ((0x00FF & _this) << 8)));
return reverse.ToString("x4");
}
public static string ToHexString(this int _this)
{
uint value = unchecked((uint)_this);
return ToHexString(value);
}
public static string ToHexString(this uint _this)
{
uint reverse = ((0xFF000000 & _this) >> 24) | ((0x00FF0000 & _this) >> 8) | ((0x0000FF00 & _this) << 8) | ((0x000000FF & _this) << 24);
return reverse.ToString("x8");
}
public static string ToHexString(this long _this)
{
ulong value = unchecked((ulong)_this);
return ToHexString(value);
}
public static string ToHexString(this ulong _this)
{
ulong reverse = (_this & 0x00000000000000FFUL) << 56 | (_this & 0x000000000000FF00UL) << 40 |
(_this & 0x0000000000FF0000UL) << 24 | (_this & 0x00000000FF000000UL) << 8 |
(_this & 0x000000FF00000000UL) >> 8 | (_this & 0x0000FF0000000000UL) >> 24 |
(_this & 0x00FF000000000000UL) >> 40 | (_this & 0xFF00000000000000UL) >> 56;
return reverse.ToString("x16");
}
public static string ToHexString(this float _this)
{
uint value = BitConverterExtensions.ToUInt32(_this);
return ToHexString(value);
}
public static string ToHexString(this double _this)
{
ulong value = BitConverterExtensions.ToUInt64(_this);
return ToHexString(value);
}
public static int ToClosestInt(this long _this)
{
if (_this > int.MaxValue)
{
return int.MaxValue;
}
if (_this < int.MinValue)
{
return int.MinValue;
}
return unchecked((int)_this);
}
}
}

View File

@@ -1,87 +0,0 @@
using System.Text;
namespace AssetStudio
{
public static class StringBuilderExtensions
{
static StringBuilderExtensions()
{
for (int i = 0; i <= byte.MaxValue; i++)
{
ByteHexRepresentations[i] = i.ToString("x2");
}
}
public static StringBuilder AppendHex(this StringBuilder _this, byte value)
{
_this.Append(ByteHexRepresentations[value]);
return _this;
}
public static StringBuilder AppendHex(this StringBuilder _this, ushort value)
{
_this.Append(ByteHexRepresentations[(value >> 0) & 0xFF]);
_this.Append(ByteHexRepresentations[(value >> 8) & 0xFF]);
return _this;
}
public static StringBuilder AppendHex(this StringBuilder _this, short value)
{
return AppendHex(_this, unchecked((ushort)value));
}
public static StringBuilder AppendHex(this StringBuilder _this, uint value)
{
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 0) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 8) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 16) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 24) & 0xFF)]);
return _this;
}
public static StringBuilder AppendHex(this StringBuilder _this, int value)
{
return AppendHex(_this, unchecked((uint)value));
}
public static StringBuilder AppendHex(this StringBuilder _this, ulong value)
{
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 0) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 8) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 16) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 24) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 32) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 40) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 48) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 56) & 0xFF)]);
return _this;
}
public static StringBuilder AppendHex(this StringBuilder _this, long value)
{
return AppendHex(_this, unchecked((ulong)value));
}
public static StringBuilder AppendHex(this StringBuilder _this, float value)
{
return AppendHex(_this, BitConverterExtensions.ToUInt32(value));
}
public static StringBuilder AppendHex(this StringBuilder _this, double value)
{
return AppendHex(_this, BitConverterExtensions.ToUInt64(value));
}
public static StringBuilder AppendIndent(this StringBuilder _this, int count)
{
for (int i = 0; i < count; i++)
{
_this.Append('\t');
}
return _this;
}
public static readonly string HexAlphabet = "0123456789abcdef";
public static readonly string[] ByteHexRepresentations = new string[256];
}
}

View File

@@ -1,5 +1,6 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using static AssetStudio.ImportHelper;
namespace AssetStudio namespace AssetStudio
{ {
@@ -8,40 +9,32 @@ namespace AssetStudio
public string FullPath; public string FullPath;
public string FileName; public string FileName;
public FileType FileType; public FileType FileType;
public long Length;
private static readonly byte[] gzipMagic = { 0x1f, 0x8b }; private static readonly byte[] gzipMagic = { 0x1f, 0x8b };
private static readonly byte[] brotliMagic = { 0x62, 0x72, 0x6F, 0x74, 0x6C, 0x69 }; private static readonly byte[] brotliMagic = { 0x62, 0x72, 0x6F, 0x74, 0x6C, 0x69 };
private static readonly byte[] zipMagic = { 0x50, 0x4B, 0x03, 0x04 }; private static readonly byte[] zipMagic = { 0x50, 0x4B, 0x03, 0x04 };
private static readonly byte[] zipSpannedMagic = { 0x50, 0x4B, 0x07, 0x08 }; private static readonly byte[] zipSpannedMagic = { 0x50, 0x4B, 0x07, 0x08 };
private static readonly byte[] mhy0Magic = { 0x6D, 0x68, 0x79, 0x30 };
private static readonly byte[] narakaMagic = { 0x15, 0x1E, 0x1C, 0x0D, 0x0D, 0x23, 0x21 };
private static readonly byte[] gunfireMagic = { 0x7C, 0x6D, 0x79, 0x72, 0x27, 0x7A, 0x73, 0x78, 0x3F };
public FileReader(string path, Game game = null) : this(path, File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), game) { }
public FileReader(string path, Stream stream, Game game = null) : base(stream, EndianType.BigEndian, game) public FileReader(string path) : this(path, File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { }
public FileReader(string path, Stream stream, bool leaveOpen = false) : base(stream, EndianType.BigEndian, leaveOpen)
{ {
Game = game;
FullPath = Path.GetFullPath(path); FullPath = Path.GetFullPath(path);
FileName = Path.GetFileName(path); FileName = Path.GetFileName(path);
FileType = CheckFileType(); FileType = CheckFileType();
Length = stream.Length;
} }
private FileType CheckFileType() private FileType CheckFileType()
{ {
if (IsSerializedFile())
{
return FileType.AssetsFile;
}
else if (Game != null)
{
return FileType.GameFile;
}
var signature = this.ReadStringToNull(20); var signature = this.ReadStringToNull(20);
Position = 0; Position = 0;
switch (signature) switch (signature)
{ {
case "ENCR":
case "UnityWeb": case "UnityWeb":
case "UnityRaw": case "UnityRaw":
case "UnityArchive": case "UnityArchive":
@@ -49,6 +42,8 @@ namespace AssetStudio
return FileType.BundleFile; return FileType.BundleFile;
case "UnityWebData1.0": case "UnityWebData1.0":
return FileType.WebFile; return FileType.WebFile;
case "blk":
return FileType.BlkFile;
default: default:
{ {
byte[] magic = ReadBytes(2); byte[] magic = ReadBytes(2);
@@ -71,7 +66,26 @@ namespace AssetStudio
magic = ReadBytes(4); magic = ReadBytes(4);
Position = 0; Position = 0;
if (zipMagic.SequenceEqual(magic) || zipSpannedMagic.SequenceEqual(magic)) if (zipMagic.SequenceEqual(magic) || zipSpannedMagic.SequenceEqual(magic))
{
return FileType.ZipFile; return FileType.ZipFile;
}
if (mhy0Magic.SequenceEqual(magic))
{
return FileType.Mhy0File;
}
magic = ReadBytes(7);
Position = 0;
if (narakaMagic.SequenceEqual(magic))
{
return FileType.BundleFile;
}
magic = ReadBytes(9);
Position = 0;
if (gunfireMagic.SequenceEqual(magic))
{
Position = 0x32;
return FileType.BundleFile;
}
return FileType.ResourceFile; return FileType.ResourceFile;
} }
} }
@@ -113,4 +127,35 @@ namespace AssetStudio
return true; return true;
} }
} }
public static class FileReaderExtensions
{
public static FileReader PreProcessing(this FileReader reader, Game game)
{
if (reader.FileType == FileType.ResourceFile || !game.Type.IsNormal())
{
switch (game.Type)
{
case GameType.GI_Pack:
reader = DecryptPack(reader, game);
break;
case GameType.GI_CB1:
reader = DecryptMark(reader);
break;
case GameType.EnsembleStars:
reader = DecryptEnsembleStar(reader);
break;
case GameType.OPFP:
reader = ParseOPFP(reader);
break;
}
}
if (reader.FileType == FileType.BundleFile && game.Type.IsBlockFile())
{
reader.FileType = FileType.BlockFile;
}
return reader;
}
}
} }

View File

@@ -10,11 +10,13 @@ namespace AssetStudio
{ {
AssetsFile, AssetsFile,
BundleFile, BundleFile,
GameFile,
WebFile, WebFile,
ResourceFile, ResourceFile,
GZipFile, GZipFile,
BrotliFile, BrotliFile,
ZipFile ZipFile,
BlkFile,
Mhy0File,
BlockFile
} }
} }

View File

@@ -1,30 +0,0 @@
using System.Collections.Generic;
namespace AssetStudio
{
public class WMVFile
{
public Dictionary<long, StreamFile[]> Bundles = new Dictionary<long, StreamFile[]>();
public WMVFile(FileReader reader)
{
if (reader.BundlePos.Length != 0)
{
foreach (var pos in reader.BundlePos)
{
reader.Position = pos;
var bundle = new BundleFile(reader);
Bundles.Add(pos, bundle.FileList);
}
}
else
{
while (reader.Position != reader.Length)
{
var pos = reader.Position;
var bundle = new BundleFile(reader);
Bundles.Add(pos, bundle.FileList);
}
}
}
}
}

View File

@@ -1,21 +0,0 @@
using System.Collections.Generic;
using System.IO;
namespace AssetStudio
{
public class CB1File
{
public Dictionary<long, StreamFile[]> Bundles = new Dictionary<long, StreamFile[]>();
public CB1File(FileReader reader)
{
var data = Mark.Decrypt(reader);
using (var ms = new MemoryStream(data))
using (var subReader = new FileReader(reader.FullPath, ms, reader.Game))
{
var bundle = new BundleFile(subReader);
Bundles.Add(0, bundle.FileList);
}
}
}
}

View File

@@ -1,39 +0,0 @@
using System.Collections.Generic;
using System.IO;
namespace AssetStudio
{
public class CBXFile
{
public Dictionary<long, StreamFile[]> Bundles = new Dictionary<long, StreamFile[]>();
public CBXFile(FileReader reader)
{
var data = Blk.Decrypt(reader);
using (var ms = new MemoryStream(data))
using (var subReader = new FileReader(reader.FullPath, ms, reader.Game))
{
if (subReader.BundlePos.Length != 0)
{
foreach (var pos in subReader.BundlePos)
{
subReader.Position = pos;
var bundle = new BundleFile(subReader);
Bundles.Add(pos, bundle.FileList);
}
}
else
{
while (subReader.Position != subReader.BaseStream.Length)
{
var pos = subReader.Position;
var bundle = new BundleFile(subReader);
if (bundle.FileList == null)
continue;
Bundles.Add(pos, bundle.FileList);
}
}
}
}
}
}

View File

@@ -1,76 +0,0 @@
using System;
namespace AssetStudio
{
public static class Blk
{
private const int KeySize = 0x1000;
private const int SeedBlockSize = 0x800;
public static byte[] ExpansionKey;
public static byte[] SBox;
public static byte[] ConstKey;
public static ulong ConstVal;
public static byte[] Decrypt(FileReader reader)
{
reader.Endian = EndianType.LittleEndian;
var signature = reader.ReadStringToNull();
if (signature != "blk")
throw new Exception("not a blk");
var count = reader.ReadInt32();
var key = reader.ReadBytes(count);
reader.Position += count;
var seedSize = Math.Min(reader.ReadInt16(), SBox == null ? SeedBlockSize : SeedBlockSize * 2);
var dataSize = (int)(reader.BaseStream.Length - reader.BaseStream.Position);
var buffer = reader.ReadBytes(dataSize);
long keySeed = -1;
for (int i = 0; i < seedSize / 8; i++)
{
keySeed ^= BitConverter.ToInt64(buffer, i * 8);
}
DecryptKey(key);
var keyLow = BitConverter.ToUInt64(key, 0);
var keyHigh = BitConverter.ToUInt64(key, 8);
var seed = keyLow ^ keyHigh ^ (ulong)keySeed ^ ConstVal;
var xorpad = new byte[KeySize];
var rand = new MT19937_64(seed);
for (int i = 0; i < KeySize / 8; i++)
{
Buffer.BlockCopy(BitConverter.GetBytes(rand.Int63()), 0, xorpad, i * 8, 8);
}
for (int i = 0; i < dataSize; i++)
{
buffer[i] ^= xorpad[i % KeySize];
}
return buffer;
}
private static void DecryptKey(byte[] key)
{
if (SBox != null)
{
for (int i = 0; i < 0x10; i++)
{
key[i] = SBox[(i % 4 * 0x100) | key[i]];
}
}
AES.Decrypt(key, ExpansionKey);
for (int i = 0; i < 0x10; i++)
{
key[i] ^= ConstKey[i];
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,66 +0,0 @@
using System;
using System.IO;
namespace AssetStudio
{
public static class Mark
{
private const string Signature = "mark";
private const int BlockSize = 0xA00;
private const int ChunkSize = 0x264;
private const int ChunkPadding = 4;
private static readonly int BlockPadding = ((BlockSize / ChunkSize) + 1) * ChunkPadding;
private static readonly int ChunkSizeWithPadding = ChunkSize + ChunkPadding;
private static readonly int BlockSizeWithPadding = BlockSize + BlockPadding;
public static byte[] Decrypt(FileReader reader)
{
var signature = reader.ReadStringToNull(4);
if (signature != Signature)
{
throw new InvalidOperationException($"Expected signature mark, got {signature} instead !!");
}
byte[] buffer;
var block = new byte[BlockSizeWithPadding];
using (var ms = new MemoryStream())
using (var writer = new BinaryWriter(ms))
{
while (reader.Length != reader.Position)
{
var blockSize = (int)Math.Min(reader.Length - reader.Position, BlockSizeWithPadding);
var readBytes = reader.Read(block, 0, blockSize);
if (readBytes != blockSize)
{
throw new InvalidOperationException($"Expected {blockSize} but got {readBytes} !!");
}
var offset = 0;
while (offset != blockSize)
{
var chunkSize = Math.Min(readBytes, ChunkSizeWithPadding);
if (!(blockSize == BlockSizeWithPadding || chunkSize == ChunkSizeWithPadding))
{
writer.Write(block, offset, chunkSize);
}
else
{
writer.Write(block, offset, chunkSize - ChunkPadding);
}
readBytes -= chunkSize;
offset += chunkSize;
}
}
buffer = ms.ToArray();
}
for (int j = 0; j < buffer.Length; j++)
{
buffer[j] ^= Crypto.MarkKey[j % Crypto.MarkKey.Length];
}
return buffer;
}
}
}

View File

@@ -1,85 +0,0 @@
using System;
using System.IO;
namespace AssetStudio
{
public static class Mr0k
{
public static byte[] ExpansionKey;
public static byte[] Key;
public static byte[] ConstKey;
public static byte[] SBox;
public static byte[] BlockKey;
public static void Decrypt(ref byte[] bytes, ref int size)
{
var key1 = new byte[0x10];
var key2 = new byte[0x10];
var key3 = new byte[0x10];
Buffer.BlockCopy(bytes, 4, key1, 0, key1.Length);
Buffer.BlockCopy(bytes, 0x74, key2, 0, key2.Length);
Buffer.BlockCopy(bytes, 0x84, key3, 0, key3.Length);
var encryptedBlockSize = Math.Min(0x10 * ((size - 0x94) >> 7), 0x400);
var encryptedBlock = new byte[encryptedBlockSize];
Buffer.BlockCopy(bytes, 0x94, encryptedBlock, 0, encryptedBlockSize);
if (ConstKey != null)
{
for (int i = 0; i < ConstKey.Length; i++)
key2[i] ^= ConstKey[i];
}
if (SBox != null)
{
for (int i = 0; i < 0x10; i++)
key1[i] = SBox[(i % 4 * 0x100) | key1[i]];
}
AES.Decrypt(key1, ExpansionKey);
AES.Decrypt(key3, ExpansionKey);
for (int i = 0; i < key1.Length; i++)
{
key1[i] ^= key3[i];
}
Buffer.BlockCopy(key1, 0, bytes, 0x84, key1.Length);
var seed1 = BitConverter.ToUInt64(key2, 0);
var seed2 = BitConverter.ToUInt64(key3, 0);
var seed = seed2 ^ seed1 ^ (seed1 + (uint)size - 20);
var seedBytes = BitConverter.GetBytes(seed);
for (var i = 0; i < encryptedBlockSize; i++)
{
encryptedBlock[i] ^= (byte)(seedBytes[i % 8] ^ Key[i]);
}
Buffer.BlockCopy(encryptedBlock, 0, bytes, 0x94, encryptedBlockSize);
size -= 0x14;
bytes = bytes.AsSpan(0x14, size).ToArray();
if (BlockKey != null)
{
for (int i = 0; i < 0xC00; i++)
{
bytes[i] ^= BlockKey[i % BlockKey.Length];
}
}
}
public static bool IsMr0k(byte[] bytes)
{
using (var ms = new MemoryStream(bytes))
using (var reader = new EndianBinaryReader(ms))
{
var header = reader.ReadStringToNull(4);
return header == "mr0k";
}
}
}
}

View File

@@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace AssetStudio
{
public class GIFile
{
public Dictionary<long, StreamFile[]> Bundles = new Dictionary<long, StreamFile[]>();
public GIFile(FileReader reader)
{
var data = Blk.Decrypt(reader);
using (var ms = new MemoryStream(data))
using (var subReader = new EndianBinaryReader(ms, reader.Endian))
{
long pos = -1;
try
{
if (reader.BundlePos.Length != 0)
{
for (int i = 0; i < reader.BundlePos.Length; i++)
{
pos = reader.BundlePos[i];
subReader.Position = pos;
var mhy0 = new Mhy0File(subReader, reader.FullPath);
Bundles.Add(pos, mhy0.FileList);
}
}
else
{
while (subReader.Position != subReader.BaseStream.Length)
{
pos = subReader.Position;
var mhy0 = new Mhy0File(subReader, reader.FullPath);
Bundles.Add(pos, mhy0.FileList);
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Failed to load a mhy0 at {string.Format("0x{0:x8}", pos)} in {Path.GetFileName(reader.FullPath)}");
Console.WriteLine(ex.Message);
}
}
}
}
}

View File

@@ -1,164 +0,0 @@
using K4os.Compression.LZ4;
using System;
using System.IO;
using System.Linq;
namespace AssetStudio
{
public partial class Mhy0File
{
public class Header
{
public int Size;
public int BundleCount;
public int BlockCount;
}
public class StorageBlock
{
public int CompressedSize;
public int UncompressedSize;
}
public class Node
{
public long Offset;
public long Size;
public string Path;
public bool IsAssetFile;
}
private Header m_Header;
private StorageBlock[] m_BlocksInfo;
private Node[] m_DirectoryInfo;
public StreamFile[] FileList;
private byte[] DecompressHeader(byte[] header)
{
using (var ms = new MemoryStream(header))
using (var reader = new EndianBinaryReader(ms))
{
reader.Position += 0x20;
var decompressedSize = reader.ReadMhy0Int1();
var decompressed = new byte[decompressedSize];
var compressed = reader.ReadBytes((int)(reader.BaseStream.Length - reader.Position));
var numWrite = LZ4Codec.Decode(compressed, decompressed);
if (numWrite != decompressedSize)
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {decompressedSize} bytes");
return decompressed;
}
}
private void ReadBlocksInfoAndDirectory(byte[] header)
{
using (var ms = new MemoryStream(header))
using (var reader = new EndianBinaryReader(ms))
{
m_Header.BundleCount = reader.ReadMhy0Int2();
m_DirectoryInfo = new Node[m_Header.BundleCount];
for (int i = 0; i < m_Header.BundleCount; i++)
{
m_DirectoryInfo[i] = new Node
{
Path = reader.ReadMhy0String(),
IsAssetFile = reader.ReadMhy0Bool(),
Offset = reader.ReadMhy0Int2(),
Size = reader.ReadMhy0Int1(),
};
}
m_Header.BlockCount = reader.ReadMhy0Int2();
m_BlocksInfo = new StorageBlock[m_Header.BlockCount];
for (int i = 0; i < m_Header.BlockCount; i++)
{
m_BlocksInfo[i] = new StorageBlock
{
CompressedSize = reader.ReadMhy0Int2(),
UncompressedSize = reader.ReadMhy0Int1()
};
}
}
}
private Stream CreateBlocksStream(string path)
{
Stream blocksStream;
var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.UncompressedSize);
if (uncompressedSizeSum >= int.MaxValue)
blocksStream = new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose);
else
blocksStream = new MemoryStream(uncompressedSizeSum);
return blocksStream;
}
private void ReadBlocks(EndianBinaryReader reader, Stream blocksStream)
{
var compressedBytes = BigArrayPool<byte>.Shared.Rent(m_BlocksInfo.Max(x => x.CompressedSize));
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(m_BlocksInfo.Max(x => x.UncompressedSize));
foreach (var blockInfo in m_BlocksInfo)
{
var compressedSize = blockInfo.CompressedSize;
reader.Read(compressedBytes, 0, compressedSize);
if (compressedSize < 0x10)
throw new Exception($"Wrong compressed length: {compressedSize}");
compressedBytes = Crypto.DescrambleEntry(compressedBytes);
var uncompressedSize = blockInfo.UncompressedSize;
var numWrite = LZ4Codec.Decode(compressedBytes.AsSpan(0xC, compressedSize - 0xC), uncompressedBytes.AsSpan(0, uncompressedSize));
if (numWrite != uncompressedSize)
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
}
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
}
private void ReadFiles(Stream blocksStream, string path)
{
FileList = new StreamFile[m_DirectoryInfo.Length];
for (int i = 0; i < m_DirectoryInfo.Length; i++)
{
var node = m_DirectoryInfo[i];
var file = new StreamFile();
FileList[i] = file;
file.path = node.Path;
file.fileName = Path.GetFileName(node.Path);
if (node.Size >= int.MaxValue)
{
var extractPath = path + "_unpacked" + Path.DirectorySeparatorChar;
Directory.CreateDirectory(extractPath);
file.stream = new FileStream(extractPath + file.fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
}
else
file.stream = new MemoryStream((int)node.Size);
blocksStream.Position = node.Offset;
blocksStream.CopyTo(file.stream, node.Size);
file.stream.Position = 0;
}
}
public Mhy0File(EndianBinaryReader reader, string path)
{
var magic = reader.ReadStringToNull(4);
if (magic != "mhy0")
throw new Exception("not a mhy0");
m_Header = new Header();
m_Header.Size = reader.ReadInt32();
var header = reader.ReadBytes(m_Header.Size);
header = Crypto.DescrambleHeader(header);
header = DecompressHeader(header);
ReadBlocksInfoAndDirectory(header);
using (var blocksStream = CreateBlocksStream(path))
{
ReadBlocks(reader, blocksStream);
ReadFiles(blocksStream, path);
}
}
}
}

View File

@@ -1,140 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
namespace AssetStudio
{
public class GameFile
{
public Dictionary<long, StreamFile[]> Bundles = new Dictionary<long, StreamFile[]>();
public GameFile(FileReader reader)
{
var bundles = new Dictionary<long, StreamFile[]>();
var ext = Path.GetExtension(reader.FileName);
switch (reader.Game.Name)
{
case "GI":
var gi = GameManager.GetGame("GI");
if (ext != gi.Extension)
goto default;
Blk.ExpansionKey = Crypto.GIExpansionKey;
Blk.SBox = Crypto.GISBox;
Blk.ConstKey = Crypto.GIConstKey;
Blk.ConstVal = Crypto.GIConstVal;
var giFile = new GIFile(reader);
bundles = giFile.Bundles;
break;
case "GI_CB1":
var gi_cb1 = GameManager.GetGame("GI_CB1");
if (ext != gi_cb1.Extension)
goto default;
var gi_cb1File = new CB1File(reader);
bundles = gi_cb1File.Bundles;
break;
case "GI_CB2":
var gi_cb2 = GameManager.GetGame("GI_CB2");
if (ext != gi_cb2.Extension)
goto default;
Blk.ExpansionKey = Crypto.CBXExpansionKey;
Blk.SBox = null;
Blk.ConstKey = Crypto.CBXConstKey;
Blk.ConstVal = Crypto.CBXConstVal;
var gi_cb2File = new CBXFile(reader);
bundles = gi_cb2File.Bundles;
break;
case "GI_CB3":
var gi_cb3 = GameManager.GetGame("GI_CB3");
if (ext != gi_cb3.Extension)
goto default;
Blk.ExpansionKey = Crypto.CBXExpansionKey;
Blk.SBox = null;
Blk.ConstKey = Crypto.CBXConstKey;
Blk.ConstVal = Crypto.CBXConstVal;
var gi_cb3File = new CBXFile(reader);
bundles = gi_cb3File.Bundles;
break;
case "BH3":
var bh3 = GameManager.GetGame("BH3");
if (ext != bh3.Extension)
goto default;
Mr0k.ExpansionKey = Crypto.BH3ExpansionKey;
Mr0k.Key = Crypto.BH3Key;
Mr0k.ConstKey = Crypto.BH3ConstKey;
Mr0k.SBox = Crypto.BH3SBox;
Mr0k.BlockKey = null;
var wmvFile = new WMVFile(reader);
bundles = wmvFile.Bundles;
break;
case "ZZZ_CB1":
var zzz = GameManager.GetGame("ZZZ_CB1");
if (ext != zzz.Extension)
goto default;
Mr0k.ExpansionKey = Crypto.Mr0kExpansionKey;
Mr0k.Key = Crypto.Mr0kKey;
Mr0k.ConstKey = Crypto.Mr0kConstKey;
Mr0k.SBox = null;
Mr0k.BlockKey = null;
var zzzFile = new BundleFile(reader);
bundles.Add(0, zzzFile.FileList);
break;
case "SR_CB2":
var sr_cb2 = GameManager.GetGame("SR_CB2");
if (ext != sr_cb2.Extension)
goto default;
Mr0k.ExpansionKey = Crypto.Mr0kExpansionKey;
Mr0k.Key = Crypto.Mr0kKey;
Mr0k.ConstKey = Crypto.Mr0kConstKey;
Mr0k.SBox = null;
Mr0k.BlockKey = null;
var sr_cb2File = new BundleFile(reader);
bundles.Add(0, sr_cb2File.FileList);
break;
case "SR_CB3":
var sr_cb3 = GameManager.GetGame("SR_CB3");
if (ext != sr_cb3.Extension)
goto default;
Mr0k.ExpansionKey = Crypto.Mr0kExpansionKey;
Mr0k.Key = Crypto.Mr0kKey;
Mr0k.ConstKey = Crypto.Mr0kConstKey;
Mr0k.SBox = null;
Mr0k.BlockKey = null;
var sr_cb3File = new BlocksFile(reader);
bundles = sr_cb3File.Bundles;
break;
case "TOT":
var tot = GameManager.GetGame("TOT");
if (ext != tot.Extension)
goto default;
Mr0k.ExpansionKey = Crypto.Mr0kExpansionKey;
Mr0k.Key = Crypto.Mr0kKey;
Mr0k.ConstKey = Crypto.Mr0kConstKey;
Mr0k.SBox = null;
Mr0k.BlockKey = Crypto.ToTBlockKey;
var totFile = new TOTFile(reader);
bundles = totFile.Bundles;
break;
default:
throw new NotSupportedException("File not supported !!\nMake sure to select the right game before loading the file");
}
Bundles = bundles;
}
}
}

View File

@@ -1,30 +0,0 @@
using System.Collections.Generic;
namespace AssetStudio
{
public class BlocksFile
{
public Dictionary<long, StreamFile[]> Bundles = new Dictionary<long, StreamFile[]>();
public BlocksFile(FileReader reader)
{
if (reader.BundlePos.Length != 0)
{
foreach (var pos in reader.BundlePos)
{
reader.Position = pos;
var bundle = new BundleFile(reader);
Bundles.Add(pos, bundle.FileList);
}
}
else
{
while (reader.Position != reader.Length)
{
var pos = reader.Position;
var bundle = new BundleFile(reader);
Bundles.Add(pos, bundle.FileList);
}
}
}
}
}

View File

@@ -1,32 +0,0 @@
using System.Collections.Generic;
namespace AssetStudio
{
public class TOTFile
{
public Dictionary<long, StreamFile[]> Bundles = new Dictionary<long, StreamFile[]>();
public TOTFile(FileReader reader)
{
if (reader.BundlePos.Length != 0)
{
foreach (var pos in reader.BundlePos)
{
reader.Position = pos;
var bundle = new BundleFile(reader);
Bundles.Add(pos, bundle.FileList);
}
}
else
{
while (reader.Position != reader.Length)
{
var pos = reader.Position;
var bundle = new BundleFile(reader);
if (bundle.FileList == null)
continue;
Bundles.Add(pos, bundle.FileList);
}
}
}
}
}

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Collections.Generic; using System.Collections.Generic;
using static AssetStudio.Crypto;
namespace AssetStudio namespace AssetStudio
{ {
@@ -11,15 +11,22 @@ namespace AssetStudio
static GameManager() static GameManager()
{ {
int index = 0; int index = 0;
Games.Add(index++, new("GI", ".blk", "GI_Data|YS_Data")); Games.Add(index++, new(GameType.Normal));
Games.Add(index++, new("GI_CB1", ".asb", "GS_Data")); Games.Add(index++, new Game(GameType.CNUnity));
Games.Add(index++, new("GI_CB2", ".blk", "G_Data")); Games.Add(index++, new Mhy0(GameType.GI, GIMhy0ShiftRow, GIMhy0Key, GIMhy0Mul, GIExpansionKey, GISBox, GIInitVector, GIInitSeed));
Games.Add(index++, new("GI_CB3", ".blk", "YS_Data")); Games.Add(index++, new Mr0k(GameType.GI_Pack, PackExpansionKey, blockKey: PackBlockKey));
Games.Add(index++, new("BH3", ".wmv", "BH3_Data")); Games.Add(index++, new Mr0k(GameType.GI_CB1));
Games.Add(index++, new("ZZZ_CB1", ".bundle", "Win_Data/StreamingAssets/Bundles")); Games.Add(index++, new Blk(GameType.GI_CB2, GI_CBXExpansionKey, initVector: GI_CBXInitVector, initSeed: GI_CBXInitSeed));
Games.Add(index++, new("SR_CB2", ".unity3d", "SR_Data")); Games.Add(index++, new Blk(GameType.GI_CB3, GI_CBXExpansionKey, initVector: GI_CBXInitVector, initSeed: GI_CBXInitSeed));
Games.Add(index++, new("SR_CB3", ".block", "SR_Data")); Games.Add(index++, new Mhy0(GameType.GI_CB3Pre, GI_CBXMhy0ShiftRow, GI_CBXMhy0Key, GI_CBXMhy0Mul, GI_CBXExpansionKey, GI_CBXSBox, GI_CBXInitVector, GI_CBXInitSeed));
Games.Add(index++, new("TOT", ".blk", "AssetbundlesCache")); Games.Add(index++, new Mr0k(GameType.BH3, BH3ExpansionKey, BH3SBox, BH3InitVector, BH3BlockKey));
Games.Add(index++, new Mr0k(GameType.SR_CB2, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey));
Games.Add(index++, new Mr0k(GameType.SR_CB3, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey));
Games.Add(index++, new Mr0k(GameType.ZZZ_CB1, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey));
Games.Add(index++, new Mr0k(GameType.TOT, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey, postKey: ToTKey));
Games.Add(index++, new Game(GameType.Naraka));
Games.Add(index++, new Game(GameType.EnsembleStars));
Games.Add(index++, new Game(GameType.OPFP));
} }
public static Game GetGame(int index) public static Game GetGame(int index)
{ {
@@ -31,16 +38,7 @@ namespace AssetStudio
return format; return format;
} }
public static Game GetGame(string name) public static Game GetGame(string name) => Games.FirstOrDefault(x => x.Value.Name == name).Value;
{
foreach(var game in Games)
{
if (game.Value.Name == name)
return game.Value;
}
return null;
}
public static Game[] GetGames() => Games.Values.ToArray(); public static Game[] GetGames() => Games.Values.ToArray();
public static string[] GetGameNames() => Games.Values.Select(x => x.Name).ToArray(); public static string[] GetGameNames() => Games.Values.Select(x => x.Name).ToArray();
public static string SupportedGames() => $"Supported Games:\n{string.Join("\n", Games.Values.Select(x => x.Name))}"; public static string SupportedGames() => $"Supported Games:\n{string.Join("\n", Games.Values.Select(x => x.Name))}";
@@ -48,15 +46,131 @@ namespace AssetStudio
public record Game public record Game
{ {
public string Name; public string Name { get; set; }
public string Extension; public GameType Type { get; }
public string Path;
public Game(string name, string extension, string path) public Game(GameType type)
{ {
Name = name; Name = type.ToString();
Extension = extension; Type = type;
Path = path;
} }
public override string ToString() => Name;
public sealed override string ToString() => Name;
}
public record Mr0k : Game
{
public byte[] ExpansionKey { get; }
public byte[] SBox { get; }
public byte[] InitVector { get; }
public byte[] BlockKey { get; }
public byte[] PostKey { get; }
public Mr0k(GameType type, byte[] expansionKey = null, byte[] sBox = null, byte[] initVector = null, byte[] blockKey = null, byte[] postKey = null) : base(type)
{
ExpansionKey = expansionKey ?? Array.Empty<byte>();
SBox = sBox ?? Array.Empty<byte>();
InitVector = initVector ?? Array.Empty<byte>();
BlockKey = blockKey ?? Array.Empty<byte>();
PostKey = postKey ?? Array.Empty<byte>();
}
}
public record Blk : Game
{
public byte[] ExpansionKey { get; }
public byte[] SBox { get; }
public byte[] InitVector { get; }
public ulong InitSeed { get; }
public Blk(GameType type, byte[] expansionKey = null, byte[] sBox = null, byte[] initVector = null, ulong initSeed = 0) : base(type)
{
ExpansionKey = expansionKey ?? Array.Empty<byte>();
SBox = sBox ?? Array.Empty<byte>();
InitVector = initVector ?? Array.Empty<byte>();
InitSeed = initSeed;
}
}
public record Mhy0 : Blk
{
public byte[] Mhy0ShiftRow { get; }
public byte[] Mhy0Key { get; }
public byte[] Mhy0Mul { get; }
public Mhy0(GameType type, byte[] mhy0ShiftRow, byte[] mhy0Key, byte[] mhy0Mul, byte[] expansionKey = null, byte[] sBox = null, byte[] initVector = null, ulong initSeed = 0) : base(type, expansionKey, sBox, initVector, initSeed)
{
Mhy0ShiftRow = mhy0ShiftRow;
Mhy0Key = mhy0Key;
Mhy0Mul = mhy0Mul;
}
}
public enum GameType
{
Normal,
GI,
GI_Pack,
GI_CB1,
GI_CB2,
GI_CB3,
GI_CB3Pre,
BH3,
ZZZ_CB1,
SR_CB2,
SR_CB3,
TOT,
Naraka,
CNUnity,
EnsembleStars,
OPFP
}
public static class GameTypes
{
public static bool IsNormal(this GameType type) => type == GameType.Normal;
public static bool IsGI(this GameType type) => type == GameType.GI;
public static bool IsGIPack(this GameType type) => type == GameType.GI_Pack;
public static bool IsGICB1(this GameType type) => type == GameType.GI_CB1;
public static bool IsGICB2(this GameType type) => type == GameType.GI_CB2;
public static bool IsGICB3(this GameType type) => type == GameType.GI_CB3;
public static bool IsGICB3Pre(this GameType type) => type == GameType.GI_CB3Pre;
public static bool IsBH3(this GameType type) => type == GameType.BH3;
public static bool IsZZZCB1(this GameType type) => type == GameType.ZZZ_CB1;
public static bool IsSRCB2(this GameType type) => type == GameType.SR_CB2;
public static bool IsSRCB3(this GameType type) => type == GameType.SR_CB3;
public static bool IsTOT(this GameType type) => type == GameType.TOT;
public static bool IsNaraka(this GameType type) => type == GameType.Naraka;
public static bool IsCNUnity(this GameType type) => type == GameType.CNUnity;
public static bool IsOPFP(this GameType type) => type == GameType.OPFP;
public static bool IsGIGroup(this GameType type) => type switch
{
GameType.GI or GameType.GI_Pack or GameType.GI_CB1 or GameType.GI_CB2 or GameType.GI_CB3 or GameType.GI_CB3Pre => true,
_ => false,
};
public static bool IsGISubGroup(this GameType type) => type switch
{
GameType.GI or GameType.GI_CB2 or GameType.GI_CB3 or GameType.GI_CB3Pre => true,
_ => false,
};
public static bool IsSRGroup(this GameType type) => type switch
{
GameType.SR_CB2 or GameType.SR_CB3 => true,
_ => false,
};
public static bool IsBlockFile(this GameType type) => type switch
{
GameType.BH3 or GameType.SR_CB3 or GameType.GI_Pack or GameType.TOT => true,
_ => false,
};
public static bool IsMhyGroup(this GameType type) => type switch
{
GameType.GI or GameType.GI_Pack or GameType.GI_CB1 or GameType.GI_CB2 or GameType.GI_CB3 or GameType.GI_CB3Pre or GameType.BH3 or GameType.SR_CB2 or GameType.SR_CB3 or GameType.TOT => true,
_ => false,
};
} }
} }

View File

@@ -73,23 +73,6 @@ namespace AssetStudio
return null; return null;
} }
public ImportedFrame FindFrame(string name)
{
if (Name == name)
{
return this;
}
foreach (var child in children)
{
var frame = child.FindFrame(name);
if (frame != null)
{
return frame;
}
}
return null;
}
public ImportedFrame FindRelativeFrameWithPath(string path) public ImportedFrame FindRelativeFrameWithPath(string path)
{ {
var subs = path.Split(new[] { '/' }, 2); var subs = path.Split(new[] { '/' }, 2);
@@ -112,6 +95,23 @@ namespace AssetStudio
return null; return null;
} }
public ImportedFrame FindFrame(string name)
{
if (Name == name)
{
return this;
}
foreach (var child in children)
{
var frame = child.FindFrame(name);
if (frame != null)
{
return frame;
}
}
return null;
}
public ImportedFrame FindChild(string name, bool recursive = true) public ImportedFrame FindChild(string name, bool recursive = true)
{ {
foreach (var child in children) foreach (var child in children)

View File

@@ -16,18 +16,21 @@ namespace AssetStudio
public interface ILogger public interface ILogger
{ {
void Log(LoggerEvent loggerEvent, string message); void Log(LoggerEvent loggerEvent, string message, bool silent = false);
} }
public sealed class DummyLogger : ILogger public sealed class DummyLogger : ILogger
{ {
public void Log(LoggerEvent loggerEvent, string message) { } public void Log(LoggerEvent loggerEvent, string message, bool silent = false) { }
} }
public sealed class ConsoleLogger : ILogger public sealed class ConsoleLogger : ILogger
{ {
public void Log(LoggerEvent loggerEvent, string message) public void Log(LoggerEvent loggerEvent, string message, bool silent = false)
{ {
if (silent)
return;
Console.WriteLine("[{0}] {1}", loggerEvent, message); Console.WriteLine("[{0}] {1}", loggerEvent, message);
} }
} }

View File

@@ -1,7 +0,0 @@
namespace AssetStudio
{
public interface IYAMLExportable
{
YAMLNode ExportYAML();
}
}

View File

@@ -1,11 +1,62 @@
using Org.Brotli.Dec; using Org.Brotli.Dec;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq;
using static AssetStudio.BundleFile;
using static AssetStudio.Crypto;
namespace AssetStudio namespace AssetStudio
{ {
public static class ImportHelper public static class ImportHelper
{ {
public static void MergeSplitAssets(string path, bool allDirectories = false)
{
var splitFiles = Directory.GetFiles(path, "*.split0", allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
foreach (var splitFile in splitFiles)
{
var destFile = Path.GetFileNameWithoutExtension(splitFile);
var destPath = Path.GetDirectoryName(splitFile);
var destFull = Path.Combine(destPath, destFile);
if (!File.Exists(destFull))
{
var splitParts = Directory.GetFiles(destPath, destFile + ".split*");
using (var destStream = File.Create(destFull))
{
for (int i = 0; i < splitParts.Length; i++)
{
var splitPart = destFull + ".split" + i;
using (var sourceStream = File.OpenRead(splitPart))
{
sourceStream.CopyTo(destStream);
}
}
}
}
}
}
public static string[] ProcessingSplitFiles(List<string> selectFile)
{
var splitFiles = selectFile.Where(x => x.Contains(".split"))
.Select(x => Path.Combine(Path.GetDirectoryName(x), Path.GetFileNameWithoutExtension(x)))
.Distinct()
.ToList();
selectFile.RemoveAll(x => x.Contains(".split"));
foreach (var file in splitFiles)
{
if (File.Exists(file))
{
selectFile.Add(file);
}
}
return selectFile.Distinct().ToArray();
}
public static FileReader DecompressGZip(FileReader reader) public static FileReader DecompressGZip(FileReader reader)
{ {
using (reader) using (reader)
@@ -33,5 +84,210 @@ namespace AssetStudio
return new FileReader(reader.FullPath, stream); return new FileReader(reader.FullPath, stream);
} }
} }
public static FileReader DecryptPack(FileReader reader, Game game)
{
const int PackSize = 0x880;
const string PackSignature = "pack";
const string UnityFSSignature = "UnityFS";
var data = reader.ReadBytes((int)reader.Length);
var idx = data.Search(PackSignature);
if (idx == -1)
{
reader.Position = 0;
return reader;
}
idx = data.Search("mr0k", idx);
if (idx == -1)
{
reader.Position = 0;
return reader;
}
var ms = new MemoryStream();
try
{
var mr0k = (Mr0k)game;
var bundleSize = 0;
reader.Position = 0;
Header header = null;
var pack = new { IsMr0k = false, BlockSize = -1 };
while (reader.Remaining > 0)
{
var pos = reader.Position;
var signature = reader.ReadStringToNull(4);
if (signature == PackSignature)
{
pack = new {
IsMr0k = reader.ReadBoolean(),
BlockSize = BinaryPrimitives.ReadInt32LittleEndian(reader.ReadBytes(4))
};
Span<byte> buffer = new byte[pack.BlockSize];
reader.Read(buffer);
if (pack.IsMr0k)
{
buffer = Mr0kUtils.Decrypt(buffer, mr0k);
}
ms.Write(buffer);
bundleSize += buffer.Length;
if (header == null)
{
using var blockReader = new EndianBinaryReader(new MemoryStream(buffer.ToArray()));
header = new Header()
{
signature = blockReader.ReadStringToNull(),
version = blockReader.ReadUInt32(),
unityVersion = blockReader.ReadStringToNull(),
unityRevision = blockReader.ReadStringToNull(),
size = blockReader.ReadInt64()
};
}
if (bundleSize % (PackSize - 0x80) == 0)
{
reader.Position += PackSize - 9 - pack.BlockSize;
}
if (bundleSize == header.size)
{
bundleSize = 0;
header = null;
pack = new { IsMr0k = false, BlockSize = -1 };
}
continue;
}
reader.Position = pos;
signature = reader.ReadStringToNull();
if (signature == UnityFSSignature)
{
header = new Header()
{
signature = reader.ReadStringToNull(),
version = reader.ReadUInt32(),
unityVersion = reader.ReadStringToNull(),
unityRevision = reader.ReadStringToNull(),
size = reader.ReadInt64()
};
reader.Position = pos;
reader.BaseStream.CopyTo(ms, header.size);
header = null;
continue;
}
throw new InvalidOperationException($"Expected signature {PackSignature} or {UnityFSSignature}, got {signature} instead !!");
}
}
catch (InvalidCastException)
{
Logger.Error($"Game type mismatch, Expected {nameof(GameType.GI_Pack)} ({nameof(Mr0k)}) but got {game.Name} ({game.GetType().Name}) !!");
}
catch (Exception e)
{
Logger.Error($"Error while reading pack file {reader.FullPath}", e);
}
finally
{
reader.Dispose();
}
ms.Position = 0;
return new FileReader(reader.FullPath, ms);
}
public static FileReader DecryptMark(FileReader reader)
{
var signature = reader.ReadStringToNull(4);
if (signature != "mark")
{
reader.Position = 0;
return reader;
}
const int BlockSize = 0xA00;
const int ChunkSize = 0x264;
const int ChunkPadding = 4;
var blockPadding = ((BlockSize / ChunkSize) + 1) * ChunkPadding;
var chunkSizeWithPadding = ChunkSize + ChunkPadding;
var blockSizeWithPadding = BlockSize + blockPadding;
var index = 0;
var block = new byte[blockSizeWithPadding];
var chunk = new byte[chunkSizeWithPadding];
var dataStream = new MemoryStream();
while (reader.BaseStream.Length != reader.BaseStream.Position)
{
var readBlockBytes = reader.Read(block);
using var blockStream = new MemoryStream(block, 0, readBlockBytes);
while (blockStream.Length != blockStream.Position)
{
var readChunkBytes = blockStream.Read(chunk);
if (readBlockBytes == blockSizeWithPadding || readChunkBytes == chunkSizeWithPadding)
{
readChunkBytes -= ChunkPadding;
}
for (int i = 0; i < readChunkBytes; i++)
{
chunk[i] ^= MarkKey[index++ % MarkKey.Length];
}
dataStream.Write(chunk, 0, readChunkBytes);
}
}
reader.Dispose();
dataStream.Position = 0;
return new FileReader(reader.FullPath, dataStream);
}
public static FileReader DecryptEnsembleStar(FileReader reader)
{
if (Path.GetExtension(reader.FileName) == ".z")
{
return reader;
}
using (reader)
{
var data = reader.ReadBytes((int)reader.Length);
var count = data.Length;
var stride = count % 3 + 1;
var remaining = count % 7;
var size = remaining + ~(count % 3) + EnsembleStarKey2.Length;
for (int i = 0; i < count; i += stride)
{
var offset = i / stride;
var k1 = offset % EnsembleStarKey1.Length;
var k2 = offset % EnsembleStarKey2.Length;
var k3 = offset % EnsembleStarKey3.Length;
data[i] = (byte)(EnsembleStarKey1[k1] ^ ((size ^ EnsembleStarKey3[k3] ^ data[i] ^ EnsembleStarKey2[k2]) + remaining));
}
return new FileReader(reader.FullPath, new MemoryStream(data));
}
}
public static FileReader ParseOPFP(FileReader reader)
{
MemoryStream ms = new();
var data = reader.ReadBytes((int)reader.BaseStream.Length);
var idx = data.Search("UnityFS");
if (idx != -1)
{
var count = data.Length - idx;
ms = new(data, idx, count);
}
return new FileReader(reader.FullPath, ms);
}
} }
} }

38
AssetStudio/Keys.json Normal file
View File

@@ -0,0 +1,38 @@
[
{
"Name": "PGR GLB/KR",
"Key": "kurokurokurokuro"
},
{
"Name": "PGR CN/JP/TW",
"Key": "y5XPvqLOrCokWRIa"
},
{
"Name": "Archeland/Kalpa of Universe",
"Key": "BlackJackProject"
},
{
"Name": "Archeland 1.1.14",
"Key": "ProjectArcheLand"
},
{
"Name": "Neural Cloud",
"Key": "1cab846f52901c9e"
},
{
"Name": "Higan: Eruthyll",
"Key": "E1x2c3a4l5i6b7ur"
},
{
"Name": "White Chord",
"Key": "yulong1868gnoluy"
},
{
"Name": "Mecharashi",
"Key": "38C83F132E7F7A0A"
},
{
"Name": "Castlevania: Moon Night Fantasy",
"Key": "1234567812345678"
}
]

View File

@@ -8,19 +8,20 @@ namespace AssetStudio
public static class Logger public static class Logger
{ {
public static ILogger Default = new DummyLogger(); public static ILogger Default = new DummyLogger();
public static bool Silent = false;
public static void Verbose(string message) => Default.Log(LoggerEvent.Verbose, message); public static void Verbose(string message) => Default.Log(LoggerEvent.Verbose, message, Silent);
public static void Debug(string message) => Default.Log(LoggerEvent.Debug, message); public static void Debug(string message) => Default.Log(LoggerEvent.Debug, message, Silent);
public static void Info(string message) => Default.Log(LoggerEvent.Info, message); public static void Info(string message) => Default.Log(LoggerEvent.Info, message, Silent);
public static void Warning(string message) => Default.Log(LoggerEvent.Warning, message); public static void Warning(string message) => Default.Log(LoggerEvent.Warning, message, Silent);
public static void Error(string message) => Default.Log(LoggerEvent.Error, message); public static void Error(string message) => Default.Log(LoggerEvent.Error, message, Silent);
public static void Error(string message, Exception e) public static void Error(string message, Exception e)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine(message); sb.AppendLine(message);
sb.AppendLine(e.ToString()); sb.AppendLine(e.ToString());
Default.Log(LoggerEvent.Error, sb.ToString()); Default.Log(LoggerEvent.Error, sb.ToString(), Silent);
} }
} }
} }

View File

@@ -1,27 +0,0 @@
namespace AssetStudio
{
public struct Float : IYAMLExportable
{
public float Value;
public Float(float value)
{
Value = value;
}
public static implicit operator Float(float value)
{
return new Float(value);
}
public static implicit operator float(Float value)
{
return value.Value;
}
public YAMLNode ExportYAML()
{
return new YAMLScalarNode(Value);
}
}
}

View File

@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace AssetStudio namespace AssetStudio
{ {
[StructLayout(LayoutKind.Sequential, Pack = 4)] [StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct Quaternion : IEquatable<Quaternion>, IYAMLExportable public struct Quaternion : IEquatable<Quaternion>
{ {
public float X; public float X;
public float Y; public float Y;
@@ -83,17 +83,6 @@ namespace AssetStudio
return !(lhs == rhs); return !(lhs == rhs);
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Style = MappingStyle.Flow;
node.Add("x", X);
node.Add("y", Y);
node.Add("z", Z);
node.Add("w", W);
return node;
}
private const float kEpsilon = 0.000001F; private const float kEpsilon = 0.000001F;
} }
} }

View File

@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace AssetStudio namespace AssetStudio
{ {
[StructLayout(LayoutKind.Sequential, Pack = 4)] [StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct Vector3 : IEquatable<Vector3>, IYAMLExportable public struct Vector3 : IEquatable<Vector3>
{ {
public float X; public float X;
public float Y; public float Y;
@@ -87,16 +87,6 @@ namespace AssetStudio
return X * X + Y * Y + Z * Z; return X * X + Y * Y + Z * Z;
} }
public YAMLNode ExportYAML()
{
var node = new YAMLMappingNode();
node.Style = MappingStyle.Flow;
node.Add("x", X);
node.Add("y", Y);
node.Add("z", Z);
return node;
}
public static Vector3 Zero => new Vector3(); public static Vector3 Zero => new Vector3();
public static Vector3 One => new Vector3(1.0f, 1.0f, 1.0f); public static Vector3 One => new Vector3(1.0f, 1.0f, 1.0f);

197
AssetStudio/Mhy0File.cs Normal file
View File

@@ -0,0 +1,197 @@
using System;
using System.IO;
using System.Linq;
using K4os.Compression.LZ4;
namespace AssetStudio
{
public class Mhy0File
{
private BundleFile.StorageBlock[] m_BlocksInfo;
private BundleFile.Node[] m_DirectoryInfo;
public BundleFile.Header m_Header;
public StreamFile[] fileList;
public long Offset;
public Mhy0 mhy0;
public Mhy0File(FileReader reader, string path, Mhy0 mhy0)
{
this.mhy0 = mhy0;
Offset = reader.Position;
reader.Endian = EndianType.LittleEndian;
var signature = reader.ReadStringToNull(4);
m_Header = new BundleFile.Header
{
version = 6,
unityVersion = "5.x.x",
unityRevision = "2017.4.30f1",
compressedBlocksInfoSize = reader.ReadUInt32(),
flags = (ArchiveFlags)0x43
};
ReadBlocksInfoAndDirectory(reader);
using var blocksStream = CreateBlocksStream(path);
ReadBlocks(reader, blocksStream);
ReadFiles(blocksStream, path);
}
private void ReadBlocksInfoAndDirectory(FileReader reader)
{
var blocksInfo = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
DescrambleHeader(blocksInfo);
using var blocksInfoStream = new MemoryStream(blocksInfo, 0x20, (int)m_Header.compressedBlocksInfoSize - 0x20);
using var blocksInfoReader = new EndianBinaryReader(blocksInfoStream);
m_Header.uncompressedBlocksInfoSize = blocksInfoReader.ReadMhy0UInt();
var compressedBlocksInfo = blocksInfoReader.ReadBytes((int)blocksInfoReader.Remaining);
var uncompressedBlocksInfo = new byte[(int)m_Header.uncompressedBlocksInfoSize];
var numWrite = LZ4Codec.Decode(compressedBlocksInfo, uncompressedBlocksInfo);
if (numWrite != m_Header.uncompressedBlocksInfoSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {m_Header.uncompressedBlocksInfoSize} bytes");
}
using var blocksInfoUncompressedStream = new MemoryStream(uncompressedBlocksInfo);
using var blocksInfoUncompressedReader = new EndianBinaryReader(blocksInfoUncompressedStream);
var nodesCount = blocksInfoUncompressedReader.ReadMhy0Int();
m_DirectoryInfo = new BundleFile.Node[nodesCount];
for (int i = 0; i < nodesCount; i++)
{
m_DirectoryInfo[i] = new BundleFile.Node
{
path = blocksInfoUncompressedReader.ReadMhy0String(),
flags = blocksInfoUncompressedReader.ReadBoolean() ? 4u : 0,
offset = blocksInfoUncompressedReader.ReadMhy0Int(),
size = blocksInfoUncompressedReader.ReadMhy0UInt()
};
}
var blocksInfoCount = blocksInfoUncompressedReader.ReadMhy0Int();
m_BlocksInfo = new BundleFile.StorageBlock[blocksInfoCount];
for (int i = 0; i < blocksInfoCount; i++)
{
m_BlocksInfo[i] = new BundleFile.StorageBlock
{
compressedSize = (uint)blocksInfoUncompressedReader.ReadMhy0Int(),
uncompressedSize = blocksInfoUncompressedReader.ReadMhy0UInt(),
flags = (StorageBlockFlags)0x43
};
}
}
private Stream CreateBlocksStream(string path)
{
Stream blocksStream;
var uncompressedSizeSum = (int)m_BlocksInfo.Sum(x => x.uncompressedSize);
if (uncompressedSizeSum >= int.MaxValue)
blocksStream = new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose);
else
blocksStream = new MemoryStream(uncompressedSizeSum);
return blocksStream;
}
private void ReadBlocks(EndianBinaryReader reader, Stream blocksStream)
{
foreach (var blockInfo in m_BlocksInfo)
{
var compressedSize = (int)blockInfo.compressedSize;
var uncompressedSize = (int)blockInfo.uncompressedSize;
if (compressedSize < 0x10)
{
throw new Exception($"Wrong compressed length: {compressedSize}");
}
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
reader.Read(compressedBytes, 0, compressedSize);
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
DescrambleEntry(compressedBytesSpan);
var numWrite = LZ4Codec.Decode(compressedBytesSpan[0xC..compressedSize], uncompressedBytesSpan);
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
}
}
private void ReadFiles(Stream blocksStream, string path)
{
fileList = new StreamFile[m_DirectoryInfo.Length];
for (int i = 0; i < m_DirectoryInfo.Length; i++)
{
var node = m_DirectoryInfo[i];
var file = new StreamFile();
fileList[i] = file;
file.path = node.path;
file.fileName = Path.GetFileName(node.path);
if (node.size >= int.MaxValue)
{
var extractPath = path + "_unpacked" + Path.DirectorySeparatorChar;
Directory.CreateDirectory(extractPath);
file.stream = new FileStream(extractPath + file.fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
}
else
file.stream = new MemoryStream((int)node.size);
blocksStream.Position = node.offset;
blocksStream.CopyTo(file.stream, node.size);
file.stream.Position = 0;
}
}
#region Scramble
private static readonly byte[] GF256Exp = new byte[] { 0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72, 0x96, 0xA1, 0xF8, 0x13, 0x35, 0x5F, 0xE1, 0x38, 0x48, 0xD8, 0x73, 0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, 0xE5, 0x34, 0x5C, 0xE4, 0x37, 0x59, 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70, 0x90, 0xAB, 0xE6, 0x31, 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44, 0xCC, 0x4F, 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, 0x4C, 0xD4, 0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, 0x18, 0x28, 0x78, 0x88, 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F, 0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, 0x9A, 0xB5, 0xC4, 0x57, 0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6, 0x61, 0xA3, 0xFE, 0x19, 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F, 0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, 0xFB, 0x16, 0x3A, 0x4E, 0xD2, 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F, 0x41, 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, 0x5B, 0xED, 0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, 0x9F, 0xBA, 0xD5, 0x64, 0xAC, 0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, 0xDF, 0x7A, 0x8E, 0x89, 0x80, 0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F, 0xB1, 0xC8, 0x43, 0xC5, 0x54, 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4, 0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, 0x45, 0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42, 0xC6, 0x51, 0xF3, 0x0E, 0x12, 0x36, 0x5A, 0xEE, 0x29, 0x7B, 0x8D, 0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, 0x39, 0x4B, 0xDD, 0x7C, 0x84, 0x97, 0xA2, 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7, 0x52, 0xF6, 0x01 };
private static readonly byte[] GF256Log = new byte[] { 0x00, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B, 0x68, 0x33, 0xEE, 0xDF, 0x03, 0x64, 0x04, 0xE0, 0x0E, 0x34, 0x8D, 0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, 0x7D, 0xC2, 0x1D, 0xB5, 0xF9, 0xB9, 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72, 0x9A, 0xC9, 0x09, 0x78, 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1, 0x24, 0x12, 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, 0x96, 0x8F, 0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, 0x40, 0x46, 0x83, 0x38, 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62, 0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, 0x10, 0x7E, 0x6E, 0x48, 0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85, 0x3D, 0xBA, 0x2B, 0x79, 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E, 0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, 0xAF, 0x58, 0xA8, 0x50, 0xF4, 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD, 0xE8, 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, 0x59, 0xCB, 0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, 0x7F, 0x0C, 0xF6, 0x6F, 0x17, 0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, 0x2D, 0xA4, 0x76, 0x7B, 0xB7, 0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1, 0x6C, 0xAA, 0x55, 0x29, 0x9D, 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE, 0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, 0x53, 0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D, 0x56, 0xF2, 0xD3, 0xAB, 0x44, 0x11, 0x92, 0xD9, 0x23, 0x20, 0x2E, 0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, 0x67, 0x4A, 0xED, 0xDE, 0xC5, 0x31, 0xFE, 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0, 0xF7, 0x70, 0x07 };
private static int GF256Mul(int a, int b) => (a == 0 || b == 0) ? 0 : GF256Exp[(GF256Log[a] + GF256Log[b]) % 0xFF];
private void DescrambleChunk(Span<byte> input)
{
byte[] vector = new byte[0x10];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 0x10; j++)
{
int k = mhy0.Mhy0ShiftRow[(2 - i) * 0x10 + j];
int idx = j % 8;
vector[j] = (byte)(mhy0.Mhy0Key[idx] ^ mhy0.SBox[(j % 4 * 0x100) | GF256Mul(mhy0.Mhy0Mul[idx], input[k])]);
}
vector.CopyTo(input);
}
}
private void Descramble(Span<byte> input, int blockSize, int entrySize)
{
var roundedEntrySize = (entrySize + 0xF) / 0x10 * 0x10;
for (int i = 0; i < roundedEntrySize; i += 0x10)
DescrambleChunk(input.Slice(i + 4, 0x10));
for (int i = 0; i < 4; i++)
input[i] ^= input[i + 4];
var currentEntry = roundedEntrySize + 4;
var finished = false;
while (currentEntry < blockSize && !finished)
{
for (int i = 0; i < entrySize; i++)
{
input[i + currentEntry] ^= input[i + 4];
if (i + currentEntry >= blockSize - 1)
{
finished = true;
break;
}
}
currentEntry += entrySize;
}
}
public void DescrambleHeader(Span<byte> input) => Descramble(input, 0x39, 0x1C);
public void DescrambleEntry(Span<byte> input) => Descramble(input, Math.Min(input.Length, 0x21), 8);
#endregion
}
}

View File

@@ -1,7 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
namespace AssetStudio namespace AssetStudio
{ {
@@ -17,35 +15,19 @@ namespace AssetStudio
public long m_PathID; public long m_PathID;
public SerializedType serializedType; public SerializedType serializedType;
public bool HasExportableType() public static List<ObjectInfo> Filter(List<ObjectInfo> objects) => objects.Where(x => x.IsExportableType()).OrderBy(x => ExportableTypes.IndexOf((ClassIDType)x.typeID)).ToList();
private bool IsExportableType()
{ {
var typeID = (ClassIDType)classID; var typeID = (ClassIDType)classID;
var isExportableType = ExportableTypes.Contains(typeID); var isExportableType = ExportableTypes.Contains(typeID);
switch (typeID) return typeID switch
{ {
case ClassIDType.IndexObject: ClassIDType.IndexObject or ClassIDType.MiHoYoBinData => isExportableType && MiHoYoBinData.Exportable,
case ClassIDType.MiHoYoBinData: _ => isExportableType,
return isExportableType && IndexObject.Exportable; };
default:
return isExportableType;
}
} }
public static ClassIDType[] ExportableTypes = new ClassIDType[] private readonly static List<ClassIDType> ExportableTypes = new List<ClassIDType> { ClassIDType.GameObject, ClassIDType.IndexObject, ClassIDType.Material, ClassIDType.Texture2D, ClassIDType.Mesh, ClassIDType.Shader, ClassIDType.TextAsset, ClassIDType.AnimationClip, ClassIDType.Font, ClassIDType.Sprite, ClassIDType.Animator, ClassIDType.MiHoYoBinData, ClassIDType.AssetBundle };
{
ClassIDType.GameObject,
ClassIDType.Material,
ClassIDType.Texture2D,
ClassIDType.Mesh,
ClassIDType.Shader,
ClassIDType.TextAsset,
ClassIDType.AnimationClip,
ClassIDType.Animator,
ClassIDType.Font,
ClassIDType.AssetBundle,
ClassIDType.Sprite,
ClassIDType.MiHoYoBinData,
ClassIDType.IndexObject
};
} }
} }

View File

@@ -9,6 +9,7 @@ namespace AssetStudio
public class ObjectReader : EndianBinaryReader public class ObjectReader : EndianBinaryReader
{ {
public SerializedFile assetsFile; public SerializedFile assetsFile;
public Game Game;
public long m_PathID; public long m_PathID;
public long byteStart; public long byteStart;
public uint byteSize; public uint byteSize;
@@ -20,10 +21,10 @@ namespace AssetStudio
public int[] version => assetsFile.version; public int[] version => assetsFile.version;
public BuildType buildType => assetsFile.buildType; public BuildType buildType => assetsFile.buildType;
public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo) : base(reader.BaseStream, reader.Endian) public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo, Game game) : base(reader.BaseStream, reader.Endian)
{ {
this.assetsFile = assetsFile; this.assetsFile = assetsFile;
Game = reader.Game; Game = game;
m_PathID = objectInfo.m_PathID; m_PathID = objectInfo.m_PathID;
byteStart = objectInfo.byteStart; byteStart = objectInfo.byteStart;
byteSize = objectInfo.byteSize; byteSize = objectInfo.byteSize;

View File

@@ -3,19 +3,26 @@ namespace AssetStudio
{ {
public static class Progress public static class Progress
{ {
public static bool Silent = false;
public static IProgress<int> Default = new Progress<int>(); public static IProgress<int> Default = new Progress<int>();
private static int preValue; private static int preValue;
public static void Reset() public static void Reset()
{ {
preValue = 0; if (!Silent)
Default.Report(0); {
preValue = 0;
Default.Report(0);
}
} }
public static void Report(int current, int total) public static void Report(int current, int total)
{ {
var value = (int)(current * 100f / total); if (!Silent)
Report(value); {
var value = (int)(current * 100f / total);
Report(value);
}
} }
private static void Report(int value) private static void Report(int value)

View File

@@ -82,10 +82,8 @@ namespace AssetStudio
foreach (var subAsset in asset.Value) foreach (var subAsset in asset.Value)
{ {
var bundleInfo = new BundleInfo() { Bundle = asset.Key, Path = subAsset.Name }; var bundleInfo = new BundleInfo() { Bundle = asset.Key, Path = subAsset.Name };
var blockInfo = assetIndex.Assets[asset.Key];
ulong key = (((ulong)blockInfo.Id) << 32) | subAsset.PathHashLast;
AssetLocationMap[subAsset.PathHashPre].Add(subAsset.PathHashLast, bundleInfo); AssetLocationMap[subAsset.PathHashPre].Add(subAsset.PathHashLast, bundleInfo);
AssetMap[key] = ((ulong)subAsset.PathHashLast) << 8 | subAsset.PathHashPre; AssetMap[subAsset.PathHashLast] = ((ulong)subAsset.PathHashLast) << 8 | subAsset.PathHashPre;
} }
} }
foreach (var asset in assetIndex.Assets) foreach (var asset in assetIndex.Assets)
@@ -112,7 +110,7 @@ namespace AssetStudio
return bundleInfo; return bundleInfo;
return null; return null;
} }
public static string GetBundlePath(uint last) public static string GetAssetPath(uint last)
{ {
foreach (var location in AssetLocationMap) foreach (var location in AssetLocationMap)
if (location.TryGetValue(last, out var bundleInfo)) if (location.TryGetValue(last, out var bundleInfo))
@@ -150,16 +148,6 @@ namespace AssetStudio
var asset = new Asset() { Hash = hash }; var asset = new Asset() { Hash = hash };
return AssetLocationMap.ElementAtOrDefault(asset.Pre).ContainsKey(asset.Last); return AssetLocationMap.ElementAtOrDefault(asset.Pre).ContainsKey(asset.Last);
} }
public static string GetContainerFromBinName(string fileName, string binName)
{
var blkName = Path.GetFileNameWithoutExtension(fileName);
var blk = Convert.ToUInt64(blkName);
var lastHex = Convert.ToUInt32(binName, 16);
var blkHash = (blk << 32) | lastHex;
var index = GetAssetIndex(blkHash);
var bundleInfo = GetBundleInfo(index);
return bundleInfo != null ? bundleInfo.Path : "";
}
} }
public class BundleInfo public class BundleInfo
{ {

View File

@@ -11,6 +11,8 @@ namespace AssetStudio
{ {
public AssetsManager assetsManager; public AssetsManager assetsManager;
public FileReader reader; public FileReader reader;
public Game game;
public long offset = 0;
public string fullName; public string fullName;
public string originalPath; public string originalPath;
public string fileName; public string fileName;
@@ -32,11 +34,11 @@ namespace AssetStudio
public List<SerializedType> m_RefTypes; public List<SerializedType> m_RefTypes;
public string userInformation; public string userInformation;
public SerializedFile(FileReader reader, AssetsManager assetsManager, string path = null) public SerializedFile(FileReader reader, AssetsManager assetsManager)
{ {
this.assetsManager = assetsManager; this.assetsManager = assetsManager;
this.reader = reader; this.reader = reader;
originalPath = path; game = assetsManager.Game;
fullName = reader.FullPath; fullName = reader.FullPath;
fileName = reader.FileName; fileName = reader.FileName;
@@ -80,11 +82,14 @@ namespace AssetStudio
if (header.m_Version >= SerializedFileFormatVersion.Unknown_8) if (header.m_Version >= SerializedFileFormatVersion.Unknown_8)
{ {
m_TargetPlatform = (BuildTarget)reader.ReadInt32(); m_TargetPlatform = (BuildTarget)reader.ReadInt32();
if (m_TargetPlatform == BuildTarget.NoTarget) m_TargetPlatform = BuildTarget.StandaloneWindows64;
if (!Enum.IsDefined(typeof(BuildTarget), m_TargetPlatform)) if (!Enum.IsDefined(typeof(BuildTarget), m_TargetPlatform))
{ {
m_TargetPlatform = BuildTarget.UnknownPlatform; m_TargetPlatform = BuildTarget.UnknownPlatform;
} }
else if (game.Type.IsMhyGroup())
{
m_TargetPlatform = BuildTarget.StandaloneWindows64;
}
} }
if (header.m_Version >= SerializedFileFormatVersion.HasTypeTreeHashes) if (header.m_Version >= SerializedFileFormatVersion.HasTypeTreeHashes)
{ {
@@ -197,7 +202,7 @@ namespace AssetStudio
m_External.guid = new Guid(reader.ReadBytes(16)); m_External.guid = new Guid(reader.ReadBytes(16));
m_External.type = reader.ReadInt32(); m_External.type = reader.ReadInt32();
} }
m_External.pathName = reader.ReadStringToNull().Replace("cab", "CAB"); m_External.pathName = reader.ReadStringToNull();
m_External.fileName = Path.GetFileName(m_External.pathName); m_External.fileName = Path.GetFileName(m_External.pathName);
m_Externals.Add(m_External); m_Externals.Add(m_External);
} }
@@ -237,7 +242,8 @@ namespace AssetStudio
var type = new SerializedType(); var type = new SerializedType();
type.classID = reader.ReadInt32(); type.classID = reader.ReadInt32();
if (BitConverter.ToBoolean(header.m_Reserved, 0))
if (game.Type.IsGIGroup() && BitConverter.ToBoolean(header.m_Reserved))
{ {
type.classID = DecodeClassID(type.classID); type.classID = DecodeClassID(type.classID);
} }
@@ -348,7 +354,7 @@ namespace AssetStudio
} }
m_Type.m_StringBuffer = reader.ReadBytes(stringBufferSize); m_Type.m_StringBuffer = reader.ReadBytes(stringBufferSize);
using (var stringBufferReader = new BinaryReader(new MemoryStream(m_Type.m_StringBuffer))) using (var stringBufferReader = new EndianBinaryReader(new MemoryStream(m_Type.m_StringBuffer), EndianType.LittleEndian))
{ {
for (int i = 0; i < numberOfNodes; i++) for (int i = 0; i < numberOfNodes; i++)
{ {
@@ -358,7 +364,7 @@ namespace AssetStudio
} }
} }
string ReadString(BinaryReader stringBufferReader, uint value) string ReadString(EndianBinaryReader stringBufferReader, uint value)
{ {
var isOffset = (value & 0x80000000) == 0; var isOffset = (value & 0x80000000) == 0;
if (isOffset) if (isOffset)
@@ -381,12 +387,14 @@ namespace AssetStudio
ObjectsDic.Add(obj.m_PathID, obj); ObjectsDic.Add(obj.m_PathID, obj);
} }
private int DecodeClassID(int value) private static int DecodeClassID(int value)
{ {
var bytes = BitConverter.GetBytes(value); var bytes = BitConverter.GetBytes(value);
value = BinaryPrimitives.ReadInt32BigEndian(bytes); Array.Reverse(bytes);
value = BitConverter.ToInt32(bytes, 0);
return (value ^ 0x23746FBE) - 3; return (value ^ 0x23746FBE) - 3;
} }
public bool IsVersionStripped => unityVersion == strippedVersion; public bool IsVersionStripped => unityVersion == strippedVersion;
private const string strippedVersion = "0.0.0"; private const string strippedVersion = "0.0.0";

View File

@@ -25,7 +25,7 @@ namespace AssetStudio
return sb.ToString(); return sb.ToString();
} }
private static void ReadStringValue(StringBuilder sb, List<TypeTreeNode> m_Nodes, BinaryReader reader, ref int i) private static void ReadStringValue(StringBuilder sb, List<TypeTreeNode> m_Nodes, EndianBinaryReader reader, ref int i)
{ {
var m_Node = m_Nodes[i]; var m_Node = m_Nodes[i];
var level = m_Node.m_Level; var level = m_Node.m_Level;
@@ -182,7 +182,7 @@ namespace AssetStudio
return obj; return obj;
} }
private static object ReadValue(List<TypeTreeNode> m_Nodes, BinaryReader reader, ref int i) private static object ReadValue(List<TypeTreeNode> m_Nodes, EndianBinaryReader reader, ref int i)
{ {
var m_Node = m_Nodes[i]; var m_Node = m_Nodes[i];
var varTypeStr = m_Node.m_Type; var varTypeStr = m_Node.m_Type;

View File

@@ -0,0 +1,68 @@
using System;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Reflection;
namespace AssetStudio
{
public static class CNUnityKeyManager
{
public const string KeysFileName = "Keys.json";
private static List<CNUnity.Entry> Entries = new List<CNUnity.Entry>();
static CNUnityKeyManager()
{
var str = File.ReadAllText(KeysFileName);
Entries = JsonConvert.DeserializeObject<List<CNUnity.Entry>>(str);
}
public static void SaveEntries(List<CNUnity.Entry> entries)
{
Entries.Clear();
Entries.AddRange(entries);
var str = JsonConvert.SerializeObject(Entries);
File.WriteAllText(KeysFileName, str);
}
public static void SetKey(int index)
{
if (TryGetEntry(index, out var cnunity))
{
if (CNUnity.SetKey(cnunity))
{
Logger.Info($"[CNUnity] Selected Key is {cnunity}");
}
else
{
Logger.Info($"[CNUnity] No Key is selected !!");
}
}
}
public static bool TryGetEntry(int index, out CNUnity.Entry key)
{
try
{
if (index < 0 || index > Entries.Count)
{
throw new ArgumentOutOfRangeException();
}
key = Entries[index];
}
catch(Exception e)
{
Logger.Error($"[CNUnity] Invalid Index, check if list is not empty !!\n{e.Message}");
key = null;
return false;
}
return true;
}
public static CNUnity.Entry[] GetEntries() => Entries.ToArray();
}
}

View File

@@ -1,231 +0,0 @@
using System;
using System.IO;
using System.Text;
namespace AssetStudio
{
internal class Emitter
{
public Emitter(TextWriter writer, bool formatKeys)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
m_stream = writer;
IsFormatKeys = formatKeys;
if (formatKeys)
{
m_sb = new StringBuilder();
}
}
public Emitter IncreaseIndent()
{
m_indent++;
return this;
}
public Emitter DecreaseIndent()
{
if (m_indent == 0)
{
throw new Exception($"Increase/decrease indent mismatch");
}
m_indent--;
return this;
}
public Emitter Write(char value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter WriteRaw(char value)
{
m_stream.Write(value);
return this;
}
public Emitter Write(byte value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(ushort value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(short value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(uint value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(int value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(ulong value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(long value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(float value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(double value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(string value)
{
if (value.Length > 0)
{
WriteDelayed();
m_stream.Write(value);
}
return this;
}
public Emitter WriteFormat(string value)
{
if (value.Length > 0)
{
WriteDelayed();
if (value.Length > 2 && value.StartsWith("m_", StringComparison.Ordinal))
{
m_sb.Append(value, 2, value.Length - 2);
if (char.IsUpper(m_sb[0]))
{
m_sb[0] = char.ToLower(m_sb[0]);
}
value = m_sb.ToString();
m_sb.Clear();
}
m_stream.Write(value);
}
return this;
}
public Emitter WriteRaw(string value)
{
m_stream.Write(value);
return this;
}
public Emitter WriteClose(char @char)
{
m_isNeedSeparator = false;
m_isNeedWhitespace = false;
m_isNeedLineBreak = false;
return Write(@char);
}
public Emitter WriteClose(string @string)
{
m_isNeedSeparator = false;
m_isNeedWhitespace = false;
return Write(@string);
}
public Emitter WriteWhitespace()
{
m_isNeedWhitespace = true;
return this;
}
public Emitter WriteSeparator()
{
m_isNeedSeparator = true;
return this;
}
public Emitter WriteLine()
{
m_isNeedLineBreak = true;
return this;
}
public void WriteMeta(MetaType type, string value)
{
Write('%').Write(type.ToString()).WriteWhitespace();
Write(value).WriteLine();
}
public void WriteDelayed()
{
if (m_isNeedLineBreak)
{
m_stream.Write('\n');
m_isNeedSeparator = false;
m_isNeedWhitespace = false;
m_isNeedLineBreak = false;
WriteIndent();
}
if (m_isNeedSeparator)
{
m_stream.Write(',');
m_isNeedSeparator = false;
}
if (m_isNeedWhitespace)
{
m_stream.Write(' ');
m_isNeedWhitespace = false;
}
}
private void WriteIndent()
{
for (int i = 0; i < m_indent * 2; i++)
{
m_stream.Write(' ');
}
}
public bool IsFormatKeys { get; }
public bool IsKey { get; set; }
private readonly TextWriter m_stream;
private readonly StringBuilder m_sb;
private int m_indent = 0;
private bool m_isNeedWhitespace = false;
private bool m_isNeedSeparator = false;
private bool m_isNeedLineBreak = false;
}
}

View File

@@ -1,18 +0,0 @@
namespace AssetStudio
{
/// <summary>
/// Specifies the style of a mapping.
/// </summary>
public enum MappingStyle
{
/// <summary>
/// The block mapping style.
/// </summary>
Block,
/// <summary>
/// The flow mapping style.
/// </summary>
Flow
}
}

View File

@@ -1,8 +0,0 @@
namespace AssetStudio
{
internal enum MetaType
{
YAML,
TAG,
}
}

View File

@@ -1,28 +0,0 @@
namespace AssetStudio
{
/// <summary>
/// Specifies the style of a YAML scalar.
/// </summary>
public enum ScalarStyle
{
/// <summary>
/// The plain scalar style.
/// </summary>
Plain,
/// <summary>
///
/// </summary>
Hex,
/// <summary>
/// The single-quoted scalar style.
/// </summary>
SingleQuoted,
/// <summary>
/// The double-quoted scalar style.
/// </summary>
DoubleQuoted,
}
}

View File

@@ -1,17 +0,0 @@
namespace AssetStudio
{
internal enum ScalarType
{
Boolean,
Byte,
UInt16,
Int16,
UInt32,
Int32,
UInt64,
Int64,
Single,
Double,
String,
}
}

View File

@@ -1,51 +0,0 @@
namespace AssetStudio
{
/// <summary>
/// Specifies the style of a sequence.
/// </summary>
public enum SequenceStyle
{
/// <summary>
/// The block sequence style
/// </summary>
Block,
/// <summary>
/// The block sequence style but with curly braces
/// </summary>
BlockCurve,
/// <summary>
/// The flow sequence style
/// </summary>
Flow,
/// <summary>
/// Single line with hex data
/// </summary>
Raw,
}
public static class SequenceStyleExtensions
{
public static bool IsRaw(this SequenceStyle _this)
{
return _this == SequenceStyle.Raw;
}
public static bool IsAnyBlock(this SequenceStyle _this)
{
return _this == SequenceStyle.Block || _this == SequenceStyle.BlockCurve;
}
/// <summary>
/// Get scalar style corresponding to current sequence style
/// </summary>
/// <param name="_this">Sequence style</param>
/// <returns>Corresponding scalar style</returns>
public static ScalarStyle ToScalarStyle(this SequenceStyle _this)
{
return _this == SequenceStyle.Raw ? ScalarStyle.Hex : ScalarStyle.Plain;
}
}
}

View File

@@ -1,42 +0,0 @@
namespace AssetStudio
{
public sealed class YAMLDocument
{
public YAMLDocument()
{
}
public YAMLScalarNode CreateScalarRoot()
{
YAMLScalarNode root = new YAMLScalarNode();
Root = root;
return root;
}
public YAMLSequenceNode CreateSequenceRoot()
{
YAMLSequenceNode root = new YAMLSequenceNode();
Root = root;
return root;
}
public YAMLMappingNode CreateMappingRoot()
{
YAMLMappingNode root = new YAMLMappingNode();
Root = root;
return root;
}
internal void Emit(Emitter emitter, bool isSeparator)
{
if(isSeparator)
{
emitter.Write("---").WriteWhitespace();
}
Root.Emit(emitter);
}
public YAMLNode Root { get; private set; }
}
}

View File

@@ -1,330 +0,0 @@
using System;
using System.Collections.Generic;
namespace AssetStudio
{
public sealed class YAMLMappingNode : YAMLNode
{
public YAMLMappingNode()
{
}
public YAMLMappingNode(MappingStyle style)
{
Style = style;
}
public void Add(int key, long value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(int key, string value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(int key, YAMLNode value)
{
YAMLScalarNode keyNode = new YAMLScalarNode(key);
InsertEnd(keyNode, value);
}
public void Add(uint key, string value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(uint key, YAMLNode value)
{
YAMLScalarNode keyNode = new YAMLScalarNode(key);
InsertEnd(keyNode, value);
}
public void Add(long key, string value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(long key, YAMLNode value)
{
YAMLScalarNode keyNode = new YAMLScalarNode(key);
InsertEnd(keyNode, value);
}
public void Add(string key, bool value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, byte value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, short value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, ushort value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, int value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, uint value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, long value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, ulong value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, float value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, string value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, YAMLNode value)
{
YAMLScalarNode keyNode = new YAMLScalarNode(key, true);
InsertEnd(keyNode, value);
}
public void Add(YAMLNode key, bool value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, byte value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, short value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, ushort value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, int value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, uint value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, long value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, ulong value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, float value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, string value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, YAMLNode value)
{
if (key.NodeType != YAMLNodeType.Scalar)
{
throw new Exception($"Only {YAMLNodeType.Scalar} node as a key supported");
}
InsertEnd(key, value);
}
public void Append(YAMLMappingNode map)
{
foreach (KeyValuePair<YAMLNode, YAMLNode> child in map.m_children)
{
Add(child.Key, child.Value);
}
}
public void InsertBegin(string key, int value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
InsertBegin(key, valueNode);
}
public void InsertBegin(string key, YAMLNode value)
{
YAMLScalarNode keyNode = new YAMLScalarNode(key, true);
InsertBegin(keyNode, value);
}
public void InsertBegin(YAMLNode key, YAMLNode value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
KeyValuePair<YAMLNode, YAMLNode> pair = new KeyValuePair<YAMLNode, YAMLNode>(key, value);
m_children.Insert(0, pair);
}
internal override void Emit(Emitter emitter)
{
base.Emit(emitter);
StartChildren(emitter);
foreach (var kvp in m_children)
{
YAMLNode key = kvp.Key;
YAMLNode value = kvp.Value;
bool iskey = emitter.IsKey;
emitter.IsKey = true;
key.Emit(emitter);
emitter.IsKey = false;
StartTransition(emitter, value);
value.Emit(emitter);
EndTransition(emitter, value);
emitter.IsKey = iskey;
}
EndChildren(emitter);
}
private void StartChildren(Emitter emitter)
{
if (Style == MappingStyle.Block)
{
if (m_children.Count == 0)
{
emitter.Write('{');
}
}
else if (Style == MappingStyle.Flow)
{
emitter.Write('{');
}
}
private void EndChildren(Emitter emitter)
{
if (Style == MappingStyle.Block)
{
if (m_children.Count == 0)
{
emitter.Write('}');
}
emitter.WriteLine();
}
else if (Style == MappingStyle.Flow)
{
emitter.WriteClose('}');
}
}
private void StartTransition(Emitter emitter, YAMLNode next)
{
emitter.Write(':').WriteWhitespace();
if (Style == MappingStyle.Block)
{
if (next.IsMultiline)
{
emitter.WriteLine();
}
}
if (next.IsIndent)
{
emitter.IncreaseIndent();
}
}
private void EndTransition(Emitter emitter, YAMLNode next)
{
if (Style == MappingStyle.Block)
{
emitter.WriteLine();
}
else if (Style == MappingStyle.Flow)
{
emitter.WriteSeparator().WriteWhitespace();
}
if (next.IsIndent)
{
emitter.DecreaseIndent();
}
}
private void InsertEnd(YAMLNode key, YAMLNode value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
KeyValuePair<YAMLNode, YAMLNode> pair = new KeyValuePair<YAMLNode, YAMLNode>(key, value);
m_children.Add(pair);
}
public static YAMLMappingNode Empty { get; } = new YAMLMappingNode(MappingStyle.Flow);
public override YAMLNodeType NodeType => YAMLNodeType.Mapping;
public override bool IsMultiline => Style == MappingStyle.Block && m_children.Count > 0;
public override bool IsIndent => Style == MappingStyle.Block;
public MappingStyle Style { get; set; }
private readonly List<KeyValuePair<YAMLNode, YAMLNode>> m_children = new List<KeyValuePair<YAMLNode, YAMLNode>>();
}
}

View File

@@ -1,40 +0,0 @@
namespace AssetStudio
{
public abstract class YAMLNode
{
internal virtual void Emit(Emitter emitter)
{
bool isWrote = false;
if (!CustomTag.IsEmpty)
{
emitter.Write(CustomTag.ToString()).WriteWhitespace();
isWrote = true;
}
if (Anchor.Length > 0)
{
emitter.Write("&").Write(Anchor).WriteWhitespace();
isWrote = true;
}
if (isWrote)
{
if (IsMultiline)
{
emitter.WriteLine();
}
}
}
public abstract YAMLNodeType NodeType { get; }
public abstract bool IsMultiline { get; }
public abstract bool IsIndent { get; }
public string Tag
{
get => CustomTag.Content;
set => CustomTag = new YAMLTag(YAMLWriter.DefaultTagHandle, value);
}
public YAMLTag CustomTag { get; set; }
public string Anchor { get; set; } = string.Empty;
}
}

View File

@@ -1,20 +0,0 @@
namespace AssetStudio
{
public enum YAMLNodeType
{
/// <summary>
/// The node is a <see cref="YamlMappingNode"/>.
/// </summary>
Mapping,
/// <summary>
/// The node is a <see cref="YamlScalarNode"/>.
/// </summary>
Scalar,
/// <summary>
/// The node is a <see cref="YamlSequenceNode"/>.
/// </summary>
Sequence
}
}

View File

@@ -1,456 +0,0 @@
//#define USE_HEX_FLOAT
using System;
using System.Globalization;
using System.Text.RegularExpressions;
namespace AssetStudio
{
public sealed class YAMLScalarNode : YAMLNode
{
public YAMLScalarNode()
{
}
public YAMLScalarNode(bool value) :
this(value, false)
{
}
public YAMLScalarNode(bool value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(byte value) :
this(value, false)
{
}
public YAMLScalarNode(byte value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(short value) :
this(value, false)
{
}
public YAMLScalarNode(short value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(ushort value) :
this(value, false)
{
}
public YAMLScalarNode(ushort value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(int value) :
this(value, false)
{
}
public YAMLScalarNode(int value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(uint value) :
this(value, false)
{
}
public YAMLScalarNode(uint value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(long value) :
this(value, false)
{
}
public YAMLScalarNode(long value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(ulong value) :
this(value, false)
{
}
public YAMLScalarNode(ulong value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(float value) :
this(value, false)
{
}
public YAMLScalarNode(float value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(double value) :
this(value, false)
{
}
public YAMLScalarNode(double value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(string value)
{
SetValue(value);
Style = GetStringStyle(value);
}
internal YAMLScalarNode(string value, bool _)
{
SetValue(value);
Style = ScalarStyle.Plain;
}
public void SetValue(bool value)
{
m_value = value ? 1u : 0u;
m_objectType = ScalarType.Boolean;
}
public void SetValue(byte value)
{
m_value = value;
m_objectType = ScalarType.Byte;
}
public void SetValue(short value)
{
m_value = unchecked((ushort)value);
m_objectType = ScalarType.Int16;
}
public void SetValue(ushort value)
{
m_value = value;
m_objectType = ScalarType.UInt16;
}
public void SetValue(int value)
{
m_value = unchecked((uint)value);
m_objectType = ScalarType.Int32;
}
public void SetValue(uint value)
{
m_value = value;
m_objectType = ScalarType.UInt32;
}
public void SetValue(long value)
{
m_value = unchecked((ulong)value);
m_objectType = ScalarType.Int64;
}
public void SetValue(ulong value)
{
m_value = value;
m_objectType = ScalarType.UInt64;
}
public void SetValue(float value)
{
#if USE_HEX_FLOAT
// It is more precise technic but output looks vague and less readable
uint hex = BitConverterExtensions.ToUInt32(value);
m_string = $"0x{hex.ToHexString()}({value.ToString(CultureInfo.InvariantCulture)})";
m_objectType = ScalarType.String;
#else
m_value = BitConverterExtensions.ToUInt32(value);
m_objectType = ScalarType.Single;
#endif
}
public void SetValue(double value)
{
#if USE_HEX_FLOAT
// It is more precise technic but output looks vague and less readable
ulong hex = BitConverterExtensions.ToUInt64(value);
m_string = $"0x{hex.ToHexString()}({value.ToString(CultureInfo.InvariantCulture)})";
m_objectType = ScalarType.String;
#else
m_value = BitConverterExtensions.ToUInt64(value);
m_objectType = ScalarType.Double;
#endif
}
public void SetValue(string value)
{
m_string = value;
m_objectType = ScalarType.String;
}
internal Emitter ToString(Emitter emitter)
{
if (Style == ScalarStyle.Hex)
{
switch (m_objectType)
{
case ScalarType.Byte:
return emitter.WriteHex((byte)m_value);
case ScalarType.Int16:
return emitter.WriteHex(unchecked((short)m_value));
case ScalarType.UInt16:
return emitter.WriteHex((ushort)m_value);
case ScalarType.Int32:
return emitter.WriteHex(unchecked((int)m_value));
case ScalarType.UInt32:
return emitter.WriteHex((uint)m_value);
case ScalarType.Int64:
return emitter.WriteHex(unchecked((long)m_value));
case ScalarType.UInt64:
return emitter.WriteHex(m_value);
case ScalarType.Single:
return emitter.WriteHex((uint)m_value);
case ScalarType.Double:
return emitter.WriteHex(m_value);
default:
throw new NotImplementedException(m_objectType.ToString());
}
}
switch (m_objectType)
{
case ScalarType.Boolean:
return emitter.Write(m_value);
case ScalarType.Byte:
return emitter.Write(m_value);
case ScalarType.Int16:
return emitter.Write(unchecked((short)m_value));
case ScalarType.UInt16:
return emitter.Write(m_value);
case ScalarType.Int32:
return emitter.Write(unchecked((int)m_value));
case ScalarType.UInt32:
return emitter.Write(m_value);
case ScalarType.Int64:
return emitter.Write(unchecked((long)m_value));
case ScalarType.UInt64:
return emitter.Write(m_value);
case ScalarType.Single:
return emitter.Write(BitConverterExtensions.ToSingle((uint)m_value));
case ScalarType.Double:
return emitter.Write(BitConverterExtensions.ToDouble(m_value));
case ScalarType.String:
return WriteString(emitter);
default:
throw new NotImplementedException(m_objectType.ToString());
}
}
internal override void Emit(Emitter emitter)
{
base.Emit(emitter);
switch (Style)
{
case ScalarStyle.Hex:
case ScalarStyle.Plain:
ToString(emitter);
break;
case ScalarStyle.SingleQuoted:
emitter.Write('\'');
ToString(emitter);
emitter.Write('\'');
break;
case ScalarStyle.DoubleQuoted:
emitter.Write('"');
ToString(emitter);
emitter.Write('"');
break;
default:
throw new Exception($"Unsupported scalar style {Style}");
}
}
private Emitter WriteString(Emitter emitter)
{
if (Style == ScalarStyle.Plain)
{
if (emitter.IsFormatKeys && emitter.IsKey)
{
emitter.WriteFormat(m_string);
}
else
{
emitter.Write(m_string);
}
}
else if (Style == ScalarStyle.SingleQuoted)
{
emitter.WriteDelayed();
for (int i = 0; i < m_string.Length; i++)
{
char c = m_string[i];
emitter.WriteRaw(c);
if (c == '\'')
{
emitter.WriteRaw(c);
}
else if (c == '\n')
{
emitter.WriteRaw("\n ");
}
}
}
else if (Style == ScalarStyle.DoubleQuoted)
{
emitter.WriteDelayed();
for (int i = 0; i < m_string.Length; i++)
{
char c = m_string[i];
switch (c)
{
case '\\':
emitter.WriteRaw('\\').WriteRaw('\\');
break;
case '\n':
emitter.WriteRaw('\\').WriteRaw('n');
break;
case '\r':
emitter.WriteRaw('\\').WriteRaw('r');
break;
case '\t':
emitter.WriteRaw('\\').WriteRaw('t');
break;
case '"':
emitter.WriteRaw('\\').WriteRaw('"');
break;
default:
emitter.WriteRaw(c);
break;
}
}
}
else
{
throw new NotSupportedException(Style.ToString());
}
return emitter;
}
private static ScalarStyle GetStringStyle(string value)
{
if (s_illegal.IsMatch(value))
{
return value.Contains("\n ") ? ScalarStyle.DoubleQuoted : ScalarStyle.SingleQuoted;
}
return ScalarStyle.Plain;
}
public static YAMLScalarNode Empty { get; } = new YAMLScalarNode();
public override YAMLNodeType NodeType => YAMLNodeType.Scalar;
public override bool IsMultiline => false;
public override bool IsIndent => false;
public string Value
{
get
{
if (Style == ScalarStyle.Hex)
{
switch (m_objectType)
{
case ScalarType.Byte:
return unchecked((byte)m_value).ToHexString();
case ScalarType.Int16:
return unchecked((short)m_value).ToHexString();
case ScalarType.UInt16:
return unchecked((ushort)m_value).ToHexString();
case ScalarType.Int32:
return unchecked((int)m_value).ToHexString();
case ScalarType.UInt32:
return unchecked((uint)m_value).ToHexString();
case ScalarType.Int64:
return unchecked((long)m_value).ToHexString();
case ScalarType.UInt64:
return m_value.ToHexString();
case ScalarType.Single:
return BitConverterExtensions.ToSingle((uint)m_value).ToHexString();
case ScalarType.Double:
return BitConverterExtensions.ToDouble(m_value).ToHexString();
default:
throw new NotImplementedException(m_objectType.ToString());
}
}
switch (m_objectType)
{
case ScalarType.Boolean:
return m_value == 1 ? "true" : "false";
case ScalarType.Byte:
return m_value.ToString();
case ScalarType.Int16:
return unchecked((short)m_value).ToString();
case ScalarType.UInt16:
return m_value.ToString();
case ScalarType.Int32:
return unchecked((int)m_value).ToString();
case ScalarType.UInt32:
return m_value.ToString();
case ScalarType.Int64:
return unchecked((long)m_value).ToString();
case ScalarType.UInt64:
return m_value.ToString();
case ScalarType.Single:
return BitConverterExtensions.ToSingle((uint)m_value).ToString(CultureInfo.InvariantCulture);
case ScalarType.Double:
return BitConverterExtensions.ToDouble(m_value).ToString(CultureInfo.InvariantCulture);
case ScalarType.String:
return m_string;
default:
throw new NotImplementedException(m_objectType.ToString());
}
}
set => m_string = value;
}
public ScalarStyle Style { get; }
private static readonly Regex s_illegal = new Regex("(^\\s)|(^-\\s)|(^-$)|(^[\\:\\[\\]'\"*&!@#%{}?<>,\\`])|([:@]\\s)|([\\n\\r])|([:\\s]$)", RegexOptions.Compiled);
private ScalarType m_objectType = ScalarType.String;
private string m_string = string.Empty;
private ulong m_value = 0;
}
}

View File

@@ -1,213 +0,0 @@
using System.Collections.Generic;
namespace AssetStudio
{
public sealed class YAMLSequenceNode : YAMLNode
{
public YAMLSequenceNode()
{
}
public YAMLSequenceNode(SequenceStyle style)
{
Style = style;
}
public void Add(bool value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(byte value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(short value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(ushort value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(int value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(uint value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(long value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(ulong value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(float value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(double value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(string value)
{
YAMLScalarNode node = new YAMLScalarNode(value);
Add(node);
}
public void Add(YAMLNode child)
{
m_children.Add(child);
}
internal override void Emit(Emitter emitter)
{
base.Emit(emitter);
StartChildren(emitter);
foreach (YAMLNode child in m_children)
{
StartChild(emitter, child);
child.Emit(emitter);
EndChild(emitter, child);
}
EndChildren(emitter);
}
private void StartChildren(Emitter emitter)
{
switch (Style)
{
case SequenceStyle.Block:
if (m_children.Count == 0)
{
emitter.Write('[');
}
break;
case SequenceStyle.BlockCurve:
if (m_children.Count == 0)
{
emitter.Write('{');
}
break;
case SequenceStyle.Flow:
emitter.Write('[');
break;
case SequenceStyle.Raw:
if (m_children.Count == 0)
{
emitter.Write('[');
}
break;
}
}
private void EndChildren(Emitter emitter)
{
switch (Style)
{
case SequenceStyle.Block:
if (m_children.Count == 0)
{
emitter.Write(']');
}
emitter.WriteLine();
break;
case SequenceStyle.BlockCurve:
if (m_children.Count == 0)
{
emitter.WriteClose('}');
}
emitter.WriteLine();
break;
case SequenceStyle.Flow:
emitter.WriteClose(']');
break;
case SequenceStyle.Raw:
if (m_children.Count == 0)
{
emitter.Write(']');
}
emitter.WriteLine();
break;
}
}
private void StartChild(Emitter emitter, YAMLNode next)
{
if (Style.IsAnyBlock())
{
emitter.Write('-').Write(' ');
if (next.NodeType == NodeType)
{
emitter.IncreaseIndent();
}
}
if (next.IsIndent)
{
emitter.IncreaseIndent();
}
}
private void EndChild(Emitter emitter, YAMLNode next)
{
if (Style.IsAnyBlock())
{
emitter.WriteLine();
if (next.NodeType == NodeType)
{
emitter.DecreaseIndent();
}
}
else if (Style == SequenceStyle.Flow)
{
emitter.WriteSeparator().WriteWhitespace();
}
if (next.IsIndent)
{
emitter.DecreaseIndent();
}
}
public static YAMLSequenceNode Empty { get; } = new YAMLSequenceNode();
public override YAMLNodeType NodeType => YAMLNodeType.Sequence;
public override bool IsMultiline => Style.IsAnyBlock() && m_children.Count > 0;
public override bool IsIndent => false;
public SequenceStyle Style { get; }
private readonly List<YAMLNode> m_children = new List<YAMLNode>();
}
}

View File

@@ -1,26 +0,0 @@
namespace AssetStudio
{
public readonly struct YAMLTag
{
public YAMLTag(string handle, string content)
{
Handle = handle;
Content = content;
}
public override string ToString()
{
return IsEmpty ? string.Empty : $"{Handle}{Content}";
}
public string ToHeaderString()
{
return IsEmpty ? string.Empty : $"{Handle} {Content}";
}
public bool IsEmpty => string.IsNullOrEmpty(Handle);
public string Handle { get; }
public string Content { get; }
}
}

View File

@@ -1,98 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace AssetStudio
{
using Version = System.Version;
public class YAMLWriter
{
public void AddDocument(YAMLDocument document)
{
#if DEBUG
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
if (m_documents.Contains(document))
{
throw new ArgumentException($"Document {document} is added already", nameof(document));
}
#endif
m_documents.Add(document);
}
public void AddTag(string handle, string content)
{
if(m_tags.Any(t => t.Handle == handle))
{
throw new Exception($"Writer already contains tag {handle}");
}
YAMLTag tag = new YAMLTag(handle, content);
m_tags.Add(tag);
}
public void Write(TextWriter output)
{
WriteHead(output);
foreach (YAMLDocument doc in m_documents)
{
WriteDocument(doc);
}
WriteTail(output);
}
public void WriteHead(TextWriter output)
{
m_emitter = new Emitter(output, IsFormatKeys);
m_isWriteSeparator = false;
if (IsWriteVersion)
{
m_emitter.WriteMeta(MetaType.YAML, Version.ToString());
m_isWriteSeparator = true;
}
if (IsWriteDefaultTag)
{
m_emitter.WriteMeta(MetaType.TAG, DefaultTag.ToHeaderString());
m_isWriteSeparator = true;
}
foreach (YAMLTag tag in m_tags)
{
m_emitter.WriteMeta(MetaType.TAG, tag.ToHeaderString());
m_isWriteSeparator = true;
}
}
public void WriteDocument(YAMLDocument doc)
{
doc.Emit(m_emitter, m_isWriteSeparator);
m_isWriteSeparator = true;
}
public void WriteTail(TextWriter output)
{
output.Write('\n');
}
public static Version Version { get; } = new Version(1, 1);
public const string DefaultTagHandle = "!u!";
public const string DefaultTagContent = "tag:unity3d.com,2011:";
public readonly YAMLTag DefaultTag = new YAMLTag(DefaultTagHandle, DefaultTagContent);
public bool IsWriteVersion { get; set; } = true;
public bool IsWriteDefaultTag { get; set; } = true;
public bool IsFormatKeys { get; set; }
private readonly HashSet<YAMLDocument> m_documents = new HashSet<YAMLDocument>();
private readonly List<YAMLTag> m_tags = new List<YAMLTag>();
private Emitter m_emitter;
private bool m_isWriteSeparator;
}
}

View File

@@ -1,25 +0,0 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace AssetStudio
{
public static class ArrayYAMLExtensions
{
public static YAMLNode ExportYAML(this byte[] _this)
{
StringBuilder sb = new StringBuilder(_this.Length * 2);
for (int i = 0; i < _this.Length; i++)
{
sb.AppendHex(_this[i]);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML<T>(this T[][] _this)
where T : IYAMLExportable
{
return ((IEnumerable<IEnumerable<T>>)_this).ExportYAML();
}
}
}

View File

@@ -1,82 +0,0 @@
namespace AssetStudio
{
internal static class EmitterExtensions
{
public static Emitter WriteHex(this Emitter _this, byte value)
{
_this.Write(HexAlphabet[value >> 4]);
_this.Write(HexAlphabet[value & 0xF]);
return _this;
}
public static Emitter WriteHex(this Emitter _this, ushort value)
{
_this.Write(HexAlphabet[(value >> 4) & 0xF]);
_this.Write(HexAlphabet[(value >> 0) & 0xF]);
_this.Write(HexAlphabet[(value >> 12) & 0xF]);
_this.Write(HexAlphabet[(value >> 8) & 0xF]);
return _this;
}
public static Emitter WriteHex(this Emitter _this, short value)
{
return WriteHex(_this, unchecked((ushort)value));
}
public static Emitter WriteHex(this Emitter _this, uint value)
{
_this.Write(HexAlphabet[unchecked((int)(value >> 4) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 0) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 12) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 8) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 20) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 16) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 28) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 24) & 0xF)]);
return _this;
}
public static Emitter WriteHex(this Emitter _this, int value)
{
return WriteHex(_this, unchecked((uint)value));
}
public static Emitter WriteHex(this Emitter _this, ulong value)
{
_this.Write(HexAlphabet[unchecked((int)(value >> 4) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 0) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 12) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 8) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 20) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 16) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 28) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 24) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 36) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 32) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 44) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 40) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 52) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 48) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 60) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 56) & 0xF)]);
return _this;
}
public static Emitter WriteHex(this Emitter _this, long value)
{
return WriteHex(_this, unchecked((ulong)value));
}
public static Emitter WriteHex(this Emitter _this, float value)
{
return WriteHex(_this, BitConverterExtensions.ToUInt32(value));
}
public static Emitter WriteHex(this Emitter _this, double value)
{
return WriteHex(_this, BitConverterExtensions.ToUInt64(value));
}
private static readonly string HexAlphabet = "0123456789ABCDEF";
}
}

View File

@@ -1,143 +0,0 @@
using System;
using System.Collections.Generic;
namespace AssetStudio
{
public static class IDictionaryExportYAMLExtensions
{
public static YAMLNode ExportYAML<T>(this IReadOnlyDictionary<int, T> _this)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(kvp.Key, kvp.Value.ExportYAML());
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T>(this IReadOnlyDictionary<string, T> _this)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(kvp.Key, kvp.Value.ExportYAML());
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T1, T2>(this IReadOnlyDictionary<Tuple<T1, long>, T2> _this)
where T1 : IYAMLExportable
where T2 : IYAMLExportable
{
// TODO: test
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode kvpMap = new YAMLMappingNode();
YAMLMappingNode keyMap = new YAMLMappingNode();
keyMap.Add("first", kvp.Key.Item1.ExportYAML());
keyMap.Add("second", kvp.Key.Item2);
kvpMap.Add("first", keyMap);
kvpMap.Add("second", kvp.Value.ExportYAML());
node.Add(kvpMap);
}
return node;
}
public static YAMLNode ExportYAML<T>(this IReadOnlyDictionary<T, int> _this)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
YAMLNode key = kvp.Key.ExportYAML();
if (key.NodeType == YAMLNodeType.Scalar)
{
map.Add(key, kvp.Value);
}
else
{
map.Add("first", key);
map.Add("second", kvp.Value);
}
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T>(this IReadOnlyDictionary<T, float> _this)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
YAMLNode key = kvp.Key.ExportYAML();
if (key.NodeType == YAMLNodeType.Scalar)
{
map.Add(key, kvp.Value);
}
else
{
map.Add("first", key);
map.Add("second", kvp.Value);
}
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T1, T2>(this IReadOnlyDictionary<T1, T2> _this)
where T1 : IYAMLExportable
where T2 : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
YAMLNode key = kvp.Key.ExportYAML();
if (key.NodeType == YAMLNodeType.Scalar)
{
map.Add(key, kvp.Value.ExportYAML());
}
else
{
map.Add("first", key);
map.Add("second", kvp.Value.ExportYAML());
}
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T1, T2>(this IReadOnlyDictionary<T1, T2[]> _this)
where T1 : IYAMLExportable
where T2 : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
YAMLNode key = kvp.Key.ExportYAML();
if (key.NodeType == YAMLNodeType.Scalar)
{
map.Add(key, kvp.Value.ExportYAML());
}
else
{
map.Add("first", key);
map.Add("second", kvp.Value.ExportYAML());
}
node.Add(map);
}
return node;
}
}
}

View File

@@ -1,105 +0,0 @@
using System;
using System.Collections.Generic;
namespace AssetStudio
{
public static class IDictionaryYAMLExtensions
{
public static YAMLNode ExportYAML(this IReadOnlyDictionary<uint, string> _this)
{
YAMLMappingNode node = new YAMLMappingNode();
foreach (var kvp in _this)
{
node.Add(kvp.Key, kvp.Value);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<long, string> _this)
{
YAMLMappingNode node = new YAMLMappingNode();
foreach (var kvp in _this)
{
node.Add(kvp.Key, kvp.Value);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<string, string> _this)
{
YAMLMappingNode node = new YAMLMappingNode();
foreach (var kvp in _this)
{
node.Add(kvp.Key, kvp.Value);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<string, int> _this)
{
YAMLMappingNode node = new YAMLMappingNode();
foreach (var kvp in _this)
{
node.Add(kvp.Key, kvp.Value);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<string, float> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(kvp.Key, kvp.Value);
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<Tuple<ushort, ushort>, float> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode keyNode = new YAMLMappingNode();
keyNode.Add(kvp.Key.Item1, kvp.Key.Item2);
YAMLMappingNode kvpMap = new YAMLMappingNode();
kvpMap.Add("first", keyNode);
kvpMap.Add("second", kvp.Value);
node.Add(kvpMap);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<Tuple<int, long>, string> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode keyNode = new YAMLMappingNode();
keyNode.Add(kvp.Key.Item1, kvp.Key.Item2);
YAMLMappingNode kvpMap = new YAMLMappingNode();
kvpMap.Add("first", keyNode);
kvpMap.Add("second", kvp.Value);
node.Add(kvpMap);
}
return node;
}
public static YAMLNode ExportYAML<T>(this IReadOnlyDictionary<Tuple<T, long>, string> _this, Func<T, int> converter)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode keyNode = new YAMLMappingNode();
keyNode.Add(converter(kvp.Key.Item1), kvp.Key.Item2);
YAMLMappingNode kvpMap = new YAMLMappingNode();
kvpMap.Add("first", keyNode);
kvpMap.Add("second", kvp.Value);
node.Add(kvpMap);
}
return node;
}
}
}

View File

@@ -1,273 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AssetStudio
{
public static class IEnumerableYAMLExtensions
{
public static YAMLNode ExportYAML(this IEnumerable<bool> _this)
{
StringBuilder sb = new StringBuilder();
foreach (bool value in _this)
{
byte bvalue = unchecked((byte)(value ? 1 : 0));
sb.AppendHex(bvalue);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IEnumerable<char> _this)
{
StringBuilder sb = new StringBuilder();
foreach (char value in _this)
{
sb.AppendHex((ushort)value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IEnumerable<byte> _this)
{
StringBuilder sb = new StringBuilder();
foreach (byte value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IEnumerable<ushort> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (ushort value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (ushort value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<short> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (short value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (short value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<uint> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (uint value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (uint value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<int> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (int value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (int value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<ulong> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (ulong value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (ulong value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<long> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (long value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (long value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<float> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (float value in _this)
{
node.Add(value);
}
return node;
}
public static YAMLNode ExportYAML(this IEnumerable<double> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (double value in _this)
{
node.Add(value);
}
return node;
}
public static YAMLNode ExportYAML(this IEnumerable<string> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (string value in _this)
{
node.Add(value);
}
return node;
}
public static YAMLNode ExportYAML(this IEnumerable<IEnumerable<string>> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (IEnumerable<string> export in _this)
{
node.Add(export.ExportYAML());
}
return node;
}
public static YAMLNode ExportYAML<T>(this IEnumerable<T> _this)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (T export in _this)
{
node.Add(export.ExportYAML());
}
return node;
}
public static YAMLNode ExportYAML<T>(this IEnumerable<IEnumerable<T>> _this)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (IEnumerable<T> export in _this)
{
node.Add(export.ExportYAML());
}
return node;
}
public static YAMLNode ExportYAML<T>(this IEnumerable<Tuple<string, T>> _this)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode();
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(kvp.Item1, kvp.Item2.ExportYAML());
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T1, T2>(this IEnumerable<Tuple<T1, T2>> _this, Func<T1, int> converter)
where T2 : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode();
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(converter(kvp.Item1), kvp.Item2.ExportYAML());
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T>(this IEnumerable<KeyValuePair<string, T>> _this)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode();
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(kvp.Key, kvp.Value.ExportYAML());
node.Add(map);
}
return node;
}
}
}

View File

@@ -1,171 +0,0 @@
using System.Collections.Generic;
using System.Text;
namespace AssetStudio
{
public static class IListYAMLExtensions
{
public static YAMLNode ExportYAML(this IReadOnlyList<bool> _this)
{
StringBuilder sb = new StringBuilder(_this.Count * 2);
foreach (bool value in _this)
{
byte bvalue = unchecked((byte)(value ? 1 : 0));
sb.AppendHex(bvalue);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IReadOnlyList<char> _this)
{
StringBuilder sb = new StringBuilder(_this.Count * 4);
foreach (char value in _this)
{
sb.AppendHex((ushort)value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IReadOnlyList<byte> _this)
{
StringBuilder sb = new StringBuilder(_this.Count * 2);
foreach (byte value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IReadOnlyList<ushort> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 4);
foreach (ushort value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (ushort value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IReadOnlyList<short> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 4);
foreach (short value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (short value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IReadOnlyList<uint> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 8);
foreach (uint value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (uint value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IReadOnlyList<int> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 8);
foreach (int value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (int value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IReadOnlyList<ulong> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 16);
foreach (ulong value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (ulong value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IReadOnlyList<long> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 16);
foreach (long value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (long value in _this)
{
node.Add(value);
}
return node;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More