v0.80.30
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>0.18.60</Version>
|
||||
<AssemblyVersion>0.18.60</AssemblyVersion>
|
||||
<FileVersion>0.18.60</FileVersion>
|
||||
<Version>0.80.30</Version>
|
||||
<AssemblyVersion>0.80.30</AssemblyVersion>
|
||||
<FileVersion>0.80.30</FileVersion>
|
||||
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetStudio.PInvoke
|
||||
{
|
||||
public static class DllLoader
|
||||
public static partial class DllLoader
|
||||
{
|
||||
|
||||
public static void PreloadDll(string dllName)
|
||||
@@ -37,7 +37,7 @@ namespace AssetStudio.PInvoke
|
||||
return directedDllDir;
|
||||
}
|
||||
|
||||
private static class Win32
|
||||
private static partial class Win32
|
||||
{
|
||||
|
||||
internal static void LoadDll(string dllDir, string dllName)
|
||||
@@ -46,9 +46,9 @@ namespace AssetStudio.PInvoke
|
||||
var directedDllPath = Path.Combine(dllDir, dllFileName);
|
||||
|
||||
// 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 exception = new Win32Exception(errorCode);
|
||||
@@ -59,15 +59,15 @@ namespace AssetStudio.PInvoke
|
||||
|
||||
// HMODULE LoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
|
||||
// HMODULE LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr LoadLibraryEx(string lpLibFileName, IntPtr hFile, uint dwFlags);
|
||||
[LibraryImport("kernel32.dll", EntryPoint = "LoadLibraryExA", SetLastError = true, StringMarshalling = StringMarshalling.Utf8)]
|
||||
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_DLL_LOAD_DIR = 0x100;
|
||||
|
||||
}
|
||||
|
||||
private static class Posix
|
||||
private static partial class Posix
|
||||
{
|
||||
|
||||
internal static void LoadDll(string dllDir, string dllName)
|
||||
@@ -93,7 +93,7 @@ namespace AssetStudio.PInvoke
|
||||
const int ldFlags = RTLD_NOW | RTLD_GLOBAL;
|
||||
var hLibrary = DlOpen(directedDllPath, ldFlags);
|
||||
|
||||
if (hLibrary == IntPtr.Zero)
|
||||
if (hLibrary == nint.Zero)
|
||||
{
|
||||
var pErrStr = DlError();
|
||||
// `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.
|
||||
// void *dlopen(const char *filename, int flag);
|
||||
[DllImport("libdl", EntryPoint = "dlopen")]
|
||||
private static extern IntPtr DlOpen([MarshalAs(UnmanagedType.LPStr)] string fileName, int flags);
|
||||
[LibraryImport("libdl", EntryPoint = "dlopen", StringMarshalling = StringMarshalling.Utf8)]
|
||||
private static partial nint DlOpen(string fileName, int flags);
|
||||
|
||||
// char *dlerror(void);
|
||||
[DllImport("libdl", EntryPoint = "dlerror")]
|
||||
private static extern IntPtr DlError();
|
||||
[LibraryImport("libdl", EntryPoint = "dlerror")]
|
||||
private static partial nint DlError();
|
||||
|
||||
private const int RTLD_LAZY = 0x1;
|
||||
private const int RTLD_NOW = 0x2;
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32014.148
|
||||
VisualStudioVersion = 17.4.33205.214
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudio", "AssetStudio\AssetStudio.csproj", "{422FEC21-EF60-4F29-AA56-95DFDA23C913}"
|
||||
EndProject
|
||||
@@ -16,16 +16,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioGUI", "AssetStud
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioUtility", "AssetStudioUtility\AssetStudioUtility.csproj", "{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}"
|
||||
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}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Texture2DDecoderNative", "Texture2DDecoderNative\Texture2DDecoderNative.vcxproj", "{29356642-C46E-4144-83D8-22DC09D0D7FD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioCLI", "AssetStudioCLI\AssetStudioCLI.csproj", "{5B2D8C81-7DE2-429C-AF90-B7C71D91F3B6}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioCLI", "AssetStudioCLI\AssetStudioCLI.csproj", "{D35262CD-E063-4203-A9D2-C1BB82B6C598}"
|
||||
EndProject
|
||||
Global
|
||||
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|x86.ActiveCfg = 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.Build.0 = Debug|Win32
|
||||
{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|x86.ActiveCfg = Release|Win32
|
||||
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x86.Build.0 = Release|Win32
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|Any CPU.Build.0 = Debug|Win32
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x64.Build.0 = Debug|x64
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x86.Build.0 = Debug|Win32
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|Any CPU.Build.0 = Release|Win32
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x64.ActiveCfg = Release|x64
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x64.Build.0 = Release|x64
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.ActiveCfg = Release|Win32
|
||||
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.Build.0 = Release|Win32
|
||||
{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
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D35262CD-E063-4203-A9D2-C1BB82B6C598}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
{
|
||||
public class AssetEntry
|
||||
{
|
||||
public string Name;
|
||||
public string Container;
|
||||
public string Source;
|
||||
public long PathID;
|
||||
public ClassIDType Type;
|
||||
public string Name { get; set; }
|
||||
public string Container { get; set; }
|
||||
public string Source { get; set; }
|
||||
public long PathID { get; set; }
|
||||
public ClassIDType Type { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
10
AssetStudio/AssetGroupOption.cs
Normal file
10
AssetStudio/AssetGroupOption.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace AssetStudio
|
||||
{
|
||||
public enum AssetGroupOption
|
||||
{
|
||||
ByType,
|
||||
ByContainer,
|
||||
BySource,
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>0.18.60</Version>
|
||||
<AssemblyVersion>0.18.60</AssemblyVersion>
|
||||
<FileVersion>0.18.60</FileVersion>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>0.80.30</Version>
|
||||
<AssemblyVersion>0.80.30</AssemblyVersion>
|
||||
<FileVersion>0.80.30</FileVersion>
|
||||
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="K4os.Compression.LZ4" Version="1.2.16" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta2" />
|
||||
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.4-beta" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Keys.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
436
AssetStudio/AssetsHelper.cs
Normal file
436
AssetStudio/AssetsHelper.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using static AssetStudio.ImportHelper;
|
||||
|
||||
namespace AssetStudio
|
||||
@@ -11,8 +13,11 @@ namespace AssetStudio
|
||||
public class AssetsManager
|
||||
{
|
||||
public Game Game;
|
||||
public bool ResolveDependancies;
|
||||
public bool Silent = false;
|
||||
public bool SkipProcess = false;
|
||||
public bool ResolveDependencies = false;
|
||||
public string SpecifyUnityVersion;
|
||||
public CancellationTokenSource tokenSource = new CancellationTokenSource();
|
||||
public List<SerializedFile> assetsFileList = new List<SerializedFile>();
|
||||
|
||||
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 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);
|
||||
|
||||
public void LoadFiles(params string[] files)
|
||||
{
|
||||
if (ResolveDependancies)
|
||||
files = CABManager.ProcessDependencies(files);
|
||||
Load(files);
|
||||
if (Silent)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories).ToArray();
|
||||
Load(files);
|
||||
if (Silent)
|
||||
{
|
||||
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)
|
||||
@@ -49,20 +84,29 @@ namespace AssetStudio
|
||||
{
|
||||
LoadFile(importFiles[i]);
|
||||
Progress.Report(i + 1, importFiles.Count);
|
||||
if (tokenSource.IsCancellationRequested)
|
||||
{
|
||||
Logger.Info("Loading files has been aborted !!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
importFiles.Clear();
|
||||
importFilesHash.Clear();
|
||||
noexistFiles.Clear();
|
||||
assetsFileListHash.Clear();
|
||||
CABManager.offsets.Clear();
|
||||
|
||||
if (!SkipProcess && !tokenSource.IsCancellationRequested)
|
||||
{
|
||||
ReadAssets();
|
||||
ProcessAssets();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadFile(string fullName)
|
||||
{
|
||||
var reader = new FileReader(fullName, Game);
|
||||
var reader = new FileReader(fullName);
|
||||
reader = reader.PreProcessing(Game);
|
||||
LoadFile(reader);
|
||||
}
|
||||
|
||||
@@ -76,9 +120,6 @@ namespace AssetStudio
|
||||
case FileType.BundleFile:
|
||||
LoadBundleFile(reader);
|
||||
break;
|
||||
case FileType.GameFile:
|
||||
LoadGameFile(reader);
|
||||
break;
|
||||
case FileType.WebFile:
|
||||
LoadWebFile(reader);
|
||||
break;
|
||||
@@ -91,6 +132,15 @@ namespace AssetStudio
|
||||
case FileType.ZipFile:
|
||||
LoadZipFile(reader);
|
||||
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))
|
||||
{
|
||||
Logger.Info($"Loading {reader.FileName}");
|
||||
Logger.Info($"Loading {reader.FullPath}");
|
||||
try
|
||||
{
|
||||
var assetsFile = new SerializedFile(reader, this, reader.FullPath);
|
||||
var assetsFile = new SerializedFile(reader, this);
|
||||
CheckStrippedVersion(assetsFile);
|
||||
assetsFileList.Add(assetsFile);
|
||||
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)
|
||||
{
|
||||
@@ -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))
|
||||
{
|
||||
@@ -127,6 +210,7 @@ namespace AssetStudio
|
||||
{
|
||||
var assetsFile = new SerializedFile(reader, this);
|
||||
assetsFile.originalPath = originalPath;
|
||||
assetsFile.offset = originalOffset;
|
||||
if (!string.IsNullOrEmpty(unityVersion) && assetsFile.header.m_Version < SerializedFileFormatVersion.Unknown_7)
|
||||
{
|
||||
assetsFile.SetVersion(unityVersion);
|
||||
@@ -145,19 +229,19 @@ namespace AssetStudio
|
||||
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);
|
||||
try
|
||||
{
|
||||
var bundleFile = new BundleFile(reader);
|
||||
foreach (var file in bundleFile.FileList)
|
||||
var bundleFile = new BundleFile(reader, Game);
|
||||
foreach (var file in bundleFile.fileList)
|
||||
{
|
||||
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
|
||||
var subReader = new FileReader(dummyPath, file.stream);
|
||||
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
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
var str = $"Error while reading bundle file {reader.FullPath}";
|
||||
@@ -316,26 +404,113 @@ namespace AssetStudio
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadGameFile(FileReader reader)
|
||||
private void LoadBlockFile(FileReader reader)
|
||||
{
|
||||
Logger.Info("Loading " + reader.FileName);
|
||||
Logger.Info("Loading " + reader.FullPath);
|
||||
try
|
||||
{
|
||||
reader.BundlePos = CABManager.offsets.TryGetValue(reader.FullPath, out var list) ? list.ToArray() : Array.Empty<long>();
|
||||
var gameFile = new GameFile(reader);
|
||||
foreach (var bundle in gameFile.Bundles)
|
||||
var offsets = AssetsHelper.Offsets.TryGetValue(reader.FullPath, out var list) ? list.ToArray() : Array.Empty<long>();
|
||||
using var stream = new BlockStream(reader.BaseStream, 0);
|
||||
if (!offsets.IsNullOrEmpty())
|
||||
{
|
||||
foreach (var file in bundle.Value)
|
||||
foreach (var offset in offsets)
|
||||
{
|
||||
stream.Offset = offset;
|
||||
var dummyPath = Path.Combine("//?/block:/", reader.FileName, offset.ToString("X8"));
|
||||
var subReader = new FileReader(dummyPath, stream, true);
|
||||
LoadBundleFile(subReader, reader.FullPath, offset);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
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, Game);
|
||||
var cabReader = new FileReader(dummyPath, file.stream);
|
||||
if (cabReader.FileType == FileType.AssetsFile)
|
||||
{
|
||||
var assetsFile = new SerializedFile(cabReader, this, reader.FullPath);
|
||||
CheckStrippedVersion(assetsFile);
|
||||
assetsFileList.Add(assetsFile);
|
||||
assetsFileListHash.Add(assetsFile.fileName);
|
||||
LoadAssetsFromMemory(cabReader, originalPath ?? reader.FullPath, mhy0File.m_Header.unityRevision, originalOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -343,10 +518,18 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
Logger.Error($"Game type mismatch, Expected {nameof(Mhy0)} but got {Game.Name} ({Game.GetType().Name}) !!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error($"Error while reading file {reader.FileName}", e);
|
||||
var str = $"Error while reading mhy0 file {reader.FullPath}";
|
||||
if (originalPath != null)
|
||||
{
|
||||
str += $" from {Path.GetFileName(originalPath)}";
|
||||
}
|
||||
Logger.Error(str, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -382,6 +565,12 @@ namespace AssetStudio
|
||||
resourceFileReaders.Clear();
|
||||
|
||||
assetsFileIndexCache.Clear();
|
||||
|
||||
tokenSource.Dispose();
|
||||
tokenSource = new CancellationTokenSource();
|
||||
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
private void ReadAssets()
|
||||
@@ -395,7 +584,12 @@ namespace AssetStudio
|
||||
{
|
||||
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
|
||||
{
|
||||
Object obj;
|
||||
@@ -444,7 +638,8 @@ namespace AssetStudio
|
||||
obj = new MeshFilter(objectReader);
|
||||
break;
|
||||
case ClassIDType.MeshRenderer:
|
||||
if (!Renderer.Parsable) continue;
|
||||
if (Renderer.Skipped)
|
||||
goto default;
|
||||
obj = new MeshRenderer(objectReader);
|
||||
break;
|
||||
case ClassIDType.MiHoYoBinData:
|
||||
@@ -466,11 +661,11 @@ namespace AssetStudio
|
||||
obj = new RectTransform(objectReader);
|
||||
break;
|
||||
case ClassIDType.Shader:
|
||||
if (!Shader.Parsable) continue;
|
||||
obj = new Shader(objectReader);
|
||||
break;
|
||||
case ClassIDType.SkinnedMeshRenderer:
|
||||
if (!Renderer.Parsable) continue;
|
||||
if (Renderer.Skipped)
|
||||
goto default;
|
||||
obj = new SkinnedMeshRenderer(objectReader);
|
||||
break;
|
||||
case ClassIDType.Sprite:
|
||||
@@ -519,12 +714,20 @@ namespace AssetStudio
|
||||
|
||||
private void ProcessAssets()
|
||||
{
|
||||
if (tokenSource.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
Logger.Info("Process Assets...");
|
||||
|
||||
foreach (var assetsFile in assetsFileList)
|
||||
{
|
||||
foreach (var obj in assetsFile.Objects)
|
||||
{
|
||||
if (tokenSource.IsCancellationRequested)
|
||||
{
|
||||
Logger.Info("Processing assets has been aborted !!");
|
||||
return;
|
||||
}
|
||||
if (obj is GameObject m_GameObject)
|
||||
{
|
||||
foreach (var pptr in m_GameObject.m_Components)
|
||||
|
||||
59
AssetStudio/BlockStream.cs
Normal file
59
AssetStudio/BlockStream.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using K4os.Compression.LZ4;
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -12,14 +14,16 @@ namespace AssetStudio
|
||||
BlocksAndDirectoryInfoCombined = 0x40,
|
||||
BlocksInfoAtTheEnd = 0x80,
|
||||
OldWebPluginCompatibility = 0x100,
|
||||
BlockInfoNeedPaddingAtStart = 0x200
|
||||
BlockInfoNeedPaddingAtStart = 0x200,
|
||||
CNUnityEncryption = 0x400
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum StorageBlockFlags
|
||||
{
|
||||
CompressionTypeMask = 0x3f,
|
||||
Streamed = 0x40
|
||||
Streamed = 0x40,
|
||||
CNUnity = 0x100
|
||||
}
|
||||
|
||||
public enum CompressionType
|
||||
@@ -31,6 +35,7 @@ namespace AssetStudio
|
||||
Lzham,
|
||||
Lz4Mr0k
|
||||
}
|
||||
|
||||
public class BundleFile
|
||||
{
|
||||
public class Header
|
||||
@@ -60,46 +65,19 @@ namespace AssetStudio
|
||||
public string path;
|
||||
}
|
||||
|
||||
private Game Game;
|
||||
private CNUnity CNUnity;
|
||||
|
||||
public Header m_Header;
|
||||
private StorageBlock[] m_BlocksInfo;
|
||||
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();
|
||||
m_Header.signature = reader.ReadStringToNull();
|
||||
|
||||
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();
|
||||
}
|
||||
Game = game;
|
||||
m_Header = ReadBundleHeader(reader);
|
||||
switch (m_Header.signature)
|
||||
{
|
||||
case "UnityArchive":
|
||||
@@ -120,10 +98,6 @@ namespace AssetStudio
|
||||
case "UnityFS":
|
||||
case "ENCR":
|
||||
ReadHeader(reader);
|
||||
if (reader.Game.Name == "ZZZ_CB1")
|
||||
{
|
||||
reader.AlignStream(0x10);
|
||||
}
|
||||
ReadBlocksInfoAndDirectory(reader);
|
||||
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)
|
||||
{
|
||||
if (m_Header.version >= 4)
|
||||
@@ -221,12 +243,12 @@ namespace AssetStudio
|
||||
|
||||
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++)
|
||||
{
|
||||
var node = m_DirectoryInfo[i];
|
||||
var file = new StreamFile();
|
||||
FileList[i] = file;
|
||||
fileList[i] = file;
|
||||
file.path = node.path;
|
||||
file.fileName = Path.GetFileName(node.path);
|
||||
if (node.size >= int.MaxValue)
|
||||
@@ -247,42 +269,74 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
private void DecryptHeader(int key)
|
||||
private void DecryptHeader()
|
||||
{
|
||||
var rand = new XORShift128();
|
||||
rand.InitSeed(key);
|
||||
m_Header.flags ^= (ArchiveFlags)rand.NextDecryptInt();
|
||||
m_Header.size ^= rand.NextDecryptLong();
|
||||
m_Header.uncompressedBlocksInfoSize ^= rand.NextDecryptUInt();
|
||||
m_Header.compressedBlocksInfoSize ^= rand.NextDecryptUInt();
|
||||
m_Header.flags ^= (ArchiveFlags)XORShift128.NextDecryptInt();
|
||||
m_Header.size ^= XORShift128.NextDecryptLong();
|
||||
m_Header.uncompressedBlocksInfoSize ^= XORShift128.NextDecryptUInt();
|
||||
m_Header.compressedBlocksInfoSize ^= XORShift128.NextDecryptUInt();
|
||||
XORShift128.Init = false;
|
||||
}
|
||||
|
||||
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.size = reader.ReadInt64();
|
||||
m_Header.uncompressedBlocksInfoSize = reader.ReadUInt32();
|
||||
m_Header.compressedBlocksInfoSize = reader.ReadUInt32();
|
||||
DecryptHeader(key);
|
||||
DecryptHeader();
|
||||
|
||||
var encUnityVersion = 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())
|
||||
{
|
||||
reader.ReadByte();
|
||||
}
|
||||
|
||||
if (Game.Type.IsNaraka())
|
||||
{
|
||||
m_Header.compressedBlocksInfoSize -= 0xCA;
|
||||
m_Header.uncompressedBlocksInfoSize -= 0xCA;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
@@ -298,6 +352,7 @@ namespace AssetStudio
|
||||
blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
|
||||
}
|
||||
MemoryStream blocksInfoUncompresseddStream;
|
||||
var blocksInfoBytesSpan = blocksInfoBytes.AsSpan();
|
||||
var uncompressedSize = m_Header.uncompressedBlocksInfoSize;
|
||||
var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask);
|
||||
switch (compressionType) //kArchiveCompressionTypeMask
|
||||
@@ -321,7 +376,7 @@ namespace AssetStudio
|
||||
case CompressionType.Lz4HC: //LZ4HC
|
||||
{
|
||||
var uncompressedBytes = new byte[uncompressedSize];
|
||||
var numWrite = LZ4Codec.Decode(blocksInfoBytes, uncompressedBytes);
|
||||
var numWrite = LZ4Codec.Decode(blocksInfoBytesSpan, uncompressedBytes);
|
||||
if (numWrite != uncompressedSize)
|
||||
{
|
||||
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
|
||||
@@ -330,10 +385,9 @@ namespace AssetStudio
|
||||
break;
|
||||
}
|
||||
case CompressionType.Lz4Mr0k: //Lz4Mr0k
|
||||
var blocksInfoSize = blocksInfoBytes.Length;
|
||||
if (Mr0k.IsMr0k(blocksInfoBytes))
|
||||
if (Mr0kUtils.IsMr0k(blocksInfoBytesSpan))
|
||||
{
|
||||
Mr0k.Decrypt(ref blocksInfoBytes, ref blocksInfoSize);
|
||||
blocksInfoBytesSpan = Mr0kUtils.Decrypt(blocksInfoBytesSpan, (Mr0k)Game).ToArray();
|
||||
}
|
||||
goto case CompressionType.Lz4HC;
|
||||
default:
|
||||
@@ -341,7 +395,7 @@ namespace AssetStudio
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
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);
|
||||
switch (compressionType) //kStorageBlockCompressionTypeMask
|
||||
{
|
||||
@@ -394,20 +453,28 @@ namespace AssetStudio
|
||||
case CompressionType.Lz4Mr0k: //Lz4Mr0k
|
||||
{
|
||||
var compressedSize = (int)blockInfo.compressedSize;
|
||||
var compressedBytes = new byte[compressedSize];
|
||||
var compressedBytes = BigArrayPool<byte>.Shared.Rent(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 uncompressedBytes = new byte[uncompressedSize];
|
||||
var numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize);
|
||||
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
|
||||
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
|
||||
var numWrite = LZ4Codec.Decode(compressedBytesSpan, 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);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -416,5 +483,11 @@ namespace AssetStudio
|
||||
}
|
||||
blocksStream.Position = 0;
|
||||
}
|
||||
|
||||
public int[] ParseVersion()
|
||||
{
|
||||
var versionSplit = Regex.Replace(m_Header.unityRevision, @"\D", ".").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return versionSplit.Select(int.Parse).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,17 +19,5 @@ namespace AssetStudio
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
public class Keyframe<T> : IYAMLExportable
|
||||
where T : struct, IYAMLExportable
|
||||
public class Keyframe<T>
|
||||
{
|
||||
public float time;
|
||||
public T value;
|
||||
@@ -15,7 +15,6 @@ namespace AssetStudio
|
||||
public int weightedMode;
|
||||
public T inWeight;
|
||||
public T outWeight;
|
||||
public int tangentMode;
|
||||
|
||||
|
||||
public Keyframe(ObjectReader reader, Func<T> readerFunc)
|
||||
@@ -31,46 +30,9 @@ namespace AssetStudio
|
||||
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
|
||||
where T : struct, IYAMLExportable
|
||||
public class AnimationCurve<T>
|
||||
{
|
||||
public Keyframe<T>[] m_Curve;
|
||||
public int m_PreInfinity;
|
||||
@@ -94,40 +56,9 @@ namespace AssetStudio
|
||||
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 string path;
|
||||
@@ -137,79 +68,9 @@ namespace AssetStudio
|
||||
curve = new AnimationCurve<Quaternion>(reader, reader.ReadQuaternion);
|
||||
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 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 class PackedFloatVector
|
||||
{
|
||||
public uint m_NumItems;
|
||||
public float m_Range;
|
||||
@@ -231,17 +92,6 @@ namespace AssetStudio
|
||||
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)
|
||||
{
|
||||
int bitPos = m_BitSize * start;
|
||||
@@ -281,7 +131,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
public class PackedIntVector : IYAMLExportable
|
||||
public class PackedIntVector
|
||||
{
|
||||
public uint m_NumItems;
|
||||
public byte[] m_Data;
|
||||
@@ -299,15 +149,6 @@ namespace AssetStudio
|
||||
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()
|
||||
{
|
||||
var data = new int[m_NumItems];
|
||||
@@ -335,7 +176,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
public class PackedQuatVector : IYAMLExportable
|
||||
public class PackedQuatVector
|
||||
{
|
||||
public uint m_NumItems;
|
||||
public byte[] m_Data;
|
||||
@@ -350,14 +191,6 @@ namespace AssetStudio
|
||||
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()
|
||||
{
|
||||
var data = new Quaternion[m_NumItems];
|
||||
@@ -423,7 +256,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
public class CompressedAnimationCurve : IYAMLExportable
|
||||
public class CompressedAnimationCurve
|
||||
{
|
||||
public string m_Path;
|
||||
public PackedIntVector m_Times;
|
||||
@@ -441,21 +274,9 @@ namespace AssetStudio
|
||||
m_PreInfinity = 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 string path;
|
||||
@@ -465,51 +286,11 @@ namespace AssetStudio
|
||||
curve = new AnimationCurve<Vector3>(reader, reader.ReadVector3);
|
||||
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()
|
||||
public class FloatCurve
|
||||
{
|
||||
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 AnimationCurve<Float> curve;
|
||||
public AnimationCurve<float> curve;
|
||||
public string attribute;
|
||||
public string path;
|
||||
public ClassIDType classID;
|
||||
@@ -518,64 +299,15 @@ namespace AssetStudio
|
||||
|
||||
public FloatCurve(ObjectReader reader)
|
||||
{
|
||||
curve = new AnimationCurve<Float>(reader, reader.ReadFloat);
|
||||
curve = new AnimationCurve<float>(reader, reader.ReadSingle);
|
||||
attribute = reader.ReadAlignedString();
|
||||
path = reader.ReadAlignedString();
|
||||
classID = (ClassIDType)reader.ReadInt32();
|
||||
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 PPtr<Object> value;
|
||||
@@ -586,27 +318,14 @@ namespace AssetStudio
|
||||
time = reader.ReadSingle();
|
||||
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 string attribute;
|
||||
public string path;
|
||||
public ClassIDType classID;
|
||||
public int classID;
|
||||
public PPtr<MonoScript> script;
|
||||
|
||||
|
||||
@@ -621,63 +340,12 @@ namespace AssetStudio
|
||||
|
||||
attribute = reader.ReadAlignedString();
|
||||
path = reader.ReadAlignedString();
|
||||
classID = (ClassIDType)reader.ReadInt32();
|
||||
classID = reader.ReadInt32();
|
||||
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_Extent;
|
||||
@@ -687,14 +355,6 @@ namespace AssetStudio
|
||||
m_Center = 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
|
||||
@@ -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 uint[] data;
|
||||
@@ -816,7 +503,7 @@ namespace AssetStudio
|
||||
public float outSlope;
|
||||
public float inSlope;
|
||||
|
||||
public StreamedCurveKey(BinaryReader reader)
|
||||
public StreamedCurveKey(EndianBinaryReader reader)
|
||||
{
|
||||
index = reader.ReadInt32();
|
||||
coeff = reader.ReadSingleArray(4);
|
||||
@@ -847,7 +534,7 @@ namespace AssetStudio
|
||||
public float time;
|
||||
public StreamedCurveKey[] keyList;
|
||||
|
||||
public StreamedFrame(BinaryReader reader)
|
||||
public StreamedFrame(EndianBinaryReader reader)
|
||||
{
|
||||
time = reader.ReadSingle();
|
||||
|
||||
@@ -865,7 +552,7 @@ namespace AssetStudio
|
||||
var frameList = new List<StreamedFrame>();
|
||||
var buffer = new byte[data.Length * 4];
|
||||
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)
|
||||
{
|
||||
@@ -959,10 +646,10 @@ namespace AssetStudio
|
||||
|
||||
public class Clip
|
||||
{
|
||||
public ACLClip m_ACLClip;
|
||||
public StreamedClip m_StreamedClip;
|
||||
public DenseClip m_DenseClip;
|
||||
public ConstantClip m_ConstantClip;
|
||||
public ACLClip m_ACLClip;
|
||||
public ValueArrayConstant m_Binding;
|
||||
|
||||
public Clip(ObjectReader reader)
|
||||
@@ -970,7 +657,7 @@ namespace AssetStudio
|
||||
var version = reader.version;
|
||||
m_StreamedClip = new StreamedClip(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);
|
||||
}
|
||||
@@ -978,7 +665,7 @@ namespace AssetStudio
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -1045,7 +732,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
public class ClipMuscleConstant : IYAMLExportable
|
||||
public class ClipMuscleConstant
|
||||
{
|
||||
public HumanPose m_DeltaPose;
|
||||
public xform m_StartX;
|
||||
@@ -1137,45 +824,23 @@ namespace AssetStudio
|
||||
m_HeightFromFeet = reader.ReadBoolean();
|
||||
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 attribute;
|
||||
public PPtr<Object> script;
|
||||
public ClassIDType typeID;
|
||||
public byte customType;
|
||||
public byte isPPtrCurve;
|
||||
public byte isIntCurve;
|
||||
|
||||
public GenericBinding() { }
|
||||
|
||||
public GenericBinding(ObjectReader reader)
|
||||
{
|
||||
version = reader.version;
|
||||
var version = reader.version;
|
||||
path = reader.ReadUInt32();
|
||||
attribute = reader.ReadUInt32();
|
||||
script = new PPtr<Object>(reader);
|
||||
@@ -1189,24 +854,15 @@ namespace AssetStudio
|
||||
}
|
||||
customType = reader.ReadByte();
|
||||
isPPtrCurve = reader.ReadByte();
|
||||
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 1)) //2022.1 and up
|
||||
{
|
||||
isIntCurve = reader.ReadByte();
|
||||
}
|
||||
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 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)
|
||||
{
|
||||
int curves = 0;
|
||||
@@ -1274,7 +922,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
public class AnimationEvent : IYAMLExportable
|
||||
public class AnimationEvent
|
||||
{
|
||||
public float time;
|
||||
public string functionName;
|
||||
@@ -1299,19 +947,6 @@ namespace AssetStudio
|
||||
}
|
||||
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
|
||||
@@ -1321,7 +956,7 @@ namespace AssetStudio
|
||||
Humanoid = 3
|
||||
};
|
||||
|
||||
public sealed class AnimationClip : NamedObject, IYAMLExportable
|
||||
public sealed class AnimationClip : NamedObject
|
||||
{
|
||||
public AnimationType m_AnimationType;
|
||||
public bool m_Legacy;
|
||||
@@ -1339,9 +974,6 @@ namespace AssetStudio
|
||||
public AABB m_Bounds;
|
||||
public uint m_MuscleClipSize;
|
||||
public ClipMuscleConstant m_MuscleClip;
|
||||
public byte[] m_AclClipData;
|
||||
public GenericBinding[] m_AclBindings;
|
||||
public KeyValuePair<float, float> m_AclRange;
|
||||
public AnimationClipBindingConstant m_ClipBindingConstant;
|
||||
public AnimationEvent[] m_Events;
|
||||
|
||||
@@ -1363,10 +995,6 @@ namespace AssetStudio
|
||||
m_Legacy = true;
|
||||
}
|
||||
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
|
||||
{
|
||||
m_UseHighQualityCurve = reader.ReadBoolean();
|
||||
@@ -1438,19 +1066,19 @@ namespace AssetStudio
|
||||
m_MuscleClipSize = reader.ReadUInt32();
|
||||
m_MuscleClip = new ClipMuscleConstant(reader);
|
||||
}
|
||||
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
|
||||
if (reader.Game.Type.IsSRGroup())
|
||||
{
|
||||
if (reader.Game.Name == "SR_CB2" || reader.Game.Name == "SR_CB3")
|
||||
{
|
||||
m_AclClipData = reader.ReadUInt8Array();
|
||||
var m_AclClipData = reader.ReadUInt8Array();
|
||||
var aclBindingsCount = reader.ReadInt32();
|
||||
m_AclBindings = new GenericBinding[aclBindingsCount];
|
||||
var 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());
|
||||
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
|
||||
{
|
||||
m_ClipBindingConstant = new AnimationClipBindingConstant(reader);
|
||||
}
|
||||
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 3)) //2018.3 and up
|
||||
@@ -1470,158 +1098,5 @@ namespace AssetStudio
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace AssetStudio
|
||||
{
|
||||
m_Avatar = new PPtr<Avatar>(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
|
||||
}
|
||||
@@ -67,31 +67,5 @@ namespace AssetStudio
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,6 +580,7 @@ namespace AssetStudio
|
||||
|
||||
public sealed class AnimatorController : RuntimeAnimatorController
|
||||
{
|
||||
public Dictionary<uint, string> m_TOS;
|
||||
public PPtr<AnimationClip>[] m_AnimationClips;
|
||||
|
||||
public AnimatorController(ObjectReader reader) : base(reader)
|
||||
@@ -588,7 +589,7 @@ namespace AssetStudio
|
||||
var m_Controller = new ControllerConstant(reader);
|
||||
|
||||
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++)
|
||||
{
|
||||
m_TOS.Add(reader.ReadUInt32(), reader.ReadAlignedString());
|
||||
@@ -601,17 +602,5 @@ namespace AssetStudio
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,26 +33,5 @@ namespace AssetStudio
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -19,76 +22,64 @@ namespace AssetStudio
|
||||
|
||||
public sealed class AssetBundle : NamedObject
|
||||
{
|
||||
public static bool Exportable;
|
||||
|
||||
public PPtr<Object>[] PreloadTable;
|
||||
public KeyValuePair<string, AssetInfo>[] Container;
|
||||
public AssetInfo MainAsset;
|
||||
public uint RuntimeComaptability;
|
||||
public string AssetBundleName;
|
||||
public int DependencyCount;
|
||||
public string[] Dependencies;
|
||||
public bool IsStreamedScenessetBundle;
|
||||
public int ExplicitDataLayout;
|
||||
public int PathFlags;
|
||||
public int SceneHashCount;
|
||||
public KeyValuePair<string, string>[] SceneHashes;
|
||||
public PPtr<Object>[] m_PreloadTable;
|
||||
public KeyValuePair<string, AssetInfo>[] m_Container;
|
||||
//public AssetInfo m_MainAsset;
|
||||
//public uint m_RuntimeComaptability;
|
||||
//public string m_AssetBundleName;
|
||||
//public string[] m_Dependencies;
|
||||
//public bool m_IsStreamedSceneAssetBundle;
|
||||
//public int m_ExplicitDataLayout;
|
||||
//public int m_PathFlags;
|
||||
//public KeyValuePair<string, string>[] m_SceneHashes;
|
||||
|
||||
public AssetBundle(ObjectReader reader) : base(reader)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
PreloadTable[i] = new PPtr<Object>(reader);
|
||||
m_PreloadTable[i] = new PPtr<Object>(reader);
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
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")
|
||||
{
|
||||
MainAsset = new AssetInfo(reader);
|
||||
RuntimeComaptability = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
AssetBundleName = reader.ReadAlignedString();
|
||||
DependencyCount = reader.ReadInt32();
|
||||
Dependencies = new string[DependencyCount];
|
||||
for (int k = 0; k < DependencyCount; k++)
|
||||
{
|
||||
Dependencies[k] = reader.ReadAlignedString();
|
||||
}
|
||||
if (reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2")
|
||||
{
|
||||
IsStreamedScenessetBundle = reader.ReadBoolean();
|
||||
reader.AlignStream();
|
||||
PathFlags = reader.ReadInt32();
|
||||
}
|
||||
else if (reader.Game.Name == "GI_CB3")
|
||||
{
|
||||
IsStreamedScenessetBundle = reader.ReadBoolean();
|
||||
reader.AlignStream();
|
||||
ExplicitDataLayout = reader.ReadInt32();
|
||||
PathFlags = reader.ReadInt32();
|
||||
}
|
||||
else if (reader.Game.Name == "GI")
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
//if (reader.Game.Type.IsMhyGroup())
|
||||
//{
|
||||
// m_MainAsset = new AssetInfo(reader);
|
||||
// m_RuntimeComaptability = reader.ReadUInt32();
|
||||
// m_AssetBundleName = reader.ReadAlignedString();
|
||||
// var dependencyCount = reader.ReadInt32();
|
||||
// m_Dependencies = new string[dependencyCount];
|
||||
// for (int k = 0; k < dependencyCount; k++)
|
||||
// {
|
||||
// m_Dependencies[k] = reader.ReadAlignedString();
|
||||
// }
|
||||
// if (reader.Game.Type.IsGIGroup())
|
||||
// {
|
||||
// m_IsStreamedSceneAssetBundle = reader.ReadBoolean();
|
||||
// reader.AlignStream();
|
||||
// if (reader.Game.Type.IsGICB3() || reader.Game.Type.IsGI())
|
||||
// {
|
||||
// m_ExplicitDataLayout = reader.ReadInt32();
|
||||
// }
|
||||
// m_PathFlags = reader.ReadInt32();
|
||||
// if (reader.Game.Type.IsGI())
|
||||
// {
|
||||
// var sceneHashCount = reader.ReadInt32();
|
||||
// m_SceneHashes = new KeyValuePair<string, string>[sceneHashCount];
|
||||
// for (int l = 0; l < sceneHashCount; l++)
|
||||
// {
|
||||
// m_SceneHashes[l] = new KeyValuePair<string, string>(reader.ReadAlignedString(), reader.ReadAlignedString());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using SevenZip;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -10,8 +9,6 @@ namespace AssetStudio
|
||||
{
|
||||
public PPtr<Component>[] m_Components;
|
||||
public string m_Name;
|
||||
public uint m_Tag;
|
||||
public bool m_IsActive;
|
||||
|
||||
public Transform m_Transform;
|
||||
public MeshRenderer m_MeshRenderer;
|
||||
@@ -35,67 +32,6 @@ namespace AssetStudio
|
||||
|
||||
var m_Layer = reader.ReadInt32();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -10,7 +9,6 @@ namespace AssetStudio
|
||||
|
||||
public Index(ObjectReader reader)
|
||||
{
|
||||
|
||||
Object = new PPtr<Object>(reader);
|
||||
Size = reader.ReadUInt64();
|
||||
}
|
||||
@@ -18,25 +16,16 @@ namespace AssetStudio
|
||||
|
||||
public sealed class IndexObject : NamedObject
|
||||
{
|
||||
public static bool Exportable;
|
||||
|
||||
public int Count;
|
||||
public Dictionary<string, Index> AssetMap;
|
||||
public Dictionary<long, string> Names = new Dictionary<long, string>();
|
||||
public KeyValuePair<string, Index>[] AssetMap;
|
||||
|
||||
public IndexObject(ObjectReader reader) : base(reader)
|
||||
{
|
||||
Count = reader.ReadInt32();
|
||||
AssetMap = new Dictionary<string, Index>(Count);
|
||||
AssetMap = new KeyValuePair<string, Index>[Count];
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
var key = reader.ReadAlignedString();
|
||||
var value = new Index(reader);
|
||||
|
||||
AssetMap.Add(key, value);
|
||||
|
||||
if (value.Object.m_FileID == 0)
|
||||
Names.Add(value.Object.m_PathID, key);
|
||||
AssetMap[i] = new KeyValuePair<string, Index>(reader.ReadAlignedString(), new Index(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SevenZip;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -21,94 +18,52 @@ namespace AssetStudio
|
||||
|
||||
public class UnityPropertySheet
|
||||
{
|
||||
private const string HDRPostfixName = "_HDR";
|
||||
private const string STPostfixName = "_ST";
|
||||
private const string TexelSizePostfixName = "_TexelSize";
|
||||
|
||||
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 KeyValuePair<string, UnityTexEnv>[] m_TexEnvs;
|
||||
public KeyValuePair<string, int>[] m_Ints;
|
||||
public KeyValuePair<string, float>[] m_Floats;
|
||||
public KeyValuePair<string, Color>[] m_Colors;
|
||||
|
||||
public UnityPropertySheet(ObjectReader reader)
|
||||
{
|
||||
var version = reader.version;
|
||||
|
||||
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++)
|
||||
{
|
||||
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
|
||||
{
|
||||
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++)
|
||||
{
|
||||
m_Ints.Add(reader.ReadAlignedString(), reader.ReadInt32());
|
||||
m_Ints[i] = new KeyValuePair<string, int>(reader.ReadAlignedString(), 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++)
|
||||
{
|
||||
m_Floats.Add(reader.ReadAlignedString(), reader.ReadSingle());
|
||||
m_Floats[i] = new KeyValuePair<string, float>(reader.ReadAlignedString(), reader.ReadSingle());
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
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 PPtr<Shader> m_Shader;
|
||||
public UnityPropertySheet m_SavedProperties;
|
||||
public Dictionary<string, string> m_StringTagMap;
|
||||
|
||||
public Material(ObjectReader reader) : base(reader)
|
||||
{
|
||||
@@ -119,16 +74,25 @@ namespace AssetStudio
|
||||
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();
|
||||
}
|
||||
|
||||
if (version[0] >= 5) //5.0 and up
|
||||
{
|
||||
var m_LightmapFlags = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up
|
||||
{
|
||||
var m_EnableInstancingVariants = reader.ReadBoolean();
|
||||
var m_DoubleSidedGI = reader.ReadBoolean(); //2017 and up
|
||||
//var m_DoubleSidedGI = a_Stream.ReadBoolean(); //2017 and up
|
||||
reader.AlignStream();
|
||||
}
|
||||
|
||||
@@ -140,15 +104,18 @@ namespace AssetStudio
|
||||
if (version[0] > 5 || (version[0] == 5 && version[1] >= 1)) //5.1 and up
|
||||
{
|
||||
var stringTagMapSize = reader.ReadInt32();
|
||||
m_StringTagMap = new Dictionary<string, string>(stringTagMapSize);
|
||||
for (int i = 0; i < stringTagMapSize; i++)
|
||||
{
|
||||
var first = 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
|
||||
{
|
||||
var disabledShaderPasses = reader.ReadStringArray();
|
||||
@@ -158,10 +125,5 @@ namespace AssetStudio
|
||||
|
||||
//vector m_BuildTextureStacks 2020 and up
|
||||
}
|
||||
|
||||
public string FindPropertyNameByCRC28(uint crc)
|
||||
{
|
||||
return m_SavedProperties.FindPropertyNameByCRC28(crc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using SevenZip;
|
||||
using System;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -15,7 +12,7 @@ namespace AssetStudio
|
||||
public Vector3 m_Min;
|
||||
public Vector3 m_Max;
|
||||
|
||||
public MinMaxAABB(BinaryReader reader)
|
||||
public MinMaxAABB(EndianBinaryReader reader)
|
||||
{
|
||||
m_Min = reader.ReadVector3();
|
||||
m_Max = reader.ReadVector3();
|
||||
@@ -300,7 +297,6 @@ namespace AssetStudio
|
||||
|
||||
public class MeshBlendShape
|
||||
{
|
||||
public string name;
|
||||
public uint firstVertex;
|
||||
public uint vertexCount;
|
||||
public bool hasNormals;
|
||||
@@ -312,7 +308,7 @@ namespace AssetStudio
|
||||
|
||||
if (version[0] == 4 && version[1] < 3) //4.3 down
|
||||
{
|
||||
name = reader.ReadAlignedString();
|
||||
var name = reader.ReadAlignedString();
|
||||
}
|
||||
firstVertex = reader.ReadUInt32();
|
||||
vertexCount = reader.ReadUInt32();
|
||||
@@ -328,11 +324,6 @@ namespace AssetStudio
|
||||
reader.AlignStream();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCRCMatch(uint digest)
|
||||
{
|
||||
return CRC.VerifyDigestUTF8(name, digest);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
TriangleStrip = 1,
|
||||
@@ -560,7 +540,7 @@ namespace AssetStudio
|
||||
var m_StreamCompression = reader.ReadByte();
|
||||
}
|
||||
var m_IsReadable = reader.ReadBoolean();
|
||||
if (reader.Game.Name == "BH3")
|
||||
if (reader.Game.Type.IsBH3())
|
||||
{
|
||||
var m_IsHighPrecisionPosition = reader.ReadBoolean();
|
||||
var m_IsHighPrecisionTangent = reader.ReadBoolean();
|
||||
@@ -568,9 +548,9 @@ namespace AssetStudio
|
||||
}
|
||||
var m_KeepVertices = 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();
|
||||
reader.AlignStream();
|
||||
@@ -665,7 +645,7 @@ namespace AssetStudio
|
||||
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
|
||||
{
|
||||
@@ -683,19 +663,24 @@ namespace AssetStudio
|
||||
|
||||
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
|
||||
{
|
||||
var m_BakedConvexCollisionMesh = reader.ReadUInt8Array();
|
||||
reader.AlignStream();
|
||||
var m_BakedTriangleCollisionMesh = reader.ReadUInt8Array();
|
||||
reader.AlignStream();
|
||||
if (reader.Game.Name == "BH3")
|
||||
if (reader.Game.Type.IsBH3())
|
||||
{
|
||||
var m_MeshOptimized = reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.Game.Name == "ZZZ_CB1")
|
||||
if (reader.Game.Type.IsZZZCB1())
|
||||
{
|
||||
var m_CloseMeshDynamicCompression = reader.ReadBoolean();
|
||||
reader.AlignStream();
|
||||
@@ -705,21 +690,20 @@ namespace AssetStudio
|
||||
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"
|
||||
|| version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
|
||||
if (reader.Game.Type.IsGIGroup() || version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
|
||||
{
|
||||
var m_MeshMetrics = new float[2];
|
||||
m_MeshMetrics[0] = 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();
|
||||
reader.AlignStream();
|
||||
var m_CloseMeshDynamicCompression = reader.ReadBoolean();
|
||||
reader.AlignStream();
|
||||
if (reader.Game.Name != "GI_CB1")
|
||||
if (!reader.Game.Type.IsGICB1() && !reader.Game.Type.IsGIPack())
|
||||
{
|
||||
var m_IsStreamingMesh = reader.ReadBoolean();
|
||||
reader.AlignStream();
|
||||
@@ -830,13 +814,9 @@ namespace AssetStudio
|
||||
m_UV1 = componentsFloatArray;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
case 8: //kShaderChannelTexCoord4
|
||||
@@ -900,8 +880,6 @@ namespace AssetStudio
|
||||
m_UV1 = componentsFloatArray;
|
||||
break;
|
||||
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
|
||||
{
|
||||
m_UV2 = componentsFloatArray;
|
||||
@@ -912,8 +890,6 @@ namespace AssetStudio
|
||||
}
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
case 7: //kShaderChannelTangent
|
||||
@@ -1255,25 +1231,6 @@ namespace AssetStudio
|
||||
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
|
||||
@@ -1419,7 +1376,7 @@ namespace AssetStudio
|
||||
switch (format)
|
||||
{
|
||||
case VertexFormat.Float:
|
||||
result[i] = BitConverter.ToSingle(inputBytes, i * 4);
|
||||
result[i] = BinaryPrimitives.ReadSingleLittleEndian(inputBytes.AsSpan(i * 4));
|
||||
break;
|
||||
case VertexFormat.Float16:
|
||||
result[i] = Half.ToHalf(inputBytes, i * 2);
|
||||
@@ -1431,10 +1388,10 @@ namespace AssetStudio
|
||||
result[i] = Math.Max((sbyte)inputBytes[i] / 127f, -1f);
|
||||
break;
|
||||
case VertexFormat.UNorm16:
|
||||
result[i] = BitConverter.ToUInt16(inputBytes, i * 2) / 65535f;
|
||||
result[i] = BinaryPrimitives.ReadUInt16LittleEndian(inputBytes.AsSpan(i * 2)) / 65535f;
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1456,11 +1413,11 @@ namespace AssetStudio
|
||||
break;
|
||||
case VertexFormat.UInt16:
|
||||
case VertexFormat.SInt16:
|
||||
result[i] = BitConverter.ToInt16(inputBytes, i * 2);
|
||||
result[i] = BinaryPrimitives.ReadInt16LittleEndian(inputBytes.AsSpan(i * 2));
|
||||
break;
|
||||
case VertexFormat.UInt32:
|
||||
case VertexFormat.SInt32:
|
||||
result[i] = BitConverter.ToInt32(inputBytes, i * 4);
|
||||
result[i] = BinaryPrimitives.ReadInt32LittleEndian(inputBytes.AsSpan(i * 4));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ namespace AssetStudio
|
||||
{
|
||||
public sealed class MeshRenderer : Renderer
|
||||
{
|
||||
public PPtr<Mesh> m_AdditionalVertexStreams;
|
||||
public MeshRenderer(ObjectReader reader) : base(reader)
|
||||
{
|
||||
var m_AdditionalVertexStreams = new PPtr<Mesh>(reader);
|
||||
m_AdditionalVertexStreams = new PPtr<Mesh>(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -12,17 +12,57 @@ namespace AssetStudio
|
||||
Bytes,
|
||||
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 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
|
||||
{
|
||||
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];
|
||||
for (int i = 0; i < RawData.Length; i++)
|
||||
@@ -35,59 +75,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
public string Str
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
[GeneratedRegex("[^\\u0020-\\u007E]")]
|
||||
private static partial Regex Chars();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using static AssetStudio.AssetsHelper;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
public sealed class PPtr<T> : IYAMLExportable
|
||||
where T : Object
|
||||
public sealed class PPtr<T> where T : Object
|
||||
{
|
||||
public int m_FileID;
|
||||
public long m_PathID;
|
||||
@@ -18,21 +20,6 @@ namespace AssetStudio
|
||||
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)
|
||||
{
|
||||
result = null;
|
||||
@@ -142,11 +129,6 @@ namespace AssetStudio
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -20,11 +19,13 @@ namespace AssetStudio
|
||||
|
||||
public abstract class Renderer : Component
|
||||
{
|
||||
public static bool Parsable;
|
||||
public static bool Skipped;
|
||||
|
||||
public PPtr<Material>[] m_Materials;
|
||||
public StaticBatchInfo m_StaticBatchInfo;
|
||||
public uint[] m_SubsetIndices;
|
||||
private bool isNewHeader = false;
|
||||
|
||||
protected Renderer(ObjectReader reader) : base(reader)
|
||||
{
|
||||
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 (reader.Game.Name == "GI")
|
||||
if (reader.Game.Type.IsGI())
|
||||
{
|
||||
CheckHeader(reader);
|
||||
}
|
||||
@@ -48,39 +49,44 @@ namespace AssetStudio
|
||||
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
|
||||
{
|
||||
var m_DynamicOccludee = reader.ReadByte();
|
||||
if (reader.Game.Name == "BH3")
|
||||
}
|
||||
if (reader.Game.Type.IsBH3())
|
||||
{
|
||||
var m_AllowHalfResolution = reader.ReadByte();
|
||||
}
|
||||
else if (reader.Game.Name == "GI_CB3")
|
||||
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();
|
||||
var m_IsRainOccluder = reader.ReadByte();
|
||||
var m_IsDynamicAOOccluder = reader.ReadByte();
|
||||
var m_IsDynamic = reader.ReadByte();
|
||||
}
|
||||
else if (reader.Game.Name == "GI")
|
||||
if (!reader.Game.Type.IsGICB1())
|
||||
{
|
||||
var m_ReceiveDecals = reader.ReadByte();
|
||||
var m_EnableShadowCulling = reader.ReadByte();
|
||||
var m_EnableGpuQuery = reader.ReadByte();
|
||||
var m_AllowHalfResolution = reader.ReadByte();
|
||||
if (reader.Game.Type.IsGI())
|
||||
{
|
||||
var m_AllowPerMaterialProp = isNewHeader ? reader.ReadByte() : 0;
|
||||
}
|
||||
var m_IsRainOccluder = 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();
|
||||
var m_IsDynamic = reader.ReadByte();
|
||||
var m_UseTessellation = reader.ReadByte();
|
||||
var m_IsTerrainTessInfo = reader.ReadByte();
|
||||
if (isNewHeader)
|
||||
}
|
||||
}
|
||||
if (!reader.Game.Type.IsGIPack())
|
||||
{
|
||||
var m_UseVertexLightInForward = reader.ReadByte();
|
||||
var m_CombineSubMeshInGeoPass = reader.ReadByte();
|
||||
var m_AllowPerMaterialProp = reader.ReadByte();
|
||||
var m_IsHQDynamicAOOccluder = reader.ReadByte();
|
||||
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();
|
||||
}
|
||||
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_LightProbeUsage = reader.ReadByte();
|
||||
var m_ReflectionProbeUsage = reader.ReadByte();
|
||||
@@ -115,7 +105,7 @@ namespace AssetStudio
|
||||
{
|
||||
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();
|
||||
}
|
||||
@@ -140,11 +130,10 @@ namespace AssetStudio
|
||||
var m_RendererPriority = reader.ReadInt32();
|
||||
}
|
||||
|
||||
var m_LightmapIndex = reader.ReadInt16();
|
||||
var m_LightmapIndexDynamic = reader.ReadInt16();
|
||||
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB1" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3")
|
||||
var m_LightmapIndex = reader.ReadUInt16();
|
||||
var m_LightmapIndexDynamic = reader.ReadUInt16();
|
||||
if (reader.Game.Type.IsGIGroup() && (m_LightmapIndex != 0xFFFF || m_LightmapIndexDynamic != 0xFFFF))
|
||||
{
|
||||
if (m_LightmapIndex != -1 || m_LightmapIndexDynamic != -1)
|
||||
throw new Exception("Not Supported !! skipping....");
|
||||
}
|
||||
}
|
||||
@@ -159,7 +148,7 @@ namespace AssetStudio
|
||||
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_ShaderLODDistanceRatio = reader.ReadSingle();
|
||||
@@ -188,7 +177,8 @@ namespace AssetStudio
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -219,21 +209,20 @@ namespace AssetStudio
|
||||
}
|
||||
else
|
||||
{
|
||||
var m_SortingLayerID = reader.ReadInt32();
|
||||
var m_SortingLayer = reader.ReadInt16();
|
||||
var m_SortingLayerID = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
//SInt16 m_SortingLayer 5.6 and up
|
||||
var m_SortingOrder = reader.ReadInt16();
|
||||
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();
|
||||
reader.AlignStream();
|
||||
}
|
||||
if (reader.Game.Name == "SR_CB3")
|
||||
if (reader.Game.Type.IsSRCB3())
|
||||
{
|
||||
var _RenderFlag = reader.ReadUInt32();
|
||||
var RenderFlag = reader.ReadUInt32();
|
||||
reader.AlignStream();
|
||||
}
|
||||
}
|
||||
@@ -241,31 +230,14 @@ namespace AssetStudio
|
||||
|
||||
private void CheckHeader(ObjectReader reader)
|
||||
{
|
||||
short index = 0;
|
||||
short value = 0;
|
||||
var pos = reader.Position;
|
||||
while (index != -1 && reader.Position <= pos + 0x1A)
|
||||
index = reader.ReadInt16();
|
||||
while (value != -1 && reader.Position <= pos + 0x1A)
|
||||
{
|
||||
value = reader.ReadInt16();
|
||||
}
|
||||
isNewHeader = (reader.Position - pos) == 0x1A;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,5 @@ namespace AssetStudio
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public abstract bool IsContainsAnimationClip(AnimationClip clip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -9,7 +10,7 @@ namespace AssetStudio
|
||||
{
|
||||
public byte[] bytes;
|
||||
|
||||
public Hash128(BinaryReader reader)
|
||||
public Hash128(EndianBinaryReader reader)
|
||||
{
|
||||
bytes = reader.ReadBytes(16);
|
||||
}
|
||||
@@ -20,7 +21,7 @@ namespace AssetStudio
|
||||
public MatrixParameter[] m_MatrixParams;
|
||||
public VectorParameter[] m_VectorParams;
|
||||
|
||||
public StructParameter(BinaryReader reader)
|
||||
public StructParameter(EndianBinaryReader reader)
|
||||
{
|
||||
var m_NameIndex = reader.ReadInt32();
|
||||
var m_Index = reader.ReadInt32();
|
||||
@@ -48,7 +49,7 @@ namespace AssetStudio
|
||||
public uint sampler;
|
||||
public int bindPoint;
|
||||
|
||||
public SamplerParameter(BinaryReader reader)
|
||||
public SamplerParameter(EndianBinaryReader reader)
|
||||
{
|
||||
sampler = reader.ReadUInt32();
|
||||
bindPoint = reader.ReadInt32();
|
||||
@@ -71,7 +72,7 @@ namespace AssetStudio
|
||||
public string m_DefaultName;
|
||||
public TextureDimension m_TexDim;
|
||||
|
||||
public SerializedTextureProperty(BinaryReader reader)
|
||||
public SerializedTextureProperty(EndianBinaryReader reader)
|
||||
{
|
||||
m_DefaultName = reader.ReadAlignedString();
|
||||
m_TexDim = (TextureDimension)reader.ReadInt32();
|
||||
@@ -98,7 +99,7 @@ namespace AssetStudio
|
||||
public float[] m_DefValue;
|
||||
public SerializedTextureProperty m_DefTexture;
|
||||
|
||||
public SerializedProperty(BinaryReader reader)
|
||||
public SerializedProperty(EndianBinaryReader reader)
|
||||
{
|
||||
m_Name = reader.ReadAlignedString();
|
||||
m_Description = reader.ReadAlignedString();
|
||||
@@ -114,7 +115,7 @@ namespace AssetStudio
|
||||
{
|
||||
public SerializedProperty[] m_Props;
|
||||
|
||||
public SerializedProperties(BinaryReader reader)
|
||||
public SerializedProperties(EndianBinaryReader reader)
|
||||
{
|
||||
int numProps = reader.ReadInt32();
|
||||
m_Props = new SerializedProperty[numProps];
|
||||
@@ -130,7 +131,7 @@ namespace AssetStudio
|
||||
public float val;
|
||||
public string name;
|
||||
|
||||
public SerializedShaderFloatValue(BinaryReader reader)
|
||||
public SerializedShaderFloatValue(EndianBinaryReader reader)
|
||||
{
|
||||
val = reader.ReadSingle();
|
||||
name = reader.ReadAlignedString();
|
||||
@@ -147,7 +148,7 @@ namespace AssetStudio
|
||||
public SerializedShaderFloatValue blendOpAlpha;
|
||||
public SerializedShaderFloatValue colMask;
|
||||
|
||||
public SerializedShaderRTBlendState(BinaryReader reader)
|
||||
public SerializedShaderRTBlendState(EndianBinaryReader reader)
|
||||
{
|
||||
srcBlend = new SerializedShaderFloatValue(reader);
|
||||
destBlend = new SerializedShaderFloatValue(reader);
|
||||
@@ -166,7 +167,7 @@ namespace AssetStudio
|
||||
public SerializedShaderFloatValue zFail;
|
||||
public SerializedShaderFloatValue comp;
|
||||
|
||||
public SerializedStencilOp(BinaryReader reader)
|
||||
public SerializedStencilOp(EndianBinaryReader reader)
|
||||
{
|
||||
pass = new SerializedShaderFloatValue(reader);
|
||||
fail = new SerializedShaderFloatValue(reader);
|
||||
@@ -183,7 +184,7 @@ namespace AssetStudio
|
||||
public SerializedShaderFloatValue w;
|
||||
public string name;
|
||||
|
||||
public SerializedShaderVectorValue(BinaryReader reader)
|
||||
public SerializedShaderVectorValue(EndianBinaryReader reader)
|
||||
{
|
||||
x = new SerializedShaderFloatValue(reader);
|
||||
y = new SerializedShaderFloatValue(reader);
|
||||
@@ -281,7 +282,7 @@ namespace AssetStudio
|
||||
public sbyte source;
|
||||
public sbyte target;
|
||||
|
||||
public ShaderBindChannel(BinaryReader reader)
|
||||
public ShaderBindChannel(EndianBinaryReader reader)
|
||||
{
|
||||
source = reader.ReadSByte();
|
||||
target = reader.ReadSByte();
|
||||
@@ -293,7 +294,7 @@ namespace AssetStudio
|
||||
public ShaderBindChannel[] m_Channels;
|
||||
public uint m_SourceMap;
|
||||
|
||||
public ParserBindChannels(BinaryReader reader)
|
||||
public ParserBindChannels(EndianBinaryReader reader)
|
||||
{
|
||||
int numChannels = reader.ReadInt32();
|
||||
m_Channels = new ShaderBindChannel[numChannels];
|
||||
@@ -315,7 +316,7 @@ namespace AssetStudio
|
||||
public sbyte m_Type;
|
||||
public sbyte m_Dim;
|
||||
|
||||
public VectorParameter(BinaryReader reader)
|
||||
public VectorParameter(EndianBinaryReader reader)
|
||||
{
|
||||
m_NameIndex = reader.ReadInt32();
|
||||
m_Index = reader.ReadInt32();
|
||||
@@ -334,7 +335,7 @@ namespace AssetStudio
|
||||
public sbyte m_Type;
|
||||
public sbyte m_RowCount;
|
||||
|
||||
public MatrixParameter(BinaryReader reader)
|
||||
public MatrixParameter(EndianBinaryReader reader)
|
||||
{
|
||||
m_NameIndex = reader.ReadInt32();
|
||||
m_Index = reader.ReadInt32();
|
||||
@@ -428,6 +429,7 @@ namespace AssetStudio
|
||||
|
||||
if ((version[0] == 2020 && version[1] > 3) ||
|
||||
(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[2] >= 4)) //2021.1.4f1 and up
|
||||
{
|
||||
@@ -443,7 +445,7 @@ namespace AssetStudio
|
||||
public int m_Index;
|
||||
public int m_OriginalIndex;
|
||||
|
||||
public UAVParameter(BinaryReader reader)
|
||||
public UAVParameter(EndianBinaryReader reader)
|
||||
{
|
||||
m_NameIndex = reader.ReadInt32();
|
||||
m_Index = reader.ReadInt32();
|
||||
@@ -605,6 +607,7 @@ namespace AssetStudio
|
||||
|
||||
if ((version[0] == 2020 && version[1] > 3) ||
|
||||
(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[2] >= 1)) //2021.1.1f1 and up
|
||||
{
|
||||
@@ -690,6 +693,7 @@ namespace AssetStudio
|
||||
{
|
||||
public SerializedSubProgram[] m_SubPrograms;
|
||||
public SerializedProgramParameters m_CommonParameters;
|
||||
public ushort[] m_SerializedKeywordStateMask;
|
||||
|
||||
public SerializedProgram(ObjectReader reader)
|
||||
{
|
||||
@@ -704,11 +708,18 @@ namespace AssetStudio
|
||||
|
||||
if ((version[0] == 2020 && version[1] > 3) ||
|
||||
(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[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);
|
||||
}
|
||||
|
||||
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_TextureName = reader.ReadAlignedString();
|
||||
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();
|
||||
reader.AlignStream();
|
||||
@@ -807,7 +818,7 @@ namespace AssetStudio
|
||||
{
|
||||
public KeyValuePair<string, string>[] tags;
|
||||
|
||||
public SerializedTagMap(BinaryReader reader)
|
||||
public SerializedTagMap(EndianBinaryReader reader)
|
||||
{
|
||||
int numTags = reader.ReadInt32();
|
||||
tags = new KeyValuePair<string, string>[numTags];
|
||||
@@ -843,7 +854,7 @@ namespace AssetStudio
|
||||
public string from;
|
||||
public string to;
|
||||
|
||||
public SerializedShaderDependency(BinaryReader reader)
|
||||
public SerializedShaderDependency(EndianBinaryReader reader)
|
||||
{
|
||||
from = reader.ReadAlignedString();
|
||||
to = reader.ReadAlignedString();
|
||||
@@ -855,7 +866,7 @@ namespace AssetStudio
|
||||
public string customEditorName;
|
||||
public string renderPipelineType;
|
||||
|
||||
public SerializedCustomEditorForRenderPipeline(BinaryReader reader)
|
||||
public SerializedCustomEditorForRenderPipeline(EndianBinaryReader reader)
|
||||
{
|
||||
customEditorName = reader.ReadAlignedString();
|
||||
renderPipelineType = reader.ReadAlignedString();
|
||||
@@ -953,7 +964,6 @@ namespace AssetStudio
|
||||
|
||||
public class Shader : NamedObject
|
||||
{
|
||||
public static bool Parsable;
|
||||
public byte[] m_Script;
|
||||
//5.3 - 5.4
|
||||
public uint decompressedSize;
|
||||
@@ -985,14 +995,14 @@ namespace AssetStudio
|
||||
decompressedLengths = reader.ReadUInt32Array().Select(x => new[] { x }).ToArray();
|
||||
}
|
||||
compressedBlob = reader.ReadUInt8Array();
|
||||
if (reader.Game.Name == "GI" || reader.Game.Name == "GI_CB2" || reader.Game.Name == "GI_CB3")
|
||||
{
|
||||
if (BitConverter.ToInt32(compressedBlob, 0) == -1)
|
||||
compressedBlob = reader.ReadUInt8Array();
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.AlignStream();
|
||||
if (reader.Game.Type.IsGISubGroup())
|
||||
{
|
||||
if (BinaryPrimitives.ReadInt32LittleEndian(compressedBlob) == -1)
|
||||
{
|
||||
compressedBlob = reader.ReadUInt8Array(); //blobDataBlocks
|
||||
reader.AlignStream();
|
||||
}
|
||||
}
|
||||
|
||||
var m_DependenciesCount = reader.ReadInt32();
|
||||
|
||||
@@ -9,11 +9,12 @@ namespace AssetStudio
|
||||
{
|
||||
public PPtr<Mesh> m_Mesh;
|
||||
public PPtr<Transform>[] m_Bones;
|
||||
public PPtr<Transform> m_RootBone;
|
||||
public float[] m_BlendShapeWeights;
|
||||
public PPtr<Transform> m_RootBone;
|
||||
public AABB m_AABB;
|
||||
public bool m_DirtyAABB;
|
||||
|
||||
|
||||
public SkinnedMeshRenderer(ObjectReader reader) : base(reader)
|
||||
{
|
||||
int m_Quality = reader.ReadInt32();
|
||||
@@ -33,16 +34,19 @@ namespace AssetStudio
|
||||
{
|
||||
m_Bones[b] = new PPtr<Transform>(reader);
|
||||
}
|
||||
reader.AlignStream();
|
||||
|
||||
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
|
||||
{
|
||||
m_BlendShapeWeights = reader.ReadSingleArray();
|
||||
}
|
||||
reader.AlignStream();
|
||||
|
||||
if (reader.Game.Type.IsGIGroup())
|
||||
{
|
||||
m_RootBone = new PPtr<Transform>(reader);
|
||||
m_AABB = new AABB(reader);
|
||||
m_DirtyAABB = reader.ReadBoolean();
|
||||
reader.AlignStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -134,9 +133,13 @@ namespace AssetStudio
|
||||
|
||||
ResourceReader resourceReader;
|
||||
if (!string.IsNullOrEmpty(m_StreamData?.path))
|
||||
{
|
||||
resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size);
|
||||
}
|
||||
else
|
||||
{
|
||||
resourceReader = new ResourceReader(reader, reader.BaseStream.Position, image_data_size);
|
||||
}
|
||||
image_data = resourceReader;
|
||||
}
|
||||
}
|
||||
@@ -204,7 +207,7 @@ namespace AssetStudio
|
||||
R8,
|
||||
ETC_RGB4Crunched,
|
||||
ETC2_RGBA8Crunched,
|
||||
R16_2,
|
||||
R16_Alt,
|
||||
ASTC_HDR_4x4,
|
||||
ASTC_HDR_5x5,
|
||||
ASTC_HDR_6x6,
|
||||
|
||||
@@ -27,32 +27,5 @@ namespace AssetStudio
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace AssetStudio
|
||||
public long m_Offset; //ulong
|
||||
public long m_Size; //ulong
|
||||
|
||||
public StreamedResource(BinaryReader reader)
|
||||
public StreamedResource(EndianBinaryReader reader)
|
||||
{
|
||||
m_Source = reader.ReadAlignedString();
|
||||
m_Offset = reader.ReadInt64();
|
||||
|
||||
59
AssetStudio/Crypto/BlkUtils.cs
Normal file
59
AssetStudio/Crypto/BlkUtils.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
164
AssetStudio/Crypto/CNUnity.cs
Normal file
164
AssetStudio/Crypto/CNUnity.cs
Normal 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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
59
AssetStudio/Crypto/CryptoHelper.cs
Normal file
59
AssetStudio/Crypto/CryptoHelper.cs
Normal file
File diff suppressed because one or more lines are too long
@@ -1,32 +1,28 @@
|
||||
namespace AssetStudio
|
||||
{
|
||||
public class MT19937_64
|
||||
public static class MT19937_64
|
||||
{
|
||||
private const ulong N = 312;
|
||||
private const ulong M = 156;
|
||||
private const ulong MATRIX_A = 0xB5026F5AA96619E9L;
|
||||
private const ulong UPPER_MASK = 0xFFFFFFFF80000000;
|
||||
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;
|
||||
|
||||
public MT19937_64(ulong seed)
|
||||
{
|
||||
this.Seed(seed);
|
||||
}
|
||||
|
||||
public void Seed(ulong seed)
|
||||
public static void Init(ulong seed)
|
||||
{
|
||||
mt[0] = seed;
|
||||
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 };
|
||||
|
||||
if (mti >= N)
|
||||
@@ -34,7 +30,7 @@
|
||||
ulong kk;
|
||||
if (mti == N + 1)
|
||||
{
|
||||
Seed(5489UL);
|
||||
Init(5489UL);
|
||||
}
|
||||
for (kk = 0; kk < (N - M); kk++)
|
||||
{
|
||||
@@ -60,9 +56,14 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
72
AssetStudio/Crypto/Mr0kUtils.cs
Normal file
72
AssetStudio/Crypto/Mr0kUtils.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -3,40 +3,42 @@ using System.Buffers.Binary;
|
||||
|
||||
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 uint MT19937 = 0x6C078965;
|
||||
|
||||
public void InitSeed(int seed)
|
||||
public static void InitSeed(uint seed)
|
||||
{
|
||||
initseed = (uint)seed;
|
||||
x = (uint)seed;
|
||||
initseed = seed;
|
||||
x = seed;
|
||||
y = MT19937 * x + 1;
|
||||
z = MT19937 * y + 1;
|
||||
w = MT19937 * z + 1;
|
||||
Init = true;
|
||||
}
|
||||
|
||||
public uint XORShift()
|
||||
public static uint XORShift()
|
||||
{
|
||||
uint t = x ^ (x << 11);
|
||||
x = y; y = z; z = w;
|
||||
return w = w ^ (w >> 19) ^ t ^ (t >> 8);
|
||||
}
|
||||
|
||||
public uint NextUInt32()
|
||||
public static uint NextUInt32()
|
||||
{
|
||||
return XORShift();
|
||||
}
|
||||
|
||||
public int NextDecryptInt() => BinaryPrimitives.ReadInt32LittleEndian(NextDecrypt(4));
|
||||
public uint NextDecryptUInt() => BinaryPrimitives.ReadUInt32LittleEndian(NextDecrypt(4));
|
||||
public static int NextDecryptInt() => BinaryPrimitives.ReadInt32LittleEndian(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 key = size * initseed - SEED;
|
||||
27
AssetStudio/CryptoStream.cs
Normal file
27
AssetStudio/CryptoStream.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -10,11 +12,10 @@ namespace AssetStudio
|
||||
|
||||
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;
|
||||
buffer = new byte[8];
|
||||
Game = game;
|
||||
}
|
||||
|
||||
public long Position
|
||||
@@ -23,8 +24,8 @@ namespace AssetStudio
|
||||
set => BaseStream.Position = value;
|
||||
}
|
||||
|
||||
public long[] BundlePos = Array.Empty<long>();
|
||||
public Game Game;
|
||||
public long Length => BaseStream.Length;
|
||||
public long Remaining => Length - Position;
|
||||
|
||||
public override short ReadInt16()
|
||||
{
|
||||
@@ -108,9 +109,179 @@ namespace AssetStudio
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
AssetStudio/ExportTypeList.cs
Normal file
18
AssetStudio/ExportTypeList.cs
Normal 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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
AssetStudio/Extensions/ByteArrayExtensions.cs
Normal file
38
AssetStudio/Extensions/ByteArrayExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using static AssetStudio.ImportHelper;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -8,40 +9,32 @@ namespace AssetStudio
|
||||
public string FullPath;
|
||||
public string FileName;
|
||||
public FileType FileType;
|
||||
public long Length;
|
||||
|
||||
private static readonly byte[] gzipMagic = { 0x1f, 0x8b };
|
||||
private static readonly byte[] brotliMagic = { 0x62, 0x72, 0x6F, 0x74, 0x6C, 0x69 };
|
||||
private static readonly byte[] zipMagic = { 0x50, 0x4B, 0x03, 0x04 };
|
||||
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);
|
||||
FileName = Path.GetFileName(path);
|
||||
FileType = CheckFileType();
|
||||
Length = stream.Length;
|
||||
|
||||
}
|
||||
|
||||
private FileType CheckFileType()
|
||||
{
|
||||
if (IsSerializedFile())
|
||||
{
|
||||
return FileType.AssetsFile;
|
||||
}
|
||||
else if (Game != null)
|
||||
{
|
||||
return FileType.GameFile;
|
||||
}
|
||||
|
||||
var signature = this.ReadStringToNull(20);
|
||||
Position = 0;
|
||||
switch (signature)
|
||||
{
|
||||
case "ENCR":
|
||||
case "UnityWeb":
|
||||
case "UnityRaw":
|
||||
case "UnityArchive":
|
||||
@@ -49,6 +42,8 @@ namespace AssetStudio
|
||||
return FileType.BundleFile;
|
||||
case "UnityWebData1.0":
|
||||
return FileType.WebFile;
|
||||
case "blk":
|
||||
return FileType.BlkFile;
|
||||
default:
|
||||
{
|
||||
byte[] magic = ReadBytes(2);
|
||||
@@ -71,7 +66,26 @@ namespace AssetStudio
|
||||
magic = ReadBytes(4);
|
||||
Position = 0;
|
||||
if (zipMagic.SequenceEqual(magic) || zipSpannedMagic.SequenceEqual(magic))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -113,4 +127,35 @@ namespace AssetStudio
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,13 @@ namespace AssetStudio
|
||||
{
|
||||
AssetsFile,
|
||||
BundleFile,
|
||||
GameFile,
|
||||
WebFile,
|
||||
ResourceFile,
|
||||
GZipFile,
|
||||
BrotliFile,
|
||||
ZipFile
|
||||
ZipFile,
|
||||
BlkFile,
|
||||
Mhy0File,
|
||||
BlockFile
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using static AssetStudio.Crypto;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -11,15 +11,22 @@ namespace AssetStudio
|
||||
static GameManager()
|
||||
{
|
||||
int index = 0;
|
||||
Games.Add(index++, new("GI", ".blk", "GI_Data|YS_Data"));
|
||||
Games.Add(index++, new("GI_CB1", ".asb", "GS_Data"));
|
||||
Games.Add(index++, new("GI_CB2", ".blk", "G_Data"));
|
||||
Games.Add(index++, new("GI_CB3", ".blk", "YS_Data"));
|
||||
Games.Add(index++, new("BH3", ".wmv", "BH3_Data"));
|
||||
Games.Add(index++, new("ZZZ_CB1", ".bundle", "Win_Data/StreamingAssets/Bundles"));
|
||||
Games.Add(index++, new("SR_CB2", ".unity3d", "SR_Data"));
|
||||
Games.Add(index++, new("SR_CB3", ".block", "SR_Data"));
|
||||
Games.Add(index++, new("TOT", ".blk", "AssetbundlesCache"));
|
||||
Games.Add(index++, new(GameType.Normal));
|
||||
Games.Add(index++, new Game(GameType.CNUnity));
|
||||
Games.Add(index++, new Mhy0(GameType.GI, GIMhy0ShiftRow, GIMhy0Key, GIMhy0Mul, GIExpansionKey, GISBox, GIInitVector, GIInitSeed));
|
||||
Games.Add(index++, new Mr0k(GameType.GI_Pack, PackExpansionKey, blockKey: PackBlockKey));
|
||||
Games.Add(index++, new Mr0k(GameType.GI_CB1));
|
||||
Games.Add(index++, new Blk(GameType.GI_CB2, GI_CBXExpansionKey, initVector: GI_CBXInitVector, initSeed: GI_CBXInitSeed));
|
||||
Games.Add(index++, new Blk(GameType.GI_CB3, GI_CBXExpansionKey, initVector: GI_CBXInitVector, initSeed: GI_CBXInitSeed));
|
||||
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 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)
|
||||
{
|
||||
@@ -31,16 +38,7 @@ namespace AssetStudio
|
||||
return format;
|
||||
}
|
||||
|
||||
public static Game GetGame(string name)
|
||||
{
|
||||
foreach(var game in Games)
|
||||
{
|
||||
if (game.Value.Name == name)
|
||||
return game.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static Game GetGame(string name) => Games.FirstOrDefault(x => x.Value.Name == name).Value;
|
||||
public static Game[] GetGames() => Games.Values.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))}";
|
||||
@@ -48,15 +46,131 @@ namespace AssetStudio
|
||||
|
||||
public record Game
|
||||
{
|
||||
public string Name;
|
||||
public string Extension;
|
||||
public string Path;
|
||||
public Game(string name, string extension, string path)
|
||||
public string Name { get; set; }
|
||||
public GameType Type { get; }
|
||||
|
||||
public Game(GameType type)
|
||||
{
|
||||
Name = name;
|
||||
Extension = extension;
|
||||
Path = path;
|
||||
Name = type.ToString();
|
||||
Type = type;
|
||||
}
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,23 +73,6 @@ namespace AssetStudio
|
||||
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)
|
||||
{
|
||||
var subs = path.Split(new[] { '/' }, 2);
|
||||
@@ -112,6 +95,23 @@ namespace AssetStudio
|
||||
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)
|
||||
{
|
||||
foreach (var child in children)
|
||||
|
||||
@@ -16,18 +16,21 @@ namespace AssetStudio
|
||||
|
||||
public interface ILogger
|
||||
{
|
||||
void Log(LoggerEvent loggerEvent, string message);
|
||||
void Log(LoggerEvent loggerEvent, string message, bool silent = false);
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace AssetStudio
|
||||
{
|
||||
public interface IYAMLExportable
|
||||
{
|
||||
YAMLNode ExportYAML();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,62 @@
|
||||
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.Compression;
|
||||
using System.Linq;
|
||||
using static AssetStudio.BundleFile;
|
||||
using static AssetStudio.Crypto;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
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)
|
||||
{
|
||||
using (reader)
|
||||
@@ -33,5 +84,210 @@ namespace AssetStudio
|
||||
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
38
AssetStudio/Keys.json
Normal 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"
|
||||
}
|
||||
]
|
||||
@@ -8,19 +8,20 @@ namespace AssetStudio
|
||||
public static class Logger
|
||||
{
|
||||
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 Debug(string message) => Default.Log(LoggerEvent.Debug, message);
|
||||
public static void Info(string message) => Default.Log(LoggerEvent.Info, message);
|
||||
public static void Warning(string message) => Default.Log(LoggerEvent.Warning, message);
|
||||
public static void Error(string message) => Default.Log(LoggerEvent.Error, message);
|
||||
public static void Verbose(string message) => Default.Log(LoggerEvent.Verbose, message, Silent);
|
||||
public static void Debug(string message) => Default.Log(LoggerEvent.Debug, message, Silent);
|
||||
public static void Info(string message) => Default.Log(LoggerEvent.Info, message, Silent);
|
||||
public static void Warning(string message) => Default.Log(LoggerEvent.Warning, message, Silent);
|
||||
public static void Error(string message) => Default.Log(LoggerEvent.Error, message, Silent);
|
||||
|
||||
public static void Error(string message, Exception e)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(message);
|
||||
sb.AppendLine(e.ToString());
|
||||
Default.Log(LoggerEvent.Error, sb.ToString());
|
||||
Default.Log(LoggerEvent.Error, sb.ToString(), Silent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
|
||||
namespace AssetStudio
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct Quaternion : IEquatable<Quaternion>, IYAMLExportable
|
||||
public struct Quaternion : IEquatable<Quaternion>
|
||||
{
|
||||
public float X;
|
||||
public float Y;
|
||||
@@ -83,17 +83,6 @@ namespace AssetStudio
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
|
||||
namespace AssetStudio
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct Vector3 : IEquatable<Vector3>, IYAMLExportable
|
||||
public struct Vector3 : IEquatable<Vector3>
|
||||
{
|
||||
public float X;
|
||||
public float Y;
|
||||
@@ -87,16 +87,6 @@ namespace AssetStudio
|
||||
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 One => new Vector3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
197
AssetStudio/Mhy0File.cs
Normal file
197
AssetStudio/Mhy0File.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -17,35 +15,19 @@ namespace AssetStudio
|
||||
public long m_PathID;
|
||||
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 isExportableType = ExportableTypes.Contains(typeID);
|
||||
switch (typeID)
|
||||
return typeID switch
|
||||
{
|
||||
case ClassIDType.IndexObject:
|
||||
case ClassIDType.MiHoYoBinData:
|
||||
return isExportableType && IndexObject.Exportable;
|
||||
default:
|
||||
return isExportableType;
|
||||
}
|
||||
}
|
||||
|
||||
public static ClassIDType[] ExportableTypes = new ClassIDType[]
|
||||
{
|
||||
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
|
||||
ClassIDType.IndexObject or ClassIDType.MiHoYoBinData => isExportableType && MiHoYoBinData.Exportable,
|
||||
_ => isExportableType,
|
||||
};
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace AssetStudio
|
||||
public class ObjectReader : EndianBinaryReader
|
||||
{
|
||||
public SerializedFile assetsFile;
|
||||
public Game Game;
|
||||
public long m_PathID;
|
||||
public long byteStart;
|
||||
public uint byteSize;
|
||||
@@ -20,10 +21,10 @@ namespace AssetStudio
|
||||
public int[] version => assetsFile.version;
|
||||
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;
|
||||
Game = reader.Game;
|
||||
Game = game;
|
||||
m_PathID = objectInfo.m_PathID;
|
||||
byteStart = objectInfo.byteStart;
|
||||
byteSize = objectInfo.byteSize;
|
||||
|
||||
@@ -3,20 +3,27 @@ namespace AssetStudio
|
||||
{
|
||||
public static class Progress
|
||||
{
|
||||
public static bool Silent = false;
|
||||
public static IProgress<int> Default = new Progress<int>();
|
||||
private static int preValue;
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
if (!Silent)
|
||||
{
|
||||
preValue = 0;
|
||||
Default.Report(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Report(int current, int total)
|
||||
{
|
||||
if (!Silent)
|
||||
{
|
||||
var value = (int)(current * 100f / total);
|
||||
Report(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Report(int value)
|
||||
{
|
||||
|
||||
@@ -82,10 +82,8 @@ namespace AssetStudio
|
||||
foreach (var subAsset in asset.Value)
|
||||
{
|
||||
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);
|
||||
AssetMap[key] = ((ulong)subAsset.PathHashLast) << 8 | subAsset.PathHashPre;
|
||||
AssetMap[subAsset.PathHashLast] = ((ulong)subAsset.PathHashLast) << 8 | subAsset.PathHashPre;
|
||||
}
|
||||
}
|
||||
foreach (var asset in assetIndex.Assets)
|
||||
@@ -112,7 +110,7 @@ namespace AssetStudio
|
||||
return bundleInfo;
|
||||
return null;
|
||||
}
|
||||
public static string GetBundlePath(uint last)
|
||||
public static string GetAssetPath(uint last)
|
||||
{
|
||||
foreach (var location in AssetLocationMap)
|
||||
if (location.TryGetValue(last, out var bundleInfo))
|
||||
@@ -150,16 +148,6 @@ namespace AssetStudio
|
||||
var asset = new Asset() { Hash = hash };
|
||||
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
|
||||
{
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace AssetStudio
|
||||
{
|
||||
public AssetsManager assetsManager;
|
||||
public FileReader reader;
|
||||
public Game game;
|
||||
public long offset = 0;
|
||||
public string fullName;
|
||||
public string originalPath;
|
||||
public string fileName;
|
||||
@@ -32,11 +34,11 @@ namespace AssetStudio
|
||||
public List<SerializedType> m_RefTypes;
|
||||
public string userInformation;
|
||||
|
||||
public SerializedFile(FileReader reader, AssetsManager assetsManager, string path = null)
|
||||
public SerializedFile(FileReader reader, AssetsManager assetsManager)
|
||||
{
|
||||
this.assetsManager = assetsManager;
|
||||
this.reader = reader;
|
||||
originalPath = path;
|
||||
game = assetsManager.Game;
|
||||
fullName = reader.FullPath;
|
||||
fileName = reader.FileName;
|
||||
|
||||
@@ -80,11 +82,14 @@ namespace AssetStudio
|
||||
if (header.m_Version >= SerializedFileFormatVersion.Unknown_8)
|
||||
{
|
||||
m_TargetPlatform = (BuildTarget)reader.ReadInt32();
|
||||
if (m_TargetPlatform == BuildTarget.NoTarget) m_TargetPlatform = BuildTarget.StandaloneWindows64;
|
||||
if (!Enum.IsDefined(typeof(BuildTarget), m_TargetPlatform))
|
||||
{
|
||||
m_TargetPlatform = BuildTarget.UnknownPlatform;
|
||||
}
|
||||
else if (game.Type.IsMhyGroup())
|
||||
{
|
||||
m_TargetPlatform = BuildTarget.StandaloneWindows64;
|
||||
}
|
||||
}
|
||||
if (header.m_Version >= SerializedFileFormatVersion.HasTypeTreeHashes)
|
||||
{
|
||||
@@ -197,7 +202,7 @@ namespace AssetStudio
|
||||
m_External.guid = new Guid(reader.ReadBytes(16));
|
||||
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_Externals.Add(m_External);
|
||||
}
|
||||
@@ -237,7 +242,8 @@ namespace AssetStudio
|
||||
var type = new SerializedType();
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -348,7 +354,7 @@ namespace AssetStudio
|
||||
}
|
||||
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++)
|
||||
{
|
||||
@@ -358,7 +364,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
string ReadString(BinaryReader stringBufferReader, uint value)
|
||||
string ReadString(EndianBinaryReader stringBufferReader, uint value)
|
||||
{
|
||||
var isOffset = (value & 0x80000000) == 0;
|
||||
if (isOffset)
|
||||
@@ -381,12 +387,14 @@ namespace AssetStudio
|
||||
ObjectsDic.Add(obj.m_PathID, obj);
|
||||
}
|
||||
|
||||
private int DecodeClassID(int value)
|
||||
private static int DecodeClassID(int value)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
value = BinaryPrimitives.ReadInt32BigEndian(bytes);
|
||||
Array.Reverse(bytes);
|
||||
value = BitConverter.ToInt32(bytes, 0);
|
||||
return (value ^ 0x23746FBE) - 3;
|
||||
}
|
||||
|
||||
public bool IsVersionStripped => unityVersion == strippedVersion;
|
||||
|
||||
private const string strippedVersion = "0.0.0";
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace AssetStudio
|
||||
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 level = m_Node.m_Level;
|
||||
@@ -182,7 +182,7 @@ namespace AssetStudio
|
||||
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 varTypeStr = m_Node.m_Type;
|
||||
|
||||
68
AssetStudio/UnityCNKeyManager.cs
Normal file
68
AssetStudio/UnityCNKeyManager.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace AssetStudio
|
||||
{
|
||||
internal enum MetaType
|
||||
{
|
||||
YAML,
|
||||
TAG,
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace AssetStudio
|
||||
{
|
||||
internal enum ScalarType
|
||||
{
|
||||
Boolean,
|
||||
Byte,
|
||||
UInt16,
|
||||
Int16,
|
||||
UInt32,
|
||||
Int32,
|
||||
UInt64,
|
||||
Int64,
|
||||
Single,
|
||||
Double,
|
||||
String,
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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>>();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user