120 Commits

Author SHA1 Message Date
VaDiM
6de33d0437 Update to v0.19 & update dependencies 2025-09-04 20:07:22 +03:00
VaDiM
7ce8b8c8ae [CLI] Some minor fixes 2025-09-04 17:54:55 +03:00
VaDiM
2d449ff4cd Fix support for meshes with omitted weight values. Close #21 2025-09-03 13:12:59 +03:00
VaDiM
1d65096001 [CLI] Add more info to the "Info" mode 2025-09-01 23:38:05 +03:00
VaDiM
355c99c034 [CLI] Fix deleting of temp files 2025-09-01 22:19:48 +03:00
VaDiM
963cd6546b Improve mesh loading 2025-09-01 16:32:46 +03:00
VaDiM
ae3b5169df Some other fixes 2025-08-27 05:23:50 +03:00
VaDiM
521e2f3bbc Improve Tuanjie support 2025-08-27 05:22:46 +03:00
VaDiM
f0a69025fe Update asset export logic
- Disabled saving files with a unique id if they already exist. A unique id will only be added to files with identical names during export.
- Added an option to overwrite exisisting files.
- Fixed support of multiple model export using "Export selected objects (split)" option. (#43)
2025-08-19 20:51:38 +03:00
VaDiM
52b0a21181 Some fixes for bundle reader 2025-08-11 01:27:02 +03:00
VaDiM
be11fdf14f [GUI] Some fixes for animation export
- Disabled animation converting if animation export is disabled in the options
- Fixed a bug with ignoring animation selection order when exporting models with selected animationClips via the “Model” tab
2025-08-11 01:25:42 +03:00
VaDiM
054906a426 More spans 2025-08-11 01:25:42 +03:00
VaDiM
35324083e1 [CLI] Add flag to always decompress bundles to disk 2025-08-03 14:26:49 +03:00
VaDiM
36bd3c8342 [GUI] Rework some import options
- Added feature to load and export import options to a file (import options include unity version).
- Added option to always decompress bundles to disk. (Related issue: #58 )
2025-08-03 00:08:00 +03:00
VaDiM
c20c07b5f2 Update bundle reader
- Replaced creation of a duplicated file/memory stream with OffsetStream.
- Added separate processing of uncompressed bundles (including streamed bundles). They will be read directly from disk.
- Added progress report on LZMA decompression process.
2025-07-24 19:07:24 +03:00
VaDiM
efca2a7557 Add support for bundles with obfuscated unity version 2025-07-24 15:59:38 +03:00
VaDiM
6b41a36c7d Fix parsing of MovieTexture and MonoScript assets 2025-07-24 15:59:37 +03:00
VaDiM
35b24990c6 Allocation-safe asset reading
Try to not pre-allocate memory during asset reading
2025-07-24 04:06:33 +03:00
VaDiM
925f5c12a3 Update FileReader & EndianSpanReader 2025-07-24 04:04:03 +03:00
VaDiM
9c64f7f56d Fix parsing of Tuanjie Renderer assets 2025-07-24 04:04:03 +03:00
VaDiM
3c6b65f724 Update BuildTarget.cs 2025-07-22 03:00:56 +03:00
VaDiM
34819608c5 Fixes for zip file reading
- Fixed path for split files
- Fixed assetsFile loading from an archive
2025-07-22 03:00:56 +03:00
VaDiM
6f8f1a5a8a Use System.Half 2025-07-13 20:40:34 +03:00
VaDiM
f0029520fb Fix for TypeTreeHelper
ref: b71a7620a3
2025-07-13 01:56:09 +03:00
VaDiM
7b7eac62d8 Fixes for Live2D export
- Fixed export of live2d models from assets without containers.
- Improved method of binding model-related assets.
2025-06-27 00:41:58 +03:00
VaDiM
3a25ed9ccd [CLI] Update "current options" structure 2025-06-12 01:41:33 +03:00
VaDiM
785eeb8665 [CLI] Add name filtering support for Live2D export mode 2025-06-12 01:41:33 +03:00
VaDiM
fd50054edf [CLI] Add regex support for splitObject export mode 2025-06-12 01:41:33 +03:00
VaDiM
a1ee61c542 Some minor fixes 2025-06-10 02:06:24 +03:00
VaDiM
12799da395 [CLI] Add Animator export 2025-06-07 23:45:49 +03:00
VaDiM
60426a4b9a Improve support of Tuanjie assets 2025-06-07 23:40:49 +03:00
VaDiM
3ea01ec9bc Display animation count when exporting fbx models 2025-05-29 05:11:21 +03:00
VaDiM
7da68aedff [CLI] Add support for multiple file loading 2025-05-29 05:11:06 +03:00
VaDiM
548f8a52cf Minor fix for multibundle reader 2025-05-25 01:17:54 +03:00
VaDiM
24337f66f9 Fix parsing of Material assets from some 2021.2 versions 2025-05-21 16:08:56 +03:00
VaDiM
92a89db4e8 Fix parsing of legacy Tuanjie AnimationClip assets 2025-05-18 02:18:58 +03:00
VaDiM
c11e085e2e Fix build 2025-05-17 14:30:19 +03:00
VaDiM
55406553f6 Some minor fixes 2025-05-17 04:58:43 +03:00
VaDiM
b0a051fc47 [GUI] Add exact search option for Scene Hierarchy. Close #49 2025-05-11 09:29:11 +03:00
VaDiM
97fa42742b [CLI] Add flag to allow filtering assets using regex. Close #45
- Added --filter-with-regex flag
2025-05-11 09:29:11 +03:00
VaDiM
97bdef0891 Update UnityCN detection method 2025-05-09 03:41:54 +03:00
VaDiM
876bafdda1 Add oodle compression support (unofficial) 2025-05-09 03:41:53 +03:00
VaDiM
66229e564a Fix parsing of AnimationClip assets via typetree for Unity versions < 5 2025-05-09 02:23:42 +03:00
VaDiM
13f37ec260 Fix audioClip converting 2025-04-17 01:01:58 +03:00
VaDiM
a0c2a7bdfe Improve integration with Live2D assets
- Added container-independent method for searching AnimationClip assets
(However, it's not really universal, so it can't completely replace the container-dependent method)
2025-04-10 01:30:45 +03:00
VaDiM
0cc74b8c12 Fix Tuanjie detection 2025-04-08 02:46:11 +03:00
VaDiM
f077064a6a Fix for UV bindings 2025-04-06 00:06:09 +03:00
VaDiM
190cb68b07 [CLI] Add flag to export all UVs as diffuse maps 2025-03-31 20:14:33 +03:00
VaDiM
3fa2ef1694 Some minor fixes 2025-03-31 20:10:02 +03:00
VaDiM
40e0bd0248 [CLI] Fix export of some fbx models 2025-03-31 19:45:47 +03:00
VaDiM
e1e43439c3 Add option to manually bind UV map types 2025-03-29 23:57:59 +03:00
VaDiM
e8ca265a43 Fix multiBundle file extraction 2025-03-25 21:54:34 +03:00
VaDiM
81ed77819a [CLI] Improve UnityVersion option info 2025-03-22 19:01:07 +03:00
VaDiM
d1fed47f92 [CLI] Change color of option example string 2025-03-22 19:01:07 +03:00
VaDiM
3b10a808d3 [GUI] Add option to autoplay audioClip assets 2025-03-22 19:01:06 +03:00
VaDiM
47d67e0a49 Update FMOD to v2.03.06
- Fixed a bug that caused audioСlip preview volume to reset when selecting an asset
- Added some native libs for linux-arm64, win-arm64
2025-03-22 19:01:06 +03:00
VaDiM
bc0e32efec Add multiBundle support
including fake headers
2025-03-10 18:08:40 +03:00
VaDiM
db4eb30a27 Fix PPtrConverter (#69) 2025-03-04 04:05:22 +03:00
VaDiM
9f918d0332 Fix export of selected Live2D motions 2025-03-03 22:18:29 +03:00
VaDiM
81cd6d79d0 Add AnimationClip equality comparer 2025-03-03 22:18:29 +03:00
VaDiM
9024e6a235 Improve external data extraction for ExportRaw mode 2025-02-24 19:03:22 +03:00
VaDiM
0b7b809285 [GUI] Some visual fixes for audio player 2025-02-20 17:33:59 +03:00
VaDiM
f7e6d23084 Add support for obfuscated unity versions 2025-02-17 18:51:05 +03:00
VaDiM
6ea1ff3e96 Fix for m_MuscleClipSize 2025-02-10 17:01:21 +03:00
VaDiM
185348d9b8 Replace Math with MathF 2025-01-31 17:55:47 +03:00
VaDiM
a8bb6c714b A small refactoring for Live2D motion export 2025-01-23 18:18:47 +03:00
VaDiM
cc21d4fa4d [CLI] Add support of Extract mode 2025-01-23 17:57:42 +03:00
VaDiM
e3e343320c [GUI] AudioClip improvements
- Increased loading speed of AudioClip preview
- Optimized memory consumption of AudioClip preview
- Fixed incorrect length detection for some sound types
- Added channel count info (audio channels)
2025-01-20 13:07:25 +03:00
VaDiM
1cdb0b762a Some fixes and improvements for Live2D export
- Fixed l2d model export for bundles with multiple models inside
- Added support of grouping exported models by model name
2025-01-15 03:38:33 +03:00
VaDiM
02f64f3c97 [GUI] Workaround for progress bar in Dark mode 2025-01-15 00:41:01 +03:00
VaDiM
58917ab7dc [GUI] Add tree view for dump 2025-01-15 00:41:01 +03:00
VaDiM
b7d21e5bd8 Add support for Tuanjie AnimationClip assets 2025-01-12 04:08:20 +03:00
VaDiM
d7b4d415ca Add support for Tuanjie Mesh assets 2025-01-12 04:08:20 +03:00
VaDiM
95f7d70419 Fix support of Tuanjie GameObject assets 2025-01-12 04:08:20 +03:00
VaDiM
4e93ea5a82 Improve ExportRaw option
- External data will also be added to exported assets
2025-01-12 04:08:19 +03:00
VaDiM
6608e76471 Improve error messages for AudioClip assets 2025-01-12 04:08:19 +03:00
VaDiM
ff92d1784d [GUI] Some fixes for Dark mode 2025-01-12 04:08:19 +03:00
VaDiM
064f5cbe57 Improve integration with Live2D assets 2025-01-12 04:08:19 +03:00
VaDiM
59db27de3a [GUI] Fix a bug with OpenFolderAfterExport option
- Fixed a bug with the OpenFolderAfterExport option when it launched a file instead of opening a folder in some cases
2025-01-12 04:08:19 +03:00
VaDiM
0d4e7ba4ae [GUI] Add Dark mode support for .NET 9 (wip). Close #9
Dark mode support for winforms was added with .net9, but the feature is still not finished. It should become better in future versions.
2025-01-12 04:08:19 +03:00
VaDiM
3605bc0ff9 Update project files: add .NET 9 & update dependencies 2025-01-12 04:08:19 +03:00
VaDiM
188ee088a2 Improve support of Texture2DArray assets 2025-01-12 04:08:19 +03:00
VaDiM
341612be16 Add Tuanjie WebFile support 2025-01-12 04:08:19 +03:00
VaDiM
e16046d775 Add support for Tuanjie Texture2D assets 2025-01-12 04:08:19 +03:00
VaDiM
d2f69432e4 Add Tuanjie build targets 2025-01-12 04:08:18 +03:00
VaDiM
08d50a8013 Update BuildTarget.cs 2025-01-12 04:08:18 +03:00
VaDiM
7b1585eff0 A small refactoring of l2d extractor 2025-01-12 04:08:18 +03:00
VaDiM
70aa8bec59 Add support for exporting l2d poses (pose3.json) 2025-01-12 04:08:18 +03:00
VaDiM
18813b22c3 Add names of pptr objects to object dumps 2024-11-02 04:51:21 +03:00
VaDiM
316837dfdf Fix parsing via typetree of old Material assets
- Fixed parsing via typetree of Material assets from Unity version <5.6
2024-11-02 02:12:49 +03:00
VaDiM
fca937e5e6 Some fixes for asset parsing via typetree 2024-11-02 01:40:45 +03:00
VaDiM
c2095c4e7a Fix audio preview for some fmod formats. Close #53 2024-10-30 17:11:19 +03:00
VaDiM
f253c868d4 Fix calculations in Live2D motions
- Fixed calculation of totalPointCount and totalSegmentCount in Live2D motions.
https://github.com/Live2D/CubismNativeFramework/pull/57
2024-10-30 01:26:37 +03:00
VaDiM
8ccdd0fd4e Minor fix for PPtrConverter 2024-10-22 01:58:15 +03:00
VaDiM
bc92dfb77c Add Material loading via typetree 2024-10-20 22:29:06 +03:00
VaDiM
c37e2e65b7 Fixes and improvements for asset parsing via typetree 2024-10-18 20:55:34 +03:00
VaDiM
58ee2b8f1e Improve support for some older Unity versions (<3.0)
- Fix export of AudioClip assets from unity v2.5 and earlier
- Fix parsing of PlayerSettings assets from unity v2.6 and earlier
- Fix parsing of AnimationClip assets from unity v2.5 and earlier
2024-10-05 02:17:16 +03:00
VaDiM
c93d27d9a4 Fixes for Unity version parser 2024-10-03 16:23:51 +03:00
VaDiM
5a84a67955 Move BuildType to UnityVersion class 2024-09-25 23:40:41 +03:00
VaDiM
fa332b45df Improve support of Unity 2023+ 2024-09-25 23:38:19 +03:00
VaDiM
348aea2be8 Update CommonString.cs
Unity 6
2024-09-25 23:38:18 +03:00
VaDiM
d52f192e75 Fix support of Sprites with wrong SpriteAtlas pathID 2024-09-13 00:39:04 +03:00
VaDiM
81a1eeb2d1 Merge branch 'pr/40' into AssetStudioMod 2024-09-04 01:15:22 +03:00
VaDiM
05ea91ef5e Merge branch 'pr/44' into AssetStudioMod 2024-08-08 21:49:27 +03:00
VaDiM
e0d5e5c6b7 Display "type" as a string in object dumps 2024-08-08 18:58:42 +03:00
VaDiM
b439cfed66 Update ClassIDType.cs 2024-08-08 18:42:28 +03:00
VaDiM
573b87b570 Replace "type" with "classID" in object dumps 2024-08-08 18:16:41 +03:00
Loyie King
585b69fb36 fix bundle blocks read failed with ArchiveFlags.BlocksInfoAtTheEnd
Bundle files may be aligned and padding with zeros. Previously `ArchiveFlags.BlocksInfoAtTheEnd` will read the end padding zeros rather than the real block infos, causing decompression errors in following logics.
2024-07-10 19:23:36 +08:00
hungrydoodles
5e3fe1775f Added so when the exported animator contains MeshRenderers without meshes attached it tries to find the missing meshes by the Renderer's original name. (Fixes NagantSR0101 from Girls Frontline 2 Animator export not exporting meshes at all in any existing variant.) Also set ReShade in 3D view to be disabled by default as it is misleading otherwise 2024-05-15 00:05:50 +05:00
VaDiM
8704feb079 Merge branch 'pr/37' into AssetStudioMod 2024-05-10 03:45:06 +03:00
VaDiM
f54fe3492b [CLI] Fix Mesh export for some region formats. Close #38 2024-05-10 03:22:39 +03:00
BM85-Hz
6d953d774d simplify loop label 2024-05-08 22:30:50 -07:00
BM85-Hz
f0a793bd3d added AudioClip loop point display to GUI 2024-05-05 18:34:28 -07:00
VaDiM
d886bf1c5d Update dependencies 2024-04-25 01:23:34 +03:00
VaDiM
1623981c0e [GUI] Add ability to filter live2d model assets 2024-04-24 22:19:51 +03:00
VaDiM
2c860f004b Use system.text.json to parse typetree
well, it's still slower than some more suitable libs for such task, but stable and with minimal code changes (and has net472 support, yeah)
2024-04-14 02:17:06 +03:00
VaDiM
a3f4c7a029 [CLI] Update readme 2024-04-12 21:48:57 +03:00
VaDiM
f86b5a97ac Fix resourceFileReader for webFile 2024-04-12 21:48:53 +03:00
VaDiM
7e408a3667 [GUI] Fix displayed info for non-fmod audio clips from Unity <5 2024-04-12 21:48:53 +03:00
VaDiM
6c515aee2e Fix support for sprite assets from Unity <5.2 2024-04-10 10:34:46 +03:00
194 changed files with 10040 additions and 5159 deletions

View File

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

View File

@@ -11,7 +11,7 @@ namespace AssetStudio.PInvoke
{
public static void PreloadDll(string dllName)
{
var localPath = Process.GetCurrentProcess().MainModule.FileName;
var localPath = Process.GetCurrentProcess().MainModule?.FileName;
var localDir = Path.GetDirectoryName(localPath);
// Not using OperatingSystem.Platform.
@@ -35,7 +35,6 @@ namespace AssetStudio.PInvoke
private static class Win32
{
internal static void LoadDll(string dllDir, string dllName)
{
var dllFileName = $"{dllName}.dll";

View File

@@ -1,26 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
<Version>0.18.0.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2023-2024</Copyright>
<TargetFrameworks>net472;net8.0;net8.0-windows;net9.0;net9.0-windows</TargetFrameworks>
<Version>0.19.0.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2021-2025</Copyright>
<DebugType>embedded</DebugType>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" />
<PackageReference Include="ZstdSharp.Port" Version="0.7.6" />
<ItemGroup>
<PackageReference Include="ZstdSharp.Port" Version="0.8.6" />
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.IO.Compression" Version="4.0.0" />
<PackageReference Include="K4os.Compression.LZ4" Version="1.1.11" />
<PackageReference Include="ZstdSharp.Port" Version="0.6.5" />
</ItemGroup>
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.Text.Json" Version="9.0.8" />
<PackageReference Include="Microsoft.Bcl.Numerics" Version="9.0.8" />
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<ProjectReference Include="..\AssetStudio.PInvoke\AssetStudio.PInvoke.csproj" />
</ItemGroup>
</Project>

View File

@@ -5,66 +5,46 @@ using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Text.Json;
using AssetStudio.CustomOptions;
using AssetStudio.CustomOptions.Asmo;
using static AssetStudio.ImportHelper;
namespace AssetStudio
{
public class AssetsManager
{
public bool ZstdEnabled = true;
public bool LoadingViaTypeTreeEnabled = true;
public List<SerializedFile> assetsFileList = new List<SerializedFile>();
public bool LoadViaTypeTree = true;
public bool MeshLazyLoad = true;
public ImportOptions Options = new ImportOptions();
public readonly List<Action<OptionsFile>> OptionLoaders = new List<Action<OptionsFile>>();
public readonly List<SerializedFile> AssetsFileList = new List<SerializedFile>();
internal Dictionary<string, int> assetsFileIndexCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
internal ConcurrentDictionary<string, BinaryReader> resourceFileReaders = new ConcurrentDictionary<string, BinaryReader>(StringComparer.OrdinalIgnoreCase);
private UnityVersion specifiedUnityVersion;
private List<string> importFiles = new List<string>();
private HashSet<ClassIDType> filteredAssetTypesList = new HashSet<ClassIDType>();
private HashSet<string> importFilesHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private HashSet<string> noexistFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private HashSet<string> assetsFileListHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly List<string> importFiles = new List<string>();
private readonly HashSet<ClassIDType> filteredAssetTypesList = new HashSet<ClassIDType>();
private readonly HashSet<string> importFilesHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> noexistFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> assetsFileListHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
public UnityVersion SpecifyUnityVersion
public AssetsManager()
{
get => specifiedUnityVersion;
set
{
if (specifiedUnityVersion == value)
{
return;
}
if (value == null)
{
specifiedUnityVersion = null;
Logger.Info("Specified Unity version: None");
return;
}
if (string.IsNullOrEmpty(value.BuildType))
{
throw new NotSupportedException("Specified Unity version is not in a correct format.\n" +
"Specify full Unity version, including letters at the end.\n" +
"Example: 2017.4.39f1");
}
specifiedUnityVersion = value;
Logger.Info($"Specified Unity version: {specifiedUnityVersion}");
}
OptionLoaders.Add(LoadImportOptions);
}
public void SetAssetFilter(params ClassIDType[] classIDTypes)
{
if (filteredAssetTypesList.Count == 0)
filteredAssetTypesList.UnionWith(new[]
{
filteredAssetTypesList.UnionWith(new HashSet<ClassIDType>
{
ClassIDType.AssetBundle,
ClassIDType.ResourceManager,
ClassIDType.GameObject,
ClassIDType.Transform,
});
}
ClassIDType.AssetBundle,
ClassIDType.ResourceManager,
ClassIDType.GameObject,
ClassIDType.Transform,
ClassIDType.RectTransform,
});
if (classIDTypes.Contains(ClassIDType.MonoBehaviour))
{
@@ -75,6 +55,23 @@ namespace AssetStudio
filteredAssetTypesList.Add(ClassIDType.Texture2D);
filteredAssetTypesList.Add(ClassIDType.SpriteAtlas);
}
if (classIDTypes.Contains(ClassIDType.Animator))
{
filteredAssetTypesList.Add(ClassIDType.AnimatorController);
filteredAssetTypesList.Add(ClassIDType.AnimatorOverrideController);
filteredAssetTypesList.Add(ClassIDType.Animation);
filteredAssetTypesList.Add(ClassIDType.AnimationClip);
filteredAssetTypesList.Add(ClassIDType.Avatar);
filteredAssetTypesList.Add(ClassIDType.Material);
filteredAssetTypesList.Add(ClassIDType.MeshFilter);
filteredAssetTypesList.Add(ClassIDType.MeshRenderer);
filteredAssetTypesList.Add(ClassIDType.SkinnedMeshRenderer);
}
if (classIDTypes.Contains(ClassIDType.AnimatorController))
{
filteredAssetTypesList.Add(ClassIDType.Animator);
filteredAssetTypesList.Add(ClassIDType.AnimatorOverrideController);
}
filteredAssetTypesList.UnionWith(classIDTypes);
}
@@ -84,32 +81,28 @@ namespace AssetStudio
SetAssetFilter(classIDTypeList.ToArray());
}
public void LoadFilesAndFolders(params string[] path)
public void LoadFilesAndFolders(params string[] paths)
{
var pathList = new List<string>();
pathList.AddRange(path);
LoadFilesAndFolders(out _, pathList);
LoadFilesAndFolders(out _, paths.ToList());
}
public void LoadFilesAndFolders(out string parentPath, params string[] path)
public void LoadFilesAndFolders(out string parentPath, params string[] paths)
{
var pathList = new List<string>();
pathList.AddRange(path);
LoadFilesAndFolders(out parentPath, pathList);
LoadFilesAndFolders(out parentPath, paths.ToList());
}
public void LoadFilesAndFolders(out string parentPath, List<string> pathList)
{
var fileList = new List<string>();
bool filesInPath = false;
var filesInPath = false;
parentPath = "";
foreach (var path in pathList)
{
var fullPath = Path.GetFullPath(path);
if (Directory.Exists(fullPath))
{
var parent = Directory.GetParent(fullPath).FullName;
if (!filesInPath && (parentPath == "" || parentPath.Length > parent.Length))
var parent = Directory.GetParent(fullPath)?.FullName;
if (!filesInPath && (parentPath == "" || parentPath?.Length > parent?.Length))
{
parentPath = parent;
}
@@ -127,6 +120,8 @@ namespace AssetStudio
{
MergeSplitAssets(parentPath);
}
LoadOptionFiles(fileList);
var toReadFile = ProcessingSplitFiles(fileList);
fileList.Clear();
pathList.Clear();
@@ -160,6 +155,8 @@ namespace AssetStudio
importFilesHash.Clear();
noexistFiles.Clear();
assetsFileListHash.Clear();
if (AssetsFileList.Count == 0)
return;
ReadAssets();
ProcessAssets();
@@ -171,12 +168,15 @@ namespace AssetStudio
return LoadFile(reader);
}
private bool LoadFile(FileReader reader)
private bool LoadFile(FileReader reader, bool fromZip = false)
{
switch (reader?.FileType)
if (reader == null)
return false;
switch (reader.FileType)
{
case FileType.AssetsFile:
return LoadAssetsFile(reader);
return LoadAssetsFile(reader, fromZip);
case FileType.BundleFile:
return LoadBundleFile(reader);
case FileType.WebFile:
@@ -191,22 +191,27 @@ namespace AssetStudio
case FileType.ZipFile:
LoadZipFile(reader);
break;
case FileType.ResourceFile when !fromZip:
reader.Dispose();
break;
}
return true;
}
private bool LoadAssetsFile(FileReader reader)
private bool LoadAssetsFile(FileReader reader, bool fromZip)
{
if (!assetsFileListHash.Contains(reader.FileName))
{
Logger.Info($"Loading {reader.FullPath}");
Logger.Info($"Loading \"{reader.FullPath}\"");
try
{
var assetsFile = new SerializedFile(reader, this);
var dirName = Path.GetDirectoryName(reader.FullPath);
CheckStrippedVersion(assetsFile);
assetsFileList.Add(assetsFile);
AssetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName);
if (fromZip)
return true;
foreach (var sharedFile in assetsFile.m_Externals)
{
@@ -233,6 +238,7 @@ namespace AssetStudio
else
{
noexistFiles.Add(sharedFilePath);
Logger.Warning($"Dependency wasn't found: {sharedFilePath}");
}
}
}
@@ -246,13 +252,13 @@ namespace AssetStudio
}
catch (Exception e)
{
Logger.Warning($"Failed to read assets file {reader.FullPath}\r\n{e}");
Logger.Warning($"Failed to read assets file \"{reader.FullPath}\"\n{e}");
reader.Dispose();
}
}
else
{
Logger.Info($"Skipping {reader.FullPath}");
Logger.Info($"Skipping \"{reader.FullPath}\"");
reader.Dispose();
}
return true;
@@ -268,52 +274,67 @@ namespace AssetStudio
assetsFile.originalPath = originalPath;
if (assetBundleUnityVer != null && assetsFile.header.m_Version < SerializedFileFormatVersion.Unknown_7)
{
assetsFile.SetVersion(assetBundleUnityVer);
assetsFile.version = assetBundleUnityVer;
}
CheckStrippedVersion(assetsFile, assetBundleUnityVer);
assetsFileList.Add(assetsFile);
AssetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName);
}
catch (NotSupportedException e)
{
Logger.Error(e.Message);
resourceFileReaders.TryAdd(reader.FileName, reader);
reader.Dispose();
return false;
}
catch (Exception e)
{
Logger.Warning($"Failed to read assets file {reader.FullPath} from {Path.GetFileName(originalPath)}\r\n{e}");
Logger.Warning($"Failed to read assets file \"{reader.FullPath}\" from {Path.GetFileName(originalPath)}\n{e}");
resourceFileReaders.TryAdd(reader.FileName, reader);
}
}
else
{
Logger.Info($"Skipping {originalPath} ({reader.FileName})");
Logger.Info($"Skipping \"{originalPath}\" ({reader.FileName})");
}
return true;
}
private bool LoadBundleFile(FileReader reader, string originalPath = null)
{
Logger.Info("Loading " + reader.FullPath);
Logger.Info($"Loading \"{reader.FullPath}\"");
Logger.Debug($"Bundle offset: {reader.Position}");
var bundleStream = new OffsetStream(reader);
var bundleReader = new FileReader(reader.FullPath, bundleStream);
var isLoaded = false;
try
{
var bundleFile = new BundleFile(reader, ZstdEnabled, specifiedUnityVersion);
foreach (var file in bundleFile.fileList)
var bundleFile = new BundleFile(bundleReader, Options.BundleOptions);
isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath);
if (!isLoaded)
return false;
while (bundleFile.IsDataAfterBundle && isLoaded)
{
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
var subReader = new FileReader(dummyPath, file.stream);
if (subReader.FileType == FileType.AssetsFile)
isLoaded = false;
bundleStream.Offset = reader.Position;
bundleReader = new FileReader($"{reader.FullPath}_0x{bundleStream.Offset:X}", bundleStream);
if (bundleReader.FileType != FileType.BundleFile)
{
if (!LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision))
return false;
Logger.Debug("Unknown data was detected after the end of the bundle.");
break;
}
else
if (bundleReader.Position > 0)
{
resourceFileReaders.TryAdd(file.fileName, subReader);
bundleStream.Offset += bundleReader.Position;
bundleReader.FullPath = $"{reader.FullPath}_0x{bundleStream.Offset:X}";
bundleReader.FileName = $"{reader.FileName}_0x{bundleStream.Offset:X}";
}
Logger.Info($"[MultiBundle] Loading \"{reader.FileName}\" from offset: 0x{bundleStream.Offset:X}");
bundleFile = new BundleFile(bundleReader, Options.BundleOptions, isMultiBundle: true);
isLoaded = LoadBundleFiles(bundleReader, bundleFile, originalPath ?? reader.FullPath);
}
return true;
return isLoaded;
}
catch (NotSupportedException e)
{
@@ -322,23 +343,46 @@ namespace AssetStudio
}
catch (Exception e)
{
var str = $"Error while reading bundle file {reader.FullPath}";
var str = $"Error while reading bundle file \"{bundleReader.FullPath}\"";
if (originalPath != null)
{
str += $" from {Path.GetFileName(originalPath)}";
}
Logger.Warning($"{str}\r\n{e}");
Logger.Warning($"{str}\n{e}");
return true;
}
finally
{
reader.Dispose();
if (!isLoaded)
bundleReader.Dispose();
}
}
private bool LoadBundleFiles(FileReader reader, BundleFile bundleFile, string originalPath = null)
{
foreach (var file in bundleFile.fileList)
{
if (file.stream == null)
continue;
file.stream.Position = 0; //go to file offset
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
var subReader = new FileReader(dummyPath, file.stream);
if (subReader.FileType == FileType.AssetsFile)
{
if (!LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision))
return false;
}
else
{
resourceFileReaders.TryAdd(file.fileName, subReader);
}
}
return true;
}
private void LoadWebFile(FileReader reader)
{
Logger.Info("Loading " + reader.FullPath);
Logger.Info($"Loading \"{reader.FullPath}\"");
try
{
var webFile = new WebFile(reader);
@@ -358,14 +402,14 @@ namespace AssetStudio
LoadWebFile(subReader);
break;
case FileType.ResourceFile:
resourceFileReaders[file.fileName] = subReader; //TODO
resourceFileReaders.TryAdd(file.fileName, subReader);
break;
}
}
}
catch (Exception e)
{
Logger.Error($"Error while reading web file {reader.FullPath}", e);
Logger.Error($"Error while reading web file \"{reader.FullPath}\"", e);
}
finally
{
@@ -402,15 +446,16 @@ namespace AssetStudio
}
// merge split files and load the result
foreach (string basePath in splitFiles)
for (var i = 0; i < splitFiles.Count; i++)
{
var basePath = splitFiles[i].Replace("\\", "/");
try
{
Stream splitStream = new MemoryStream();
int i = 0;
var j = 0;
while (true)
{
string path = $"{basePath}.split{i++}";
string path = $"{basePath}.split{j++}";
ZipArchiveEntry entry = archive.GetEntry(path);
if (entry == null)
break;
@@ -421,11 +466,12 @@ namespace AssetStudio
}
splitStream.Seek(0, SeekOrigin.Begin);
FileReader entryReader = new FileReader(basePath, splitStream);
LoadFile(entryReader);
if (!LoadFile(entryReader, fromZip: true))
break;
}
catch (Exception e)
{
Logger.Warning($"Error while reading zip split file {basePath}\r\n{e}");
Logger.Warning($"Error while reading zip split file \"{basePath}\"\n{e}");
}
}
@@ -435,6 +481,8 @@ namespace AssetStudio
Progress.Reset();
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.Length == 0)
continue;
try
{
string dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), reader.FileName, entry.FullName);
@@ -449,7 +497,9 @@ namespace AssetStudio
streamReader.Position = 0;
FileReader entryReader = new FileReader(dummyPath, streamReader);
LoadFile(entryReader);
if (!LoadFile(entryReader, fromZip: true))
break;
if (entryReader.FileType == FileType.ResourceFile)
{
entryReader.Position = 0;
@@ -459,7 +509,7 @@ namespace AssetStudio
}
catch (Exception e)
{
Logger.Warning($"Error while reading zip entry {entry.FullName}\r\n{e}");
Logger.Warning($"Error while reading zip entry \"{entry.FullName}\"\n{e}");
}
}
}
@@ -474,29 +524,92 @@ namespace AssetStudio
}
}
public void LoadOptionFiles(List<string> pathList)
{
if (pathList.Count == 0)
return;
var optionFileIndexes = new List<int>();
for (var i = 0; i < pathList.Count; i++)
{
var path = pathList[i];
if (!path.EndsWith(OptionsFile.Extension, StringComparison.OrdinalIgnoreCase))
continue;
optionFileIndexes.Add(i);
var optionsFile = LoadOptionsFile(new FileReader(path));
if (optionsFile == null)
continue;
foreach (var optionsLoader in OptionLoaders)
{
optionsLoader(optionsFile);
}
}
for (var i = 0; i < optionFileIndexes.Count; i++)
{
pathList.RemoveAt(optionFileIndexes[i] - i);
}
}
private static OptionsFile LoadOptionsFile(FileReader reader)
{
Logger.Info($"Loading options file \"{reader.FullPath}\"");
try
{
return new OptionsFile(reader);
}
catch (Exception e)
{
Logger.Warning($"Error while loading options file \"{reader.FullPath}\"\n{e}");
return null;
}
finally
{
reader.Dispose();
}
}
private void LoadImportOptions(OptionsFile optionsFile)
{
try
{
var importOptions = ImportOptions.FromOptionsFile(optionsFile);
if (importOptions == null)
return;
Options = importOptions;
Logger.Info("Import options successfully loaded.");
}
catch (Exception e)
{
Logger.Warning($"Error while reading import options\n{e}");
}
}
public void CheckStrippedVersion(SerializedFile assetsFile, UnityVersion bundleUnityVer = null)
{
if (assetsFile.version.IsStripped && specifiedUnityVersion == null)
if (assetsFile.version.IsStripped && Options.CustomUnityVersion == null)
{
var msg = "The asset's Unity version has been stripped, please set the version in the options.";
if (bundleUnityVer != null && !bundleUnityVer.IsStripped)
msg += $"\n\nAssumed Unity version based on asset bundle: {bundleUnityVer}";
throw new NotSupportedException(msg);
}
if (specifiedUnityVersion != null)
if (Options.CustomUnityVersion != null)
{
assetsFile.SetVersion(SpecifyUnityVersion);
assetsFile.version = Options.CustomUnityVersion;
}
}
public void Clear()
{
foreach (var assetsFile in assetsFileList)
foreach (var assetsFile in AssetsFileList)
{
assetsFile.Objects.Clear();
assetsFile.reader.Close();
}
assetsFileList.Clear();
AssetsFileList.Clear();
foreach (var resourceFileReader in resourceFileReaders)
{
@@ -511,11 +624,20 @@ namespace AssetStudio
{
Logger.Info("Read assets...");
var progressCount = assetsFileList.Sum(x => x.m_Objects.Count);
int i = 0;
Progress.Reset();
foreach (var assetsFile in assetsFileList)
var jsonOptions = new JsonSerializerOptions
{
Converters = { new JsonConverterHelper.ByteArrayConverter(), new JsonConverterHelper.PPtrConverter(), new JsonConverterHelper.KVPConverter() },
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
PropertyNameCaseInsensitive = true,
IncludeFields = true,
};
var progressCount = AssetsFileList.Sum(x => x.m_Objects.Count);
var i = 0;
Progress.Reset();
foreach (var assetsFile in AssetsFileList)
{
JsonConverterHelper.AssetsFile = assetsFile;
foreach (var objectInfo in assetsFile.m_Objects)
{
var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objectInfo);
@@ -532,8 +654,8 @@ namespace AssetStudio
obj = new Animation(objectReader);
break;
case ClassIDType.AnimationClip:
obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled
? new AnimationClip(objectReader, TypeTreeHelper.ReadType(objectReader.serializedType.m_Type, objectReader))
obj = objectReader.serializedType?.m_Type != null && LoadViaTypeTree
? new AnimationClip(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions, objectInfo)
: new AnimationClip(objectReader);
break;
case ClassIDType.Animator:
@@ -554,6 +676,9 @@ namespace AssetStudio
case ClassIDType.Avatar:
obj = new Avatar(objectReader);
break;
case ClassIDType.BuildSettings:
obj = new BuildSettings(objectReader);
break;
case ClassIDType.Font:
obj = new Font(objectReader);
break;
@@ -561,7 +686,9 @@ namespace AssetStudio
obj = new GameObject(objectReader);
break;
case ClassIDType.Material:
obj = new Material(objectReader);
obj = objectReader.serializedType?.m_Type != null && LoadViaTypeTree
? new Material(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions)
: new Material(objectReader);
break;
case ClassIDType.Mesh:
obj = new Mesh(objectReader);
@@ -607,13 +734,13 @@ namespace AssetStudio
obj = new TextAsset(objectReader);
break;
case ClassIDType.Texture2D:
obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled
? new Texture2D(objectReader, TypeTreeHelper.ReadType(objectReader.serializedType.m_Type, objectReader))
obj = objectReader.serializedType?.m_Type != null && LoadViaTypeTree
? new Texture2D(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions)
: new Texture2D(objectReader);
break;
case ClassIDType.Texture2DArray:
obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled
? new Texture2DArray(objectReader, TypeTreeHelper.ReadType(objectReader.serializedType.m_Type, objectReader))
obj = objectReader.serializedType?.m_Type != null && LoadViaTypeTree
? new Texture2DArray(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions)
: new Texture2DArray(objectReader);
break;
case ClassIDType.Transform:
@@ -655,7 +782,7 @@ namespace AssetStudio
{
Logger.Info("Process assets...");
foreach (var assetsFile in assetsFileList)
foreach (var assetsFile in AssetsFileList)
{
foreach (var obj in assetsFile.Objects)
{
@@ -685,6 +812,34 @@ namespace AssetStudio
case Animation m_Animation:
m_GameObject.m_Animation = m_Animation;
break;
case MonoBehaviour m_MonoBehaviour:
if (m_MonoBehaviour.m_Script.TryGet(out var m_Script))
{
switch (m_Script.m_ClassName)
{
case "CubismModel":
if (m_GameObject.m_Transform == null)
break;
m_GameObject.CubismModel = new CubismModel(m_GameObject)
{
CubismModelMono = m_MonoBehaviour
};
break;
case "CubismPhysicsController":
if (m_GameObject.CubismModel != null)
m_GameObject.CubismModel.PhysicsController = m_MonoBehaviour;
break;
case "CubismFadeController":
if (m_GameObject.CubismModel != null)
m_GameObject.CubismModel.FadeController = m_MonoBehaviour;
break;
case "CubismExpressionController":
if (m_GameObject.CubismModel != null)
m_GameObject.CubismModel.ExpressionController = m_MonoBehaviour;
break;
}
}
break;
}
}
}
@@ -699,16 +854,17 @@ namespace AssetStudio
{
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
}
else if (m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlaOld))
else if (m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlasOld))
{
if (m_SpriteAtlaOld.m_IsVariant)
if (m_SpriteAtlasOld.m_IsVariant)
{
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
}
}
else
{
Logger.Warning($"\"{m_Sprite.m_Name}\": Sprite loading error. SpriteAtlas with PathID: \"{m_Sprite.m_SpriteAtlas.m_PathID}\" was not found.");
Logger.Debug($"\"{m_Sprite.m_Name}\": The actual SpriteAtlas PathID \"{m_SpriteAtlas.m_PathID}\" does not match the specified one \"{m_Sprite.m_SpriteAtlas.m_PathID}\".");
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
}
}
}

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public enum BuildTarget
{
@@ -53,6 +48,18 @@ namespace AssetStudio
PS5,
EmbeddedLinux,
QNX,
VisionOS,
Switch2,
UnknownPlatform = 9999
}
public enum TuanjieBuildTarget
{
MiniGame = 47,
OpenHarmony,
HMIAndroid,
ArmLinux,
ArmLinuxServer,
VisionOS,
}
}

View File

@@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
{
public class BuildType
{
private string buildType;
public BuildType(string type)
{
buildType = type;
}
public bool IsAlpha => buildType == "a";
public bool IsPatch => buildType == "p";
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Runtime.InteropServices;
#if NETFRAMEWORK
using AssetStudio.PInvoke;
#endif
namespace BundleCompression.Oodle
{
public static class OodleLZ
{
private const string LibName = "ooz";
#if NETFRAMEWORK
static OodleLZ()
{
DllLoader.PreloadDll(LibName);
}
#endif
[DllImport(LibName)]
private static extern int Ooz_Decompress(
in byte srcBuffer,
UIntPtr srcLen,
ref byte dstBuffer,
UIntPtr dstLen,
int fuzzSafetyFlag,
int crcCheckFlag,
int logVerbosityFlag,
UIntPtr rawBuffer,
UIntPtr rawBufferSize,
UIntPtr chunkDecodeCallback,
UIntPtr chunkDecodeContext,
UIntPtr scratchBuf,
UIntPtr scratchBufSize,
int threadPhase);
public static int Decompress(ReadOnlySpan<byte> srcSpanBuffer, Span<byte> dstSpanBuffer)
{
return Ooz_Decompress(in srcSpanBuffer[0], (UIntPtr)srcSpanBuffer.Length, ref dstSpanBuffer[0], (UIntPtr)dstSpanBuffer.Length,
0, 0, 0, UIntPtr.Zero, UIntPtr.Zero, UIntPtr.Zero, UIntPtr.Zero, UIntPtr.Zero, UIntPtr.Zero, 0);
}
}
}

View File

@@ -1,6 +1,7 @@
// LzmaDecoder.cs
using System;
using AssetStudio;
namespace SevenZip.Compression.LZMA
{
@@ -247,6 +248,8 @@ namespace SevenZip.Compression.LZMA
m_OutWindow.PutByte(b);
nowPos64++;
}
Progress.Reset();
while (nowPos64 < outSize64)
{
// UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64);
@@ -338,6 +341,8 @@ namespace SevenZip.Compression.LZMA
}
m_OutWindow.CopyBlock(rep0, len);
nowPos64 += len;
Progress.Report((int)(nowPos64 * 100f / outSize64), 100);
}
}
}

View File

@@ -2,12 +2,11 @@ using System;
using System.IO;
using SevenZip.Compression.LZMA;
namespace AssetStudio
namespace BundleCompression.Lzma
{
public static class SevenZipHelper
public static class SevenZipLzma
{
public static MemoryStream StreamDecompress(MemoryStream inStream)
public static MemoryStream DecompressStream(MemoryStream inStream)
{
var decoder = new Decoder();
@@ -34,7 +33,7 @@ namespace AssetStudio
return newOutStream;
}
public static void StreamDecompress(Stream compressedStream, Stream decompressedStream, long compressedSize, long decompressedSize)
public static long DecompressStream(Stream compressedStream, Stream decompressedStream, long compressedSize, long decompressedSize)
{
var basePosition = compressedStream.Position;
var decoder = new Decoder();
@@ -44,6 +43,7 @@ namespace AssetStudio
decoder.SetDecoderProperties(properties);
decoder.Code(compressedStream, decompressedStream, compressedSize - 5, decompressedSize, null);
compressedStream.Position = basePosition + compressedSize;
return decompressedStream.Position;
}
}
}

View File

@@ -0,0 +1,65 @@
using BundleCompression.Lzma;
using BundleCompression.Oodle;
using System;
using System.IO;
using System.Text.RegularExpressions;
using K4os.Compression.LZ4;
using ZstdSharp;
namespace AssetStudio
{
public static class BundleDecompressionHelper
{
private static readonly Decompressor ZstdDecompressor = new Decompressor();
private static readonly string MsgPattern = @"\. ";
public static MemoryStream DecompressLzmaStream(MemoryStream inStream)
{
return SevenZipLzma.DecompressStream(inStream);
}
public static long DecompressLzmaStream(Stream compressedStream, Stream decompressedStream, long compressedSize, long decompressedSize, ref string errorMsg)
{
var numWrite = -1L;
try
{
numWrite = SevenZipLzma.DecompressStream(compressedStream, decompressedStream, compressedSize, decompressedSize);
}
catch (Exception e)
{
Logger.Debug(e.ToString());
errorMsg = $"({Regex.Split(e.Message, MsgPattern, RegexOptions.CultureInvariant)[0]})";
}
return numWrite;
}
public static int DecompressBlock(CompressionType type, ReadOnlySpan<byte> srcBuffer, Span<byte> dstBuffer, ref string errorMsg)
{
var numWrite = -1;
try
{
switch (type)
{
case CompressionType.Lz4:
case CompressionType.Lz4HC:
numWrite = LZ4Codec.Decode(srcBuffer, dstBuffer);
break;
case CompressionType.Zstd:
numWrite = ZstdDecompressor.Unwrap(srcBuffer, dstBuffer);
break;
case CompressionType.Oodle:
numWrite = OodleLZ.Decompress(srcBuffer, dstBuffer);
break;
default:
throw new NotSupportedException();
}
}
catch (Exception e)
{
Logger.Debug(e.ToString());
errorMsg = $"({Regex.Split(e.Message, MsgPattern, RegexOptions.CultureInvariant)[0]})";
}
return numWrite;
}
}
}

View File

@@ -1,8 +1,8 @@
using K4os.Compression.LZ4;
using ZstdSharp;
using System;
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using AssetStudio.CustomOptions;
namespace AssetStudio
{
@@ -19,8 +19,8 @@ namespace AssetStudio
[Flags]
public enum CnEncryptionFlags
{
OldFlag = 0x200,
NewFlag = 0x400
V1 = 0x200,
V2_V3 = 0x1400,
}
[Flags]
@@ -32,16 +32,21 @@ namespace AssetStudio
public enum CompressionType
{
Auto = -1,
None,
Lzma,
Lz4,
Lz4HC,
Lzham,
Custom,
Zstd, //custom
Oodle, //custom
}
public class BundleFile
{
public readonly bool IsDataAfterBundle;
private readonly CustomBundleOptions _bundleOptions;
public class Header
{
public string signature;
@@ -73,15 +78,19 @@ namespace AssetStudio
private StorageBlock[] m_BlocksInfo;
private Node[] m_DirectoryInfo;
public StreamFile[] fileList;
public List<StreamFile> fileList;
public BundleFile(FileReader reader, bool useZstd, UnityVersion specUnityVer = null)
public BundleFile(FileReader reader, CustomBundleOptions bundleOptions, bool isMultiBundle = false)
{
_bundleOptions = bundleOptions;
m_Header = new Header();
m_Header.signature = reader.ReadStringToNull();
m_Header.version = reader.ReadUInt32();
m_Header.unityVersion = reader.ReadStringToNull();
m_Header.unityRevision = new UnityVersion(reader.ReadStringToNull());
var revStr = reader.ReadStringToNull();
if (!UnityVersion.TryParse(revStr, out m_Header.unityRevision))
m_Header.unityRevision = new UnityVersion();
switch (m_Header.signature)
{
case "UnityArchive":
@@ -93,58 +102,47 @@ namespace AssetStudio
goto case "UnityFS";
}
ReadHeaderAndBlocksInfo(reader);
using (var blocksStream = CreateBlocksStream(reader.FullPath))
using (reader)
{
ReadBlocksAndDirectory(reader, blocksStream);
ReadFiles(blocksStream, reader.FullPath);
ReadFiles(ReadBlocksAndDirectory(reader));
}
break;
case "UnityFS":
ReadHeader(reader);
var isUnityCnEnc = false;
var bundleSize = m_Header.size;
var streamSize = reader.BaseStream.Length;
if (bundleSize > streamSize)
Logger.Warning("Bundle size is incorrect.");
IsDataAfterBundle = streamSize - bundleSize > 200;
var unityVer = m_Header.unityRevision;
if (specUnityVer != null)
var customUnityVer = _bundleOptions.Options.CustomUnityVersion;
if (customUnityVer != null)
{
if (!unityVer.IsStripped && specUnityVer != unityVer)
if (!unityVer.IsStripped && customUnityVer != unityVer)
{
Logger.Warning($"Detected Unity version is different from the specified one ({specUnityVer.FullVersion.Color(ColorConsole.BrightCyan)}).\n" +
Logger.Warning($"Detected Unity version is different from the specified one ({customUnityVer.FullVersion.Color(ColorConsole.BrightCyan)}).\n" +
$"Assets may load with errors.\n" +
$"It is recommended to specify the detected Unity version: {unityVer.FullVersion.Color(ColorConsole.BrightCyan)}");
}
unityVer = specUnityVer;
unityVer = customUnityVer;
}
if (!unityVer.IsStripped)
{
// https://issuetracker.unity3d.com/issues/files-within-assetbundles-do-not-start-on-aligned-boundaries-breaking-patching-on-nintendo-switch
if (unityVer < 2020
|| unityVer.IsInRange(2020, (2020, 3, 34))
|| unityVer.IsInRange(2021, (2021, 3, 2))
|| unityVer.IsInRange(2022, (2022, 1, 1)))
{
isUnityCnEnc = ((CnEncryptionFlags)m_Header.flags & CnEncryptionFlags.OldFlag) != 0;
}
else
{
isUnityCnEnc = ((CnEncryptionFlags)m_Header.flags & CnEncryptionFlags.NewFlag) != 0;
}
}
if (isUnityCnEnc)
{
var msg = "Unsupported bundle file. ";
msg += specUnityVer != null
? "UnityCN encryption was detected or the specified Unity version is incorrect."
: "UnityCN encryption was detected.";
throw new NotSupportedException(msg);
}
UnityCnCheck(reader, unityVer);
ReadBlocksInfoAndDirectory(reader, unityVer);
using (var blocksStream = CreateBlocksStream(reader.FullPath))
if (IsUncompressedBundle && !IsDataAfterBundle && !isMultiBundle)
{
ReadBlocks(reader, blocksStream, useZstd);
ReadFiles(blocksStream, reader.FullPath);
Logger.Debug($"[Uncompressed bundle] BlockData count: {m_BlocksInfo.Length}");
ReadFiles(reader.BaseStream, reader.Position);
break;
}
ReadFiles(ReadBlocks(reader));
if (!IsDataAfterBundle)
reader.Close();
break;
}
}
@@ -163,7 +161,7 @@ namespace AssetStudio
m_BlocksInfo = new StorageBlock[1];
for (int i = 0; i < levelCount; i++)
{
var storageBlock = new StorageBlock()
var storageBlock = new StorageBlock
{
compressedSize = reader.ReadUInt32(),
uncompressedSize = reader.ReadUInt32(),
@@ -186,23 +184,24 @@ namespace AssetStudio
private Stream CreateBlocksStream(string path)
{
Stream blocksStream;
var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.uncompressedSize);
if (uncompressedSizeSum >= int.MaxValue)
if (uncompressedSizeSum < int.MaxValue && !_bundleOptions.DecompressToDisk)
return new MemoryStream((int)uncompressedSizeSum);
if (!Directory.Exists(Path.GetDirectoryName(path)))
{
/*var memoryMappedFile = MemoryMappedFile.CreateNew(null, uncompressedSizeSum);
assetsDataStream = memoryMappedFile.CreateViewStream();*/
blocksStream = new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose);
var tempDir = Path.Combine(Directory.GetCurrentDirectory(), "Studio_temp");
Directory.CreateDirectory(tempDir);
var filename = Path.GetFileName(path);
var hash = path.GetHashCode();
path = Path.Combine(tempDir, $"{filename}_{hash:X}");
}
else
{
blocksStream = new MemoryStream((int)uncompressedSizeSum);
}
return blocksStream;
return new TempFileStream(path + ".temp", FileMode.Create);
}
private void ReadBlocksAndDirectory(FileReader reader, Stream blocksStream)
private Stream ReadBlocksAndDirectory(FileReader reader)
{
var blocksStream = CreateBlocksStream(reader.FullPath);
var isCompressed = m_Header.signature == "UnityWeb";
foreach (var blockInfo in m_BlocksInfo)
{
@@ -211,7 +210,7 @@ namespace AssetStudio
{
using (var memoryStream = new MemoryStream(uncompressedBytes))
{
using (var decompressStream = SevenZipHelper.StreamDecompress(memoryStream))
using (var decompressStream = BundleDecompressionHelper.DecompressLzmaStream(memoryStream))
{
uncompressedBytes = decompressStream.ToArray();
}
@@ -220,10 +219,11 @@ namespace AssetStudio
blocksStream.Write(uncompressedBytes, 0, uncompressedBytes.Length);
}
blocksStream.Position = 0;
var blocksReader = new EndianBinaryReader(blocksStream);
var nodesCount = blocksReader.ReadInt32();
m_DirectoryInfo = new Node[nodesCount];
for (int i = 0; i < nodesCount; i++)
for (var i = 0; i < nodesCount; i++)
{
m_DirectoryInfo[i] = new Node
{
@@ -232,33 +232,27 @@ namespace AssetStudio
size = blocksReader.ReadUInt32()
};
}
return blocksStream;
}
public void ReadFiles(Stream blocksStream, string path)
private void ReadFiles(Stream inputStream, long blocksOffset = 0)
{
fileList = new StreamFile[m_DirectoryInfo.Length];
for (int i = 0; i < m_DirectoryInfo.Length; i++)
fileList = new List<StreamFile>(m_DirectoryInfo.Length);
foreach (var node in m_DirectoryInfo)
{
var node = m_DirectoryInfo[i];
var file = new StreamFile();
fileList[i] = file;
fileList.Add(file);
file.path = node.path;
file.fileName = Path.GetFileName(node.path);
if (node.size >= int.MaxValue)
try
{
/*var memoryMappedFile = MemoryMappedFile.CreateNew(null, entryinfo_size);
file.stream = memoryMappedFile.CreateViewStream();*/
var extractPath = path + "_unpacked" + Path.DirectorySeparatorChar;
Directory.CreateDirectory(extractPath);
file.stream = new FileStream(extractPath + file.fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
file.stream = new OffsetStream(inputStream, node.offset + blocksOffset, node.size);
}
else
catch (IOException e)
{
file.stream = new MemoryStream((int)node.size);
Logger.Warning($"Failed to access {file.fileName} file.\n{e}");
}
blocksStream.Position = node.offset;
blocksStream.CopyTo(file.stream, node.size);
file.stream.Position = 0;
}
}
@@ -274,7 +268,7 @@ namespace AssetStudio
}
}
private void ReadBlocksInfoAndDirectory(FileReader reader, UnityVersion unityVer)
private void ReadBlocksInfoAndDirectory(FileReader reader, UnityVersion unityVer, bool silent = false)
{
byte[] blocksInfoBytes;
@@ -295,52 +289,88 @@ namespace AssetStudio
}
}
var compressedSize = (int)m_Header.compressedBlocksInfoSize;
var uncompressedSize = (int)m_Header.uncompressedBlocksInfoSize;
if (uncompressedSize < 0 || compressedSize < 0 || compressedSize > reader.BaseStream.Length)
{
throw new IOException("Incorrect blockInfo length.\nBlockInfo sizes might be encrypted.\n");
}
if ((m_Header.flags & ArchiveFlags.BlocksInfoAtTheEnd) != 0)
{
var position = reader.Position;
reader.Position = reader.BaseStream.Length - m_Header.compressedBlocksInfoSize;
blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
reader.Position = m_Header.size - compressedSize;
blocksInfoBytes = reader.ReadBytes(compressedSize);
reader.Position = position;
}
else //0x40 BlocksAndDirectoryInfoCombined
{
blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
blocksInfoBytes = reader.ReadBytes(compressedSize);
}
MemoryStream blocksInfoUncompressedStream;
var uncompressedSize = m_Header.uncompressedBlocksInfoSize;
var customBlockInfoCompression = _bundleOptions.CustomBlockInfoCompression;
var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask);
if (customBlockInfoCompression == CompressionType.Auto)
{
if (!silent && compressionType > CompressionType.Lzham && Enum.IsDefined(typeof(CompressionType), compressionType))
{
Logger.Warning($"Non-standard blockInfo compression type: {(int)compressionType}. Trying to decompress as {compressionType} archive..");
}
}
else if (compressionType != CompressionType.None)
{
compressionType = customBlockInfoCompression;
if (!silent)
{
Logger.Info($"Custom blockInfo compression type: {customBlockInfoCompression}");
}
}
Logger.Debug($"BlockInfo compression: {compressionType}");
int numWrite;
var errorMsg = string.Empty;
MemoryStream blocksInfoUncompressedStream;
switch (compressionType)
{
case CompressionType.None:
{
blocksInfoUncompressedStream = new MemoryStream(blocksInfoBytes);
numWrite = compressedSize;
break;
}
case CompressionType.Lzma:
{
blocksInfoUncompressedStream = new MemoryStream((int) (uncompressedSize));
blocksInfoUncompressedStream = new MemoryStream(uncompressedSize);
using (var blocksInfoCompressedStream = new MemoryStream(blocksInfoBytes))
{
SevenZipHelper.StreamDecompress(blocksInfoCompressedStream, blocksInfoUncompressedStream,
m_Header.compressedBlocksInfoSize, m_Header.uncompressedBlocksInfoSize);
numWrite = (int)BundleDecompressionHelper.DecompressLzmaStream(blocksInfoCompressedStream, blocksInfoUncompressedStream, compressedSize, uncompressedSize, ref errorMsg);
}
blocksInfoUncompressedStream.Position = 0;
break;
}
case CompressionType.Lz4:
case CompressionType.Lz4HC:
case CompressionType.Zstd:
case CompressionType.Oodle:
{
var uncompressedBytes = new byte[uncompressedSize];
var numWrite = LZ4Codec.Decode(blocksInfoBytes, uncompressedBytes);
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
numWrite = BundleDecompressionHelper.DecompressBlock(compressionType, blocksInfoBytes, uncompressedBytes, ref errorMsg);
blocksInfoUncompressedStream = new MemoryStream(uncompressedBytes);
break;
}
case CompressionType.Lzham:
throw new IOException($"Unsupported blockInfo compression type: {compressionType}.\n");
default:
throw new IOException($"Unsupported block info compression type {compressionType}");
throw new IOException($"Unknown blockInfo compression type: {compressionType}.\nYou may try to specify the compression type manually.\n");
}
if (numWrite != uncompressedSize)
{
var msg = $"{compressionType} blockInfo decompression error. {errorMsg}\nWrite {numWrite} bytes but expected {uncompressedSize} bytes.";
var exMsg = compressionType > CompressionType.Lz4HC || customBlockInfoCompression != CompressionType.Auto
? "Wrong compression type or blockInfo data might be encrypted."
: "BlockInfo data might be encrypted.";
throw new IOException($"{msg}\n{exMsg}\n");
}
using (var blocksInfoReader = new EndianBinaryReader(blocksInfoUncompressedStream))
@@ -348,7 +378,7 @@ namespace AssetStudio
var uncompressedDataHash = blocksInfoReader.ReadBytes(16);
var blocksInfoCount = blocksInfoReader.ReadInt32();
m_BlocksInfo = new StorageBlock[blocksInfoCount];
for (int i = 0; i < blocksInfoCount; i++)
for (var i = 0; i < blocksInfoCount; i++)
{
m_BlocksInfo[i] = new StorageBlock
{
@@ -360,7 +390,7 @@ namespace AssetStudio
var nodesCount = blocksInfoReader.ReadInt32();
m_DirectoryInfo = new Node[nodesCount];
for (int i = 0; i < nodesCount; i++)
for (var i = 0; i < nodesCount; i++)
{
m_DirectoryInfo[i] = new Node
{
@@ -377,75 +407,147 @@ namespace AssetStudio
}
}
private void ReadBlocks(FileReader reader, Stream blocksStream, bool useZstd)
private Stream ReadBlocks(FileReader reader)
{
var zstdCodec = new Decompressor();
var i = 0;
foreach (var blockInfo in m_BlocksInfo)
var customBlockCompression = _bundleOptions.CustomBlockCompression;
var blocksStream = CreateBlocksStream(reader.FullPath);
var blocksCompression = m_BlocksInfo.Max(x => (CompressionType)(x.flags & StorageBlockFlags.CompressionTypeMask));
var blockSize = (int)m_BlocksInfo.Max(x => x.uncompressedSize);
Logger.Debug($"BlockData compression: {blocksCompression}\n" +
$"BlockData count: {m_BlocksInfo.Length}\n" +
$"BlockSize: {blockSize}");
if (customBlockCompression == CompressionType.Auto)
{
var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask);
switch (compressionType)
if (blocksCompression > CompressionType.Lzham && Enum.IsDefined(typeof(CompressionType), blocksCompression))
{
case CompressionType.None:
{
reader.BaseStream.CopyTo(blocksStream, blockInfo.compressedSize);
break;
}
case CompressionType.Lzma:
{
SevenZipHelper.StreamDecompress(reader.BaseStream, blocksStream, blockInfo.compressedSize, blockInfo.uncompressedSize);
break;
}
case CompressionType.Lz4:
case CompressionType.Lz4HC:
case CompressionType.Custom:
{
var compressedSize = (int)blockInfo.compressedSize;
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
_ = reader.Read(compressedBytes, 0, compressedSize);
var uncompressedSize = (int)blockInfo.uncompressedSize;
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
try
{
var compTypeStr = compressionType.ToString();
if (compressionType == CompressionType.Custom)
{
compTypeStr = useZstd ? "Zstd" : "Lz4";
if (i == 0)
{
Logger.Debug($"Custom block compression type was detected. Trying to decompress as {compTypeStr} archive..");
i++;
}
}
int numWrite;
if (compressionType == CompressionType.Custom && useZstd)
{
numWrite = zstdCodec.Unwrap(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize);
}
else
{
numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize);
}
if (numWrite != uncompressedSize)
{
throw new IOException($"{compTypeStr} block decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
}
finally
{
BigArrayPool<byte>.Shared.Return(compressedBytes, clearArray: true);
BigArrayPool<byte>.Shared.Return(uncompressedBytes, clearArray: true);
}
break;
}
default:
throw new IOException($"Unsupported block compression type {compressionType}");
Logger.Warning($"Non-standard block compression type: {(int)blocksCompression}. Trying to decompress as {blocksCompression} archive..");
}
}
blocksStream.Position = 0;
else
{
Logger.Info($"Custom block compression type: {customBlockCompression}");
blocksCompression = customBlockCompression;
}
byte[] sharedCompressedBuff = null;
byte[] sharedUncompressedBuff = null;
if (blocksCompression != CompressionType.Lzma && blocksCompression != CompressionType.Lzham)
{
sharedCompressedBuff = BigArrayPool<byte>.Shared.Rent(blockSize);
sharedUncompressedBuff = BigArrayPool<byte>.Shared.Rent(blockSize);
}
try
{
foreach (var blockInfo in m_BlocksInfo)
{
var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask);
if (customBlockCompression != CompressionType.Auto && compressionType > 0)
{
compressionType = customBlockCompression;
}
long numWrite;
var errorMsg = string.Empty;
switch (compressionType)
{
case CompressionType.None:
reader.BaseStream.CopyTo(blocksStream, blockInfo.compressedSize);
numWrite = blockInfo.compressedSize;
break;
case CompressionType.Lzma:
Logger.Info("Decompressing LZMA stream...");
numWrite = BundleDecompressionHelper.DecompressLzmaStream(reader.BaseStream, blocksStream, blockInfo.compressedSize, blockInfo.uncompressedSize, ref errorMsg);
break;
case CompressionType.Lz4:
case CompressionType.Lz4HC:
case CompressionType.Zstd:
case CompressionType.Oodle:
var compressedSize = (int)blockInfo.compressedSize;
var uncompressedSize = (int)blockInfo.uncompressedSize;
sharedCompressedBuff.AsSpan().Clear();
sharedUncompressedBuff.AsSpan().Clear();
_ = reader.Read(sharedCompressedBuff, 0, compressedSize);
var compressedSpan = new ReadOnlySpan<byte>(sharedCompressedBuff, 0, compressedSize);
var uncompressedSpan = new Span<byte>(sharedUncompressedBuff, 0, uncompressedSize);
numWrite = BundleDecompressionHelper.DecompressBlock(compressionType, compressedSpan, uncompressedSpan, ref errorMsg);
if (numWrite == uncompressedSize)
{
blocksStream.Write(sharedUncompressedBuff, 0, uncompressedSize);
continue;
}
break;
case CompressionType.Lzham:
throw new IOException($"Unsupported block compression type: {compressionType}.\n");
default:
throw new IOException($"Unknown block compression type: {compressionType}.\nYou may try to specify the compression type manually.\n");
}
if (numWrite != blockInfo.uncompressedSize)
{
var msg = $"{compressionType} block decompression error. {errorMsg}\nWrite {numWrite} bytes but expected {blockInfo.uncompressedSize} bytes.";
var exMsg = compressionType > CompressionType.Lz4HC || customBlockCompression != CompressionType.Auto
? "Wrong compression type or block data might be encrypted."
: "Block data might be encrypted.";
throw new IOException($"{msg}\n{exMsg}\n");
}
}
}
finally
{
if (sharedCompressedBuff != null)
BigArrayPool<byte>.Shared.Return(sharedCompressedBuff, clearArray: true);
if (sharedUncompressedBuff != null)
BigArrayPool<byte>.Shared.Return(sharedUncompressedBuff, clearArray: true);
}
return blocksStream;
}
private void UnityCnCheck(FileReader reader, UnityVersion unityVer)
{
if ((m_Header.flags & ArchiveFlags.BlocksInfoAtTheEnd) != 0)
return;
var hasUnityCnFlag = false;
if (!unityVer.IsStripped)
{
// https://issuetracker.unity3d.com/issues/files-within-assetbundles-do-not-start-on-aligned-boundaries-breaking-patching-on-nintendo-switch
if (unityVer < 2020
|| unityVer.IsInRange(2020, (2020, 3, 34))
|| unityVer.IsInRange(2021, (2021, 3, 2))
|| unityVer.IsInRange(2022, (2022, 1, 1)))
{
hasUnityCnFlag = ((CnEncryptionFlags)m_Header.flags & CnEncryptionFlags.V1) != 0;
}
else
{
hasUnityCnFlag = ((CnEncryptionFlags)m_Header.flags & CnEncryptionFlags.V2_V3) != 0;
}
}
if (!hasUnityCnFlag)
return;
var pos = reader.Position;
reader.Position += 70;
try
{
ReadBlocksInfoAndDirectory(reader, unityVer, silent: true);
}
catch (Exception)
{
reader.Position = pos;
return;
}
throw new NotSupportedException("Unsupported bundle file. UnityCN encryption was detected.");
}
private bool IsUncompressedBundle => m_BlocksInfo.All(x => (CompressionType)(x.flags & StorageBlockFlags.CompressionTypeMask) == CompressionType.None);
}
}

View File

@@ -297,6 +297,7 @@ namespace AssetStudio
//void = 100011,
TilemapCollider2D = 19719996,
AssetImporterLog = 41386430,
GraphicsStateCollection = 55640938,
VFXRenderer = 73398921,
SerializableManagedRefTestClass = 76251197,
Grid = 156049354,
@@ -309,9 +310,13 @@ namespace AssetStudio
AssemblyDefinitionReferenceImporter = 294290339,
SiblingDerived = 334799969,
TestObjectWithSerializedMapStringNonAlignedStruct = 342846651,
AudioResource = 355983997,
SubDerived = 367388927,
AssetImportInProgressProxy = 369655926,
PluginBuildInfo = 382020655,
MemorySettings = 387306366,
BuildMetaDataImporter = 403037116,
BuildInstructionImporter = 403037117,
EditorProjectAccess = 426301858,
PrefabImporter = 468431735,
TestObjectWithSerializedArray = 478637458,
@@ -323,16 +328,22 @@ namespace AssetStudio
AudioBuildInfo = 641289076,
CachedSpriteAtlasRuntimeData = 644342135,
RendererFake = 646504946,
MultiplayerManager = 655991488,
AssemblyDefinitionReferenceAsset = 662584278,
BuiltAssetBundleInfoSet = 668709126,
SpriteAtlas = 687078895,
RayTracingShaderImporter = 747330370,
BuildArchiveImporter = 780535461,
PreviewImporter = 815301076,
RayTracingShader = 825902497,
LightingSettings = 850595691,
PlatformModuleSetup = 877146078,
VersionControlSettings = 890905787,
CustomCollider2D = 893571522,
AimConstraint = 895512359,
VFXManager = 937362698,
RoslynAnalyzerConfigAsset = 947337230,
RuleSetFileAsset = 954905827,
VisualEffectSubgraph = 994735392,
VisualEffectSubgraphOperator = 994735403,
VisualEffectSubgraphBlock = 994735404,
@@ -345,25 +356,40 @@ namespace AssetStudio
LookAtConstraint = 1183024399,
SpriteAtlasImporter = 1210832254,
MultiArtifactTestImporter = 1223240404,
AudioContainerElement = 1233149941,
GameObjectRecorder = 1268269756,
AudioRandomContainer = 1307931743,
LightingDataAssetParent = 1325145578,
PresetManager = 1386491679,
TestObjectWithSpecialLayoutTwo = 1392443030,
StreamingManager = 1403656975,
LowerResBlitTexture = 1480428607,
VideoBuildInfo = 1521398425,
C4DImporter = 1541671625,
StreamingController = 1542919678,
RenderPassAttachment = 1571458007,
TestObjectVectorPairStringBool = 1628831178,
ShaderContainer = 1557264870,
RoslynAdditionalFileAsset = 1597193336,
RoslynAdditionalFileImporter = 1642787288,
MultiplayerRolesData = 1652712579,
SceneRoots = 1660057539,
BrokenPrefabAsset = 1731078267,
AndroidAssetPackImporter = 1736697216,
VulkanDeviceFilterLists = 1740304944,
GridLayout = 1742807556,
AssemblyDefinitionImporter = 1766753193,
ParentConstraint = 1773428102,
FakeComponent = 1803986026,
RuleSetFileImporter = 1777034230,
PositionConstraint = 1818360608,
RotationConstraint = 1818360609,
ScaleConstraint = 1818360610,
Tilemap = 1839735485,
PackageManifest = 1896753125,
PackageManifestImporter = 1896753126,
RoslynAnalyzerConfigImporter = 1903396204,
UIRenderer = 1931382933,
TerrainLayer = 1953259897,
SpriteShapeRenderer = 1971053207,
NativeObjectType = 1977754360,
@@ -375,6 +401,7 @@ namespace AssetStudio
VisualEffectObject = 2059678085,
VisualEffect = 2083052967,
LocalizationAsset = 2083778819,
ScriptedImporter = 2089858483
ScriptedImporter = 2089858483,
ShaderIncludeImporter = 2103361453,
}
}

View File

@@ -1,22 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
namespace AssetStudio
{
public sealed class Animation : Behaviour
{
public PPtr<AnimationClip>[] m_Animations;
public List<PPtr<AnimationClip>> m_Animations;
public Animation(ObjectReader reader) : base(reader)
{
var m_Animation = new PPtr<AnimationClip>(reader);
int numAnimations = reader.ReadInt32();
m_Animations = new PPtr<AnimationClip>[numAnimations];
for (int i = 0; i < numAnimations; i++)
m_Animations = new List<PPtr<AnimationClip>>();
for (var i = 0; i < numAnimations; i++)
{
m_Animations[i] = new PPtr<AnimationClip>(reader);
m_Animations.Add(new PPtr<AnimationClip>(reader));
}
}
}

View File

@@ -1,9 +1,8 @@
using Newtonsoft.Json;
using System;
using System.Collections;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
namespace AssetStudio
{
@@ -36,7 +35,7 @@ namespace AssetStudio
public class AnimationCurve<T>
{
public Keyframe<T>[] m_Curve;
public List<Keyframe<T>> m_Curve;
public int m_PreInfinity;
public int m_PostInfinity;
public int m_RotationOrder;
@@ -45,17 +44,16 @@ namespace AssetStudio
public AnimationCurve(ObjectReader reader, Func<T> readerFunc)
{
var version = reader.version;
int numCurves = reader.ReadInt32();
m_Curve = new Keyframe<T>[numCurves];
for (int i = 0; i < numCurves; i++)
m_Curve = new List<Keyframe<T>>();
for (var i = 0; i < numCurves; i++)
{
m_Curve[i] = new Keyframe<T>(reader, readerFunc);
m_Curve.Add(new Keyframe<T>(reader, readerFunc));
}
m_PreInfinity = reader.ReadInt32();
m_PostInfinity = reader.ReadInt32();
if (version >= (5, 3)) //5.3 and up
if (reader.version >= (5, 3)) //5.3 and up
{
m_RotationOrder = reader.ReadInt32();
}
@@ -113,7 +111,7 @@ namespace AssetStudio
var data = new List<float>();
for (var index = 0; index != end; index += chunkStride / 4)
{
for (int i = 0; i < itemCountInChunk; ++i)
for (var i = 0; i < itemCountInChunk; ++i)
{
uint x = 0;
@@ -134,7 +132,6 @@ namespace AssetStudio
data.Add(x / (scale * ((1 << m_BitSize) - 1)) + m_Start);
}
}
return data.ToArray();
}
}
@@ -161,16 +158,16 @@ namespace AssetStudio
public int[] UnpackInts()
{
var data = new int[m_NumItems];
var data = new List<int>();
int indexPos = 0;
int bitPos = 0;
for (int i = 0; i < m_NumItems; i++)
for (var i = 0; i < m_NumItems; i++)
{
int bits = 0;
data[i] = 0;
int elem = 0;
while (bits < m_BitSize)
{
data[i] |= (m_Data[indexPos] >> bitPos) << bits;
elem |= (m_Data[indexPos] >> bitPos) << bits;
int num = Math.Min(m_BitSize - bits, 8 - bitPos);
bitPos += num;
bits += num;
@@ -180,9 +177,10 @@ namespace AssetStudio
bitPos = 0;
}
}
data[i] &= (1 << m_BitSize) - 1;
elem &= (1 << m_BitSize) - 1;
data.Add(elem);
}
return data;
return data.ToArray();
}
}
@@ -205,11 +203,11 @@ namespace AssetStudio
public Quaternion[] UnpackQuats()
{
var data = new Quaternion[m_NumItems];
var data = new List<Quaternion>();
int indexPos = 0;
int bitPos = 0;
for (int i = 0; i < m_NumItems; i++)
for (var i = 0; i < m_NumItems; i++)
{
uint flags = 0;
@@ -228,10 +226,9 @@ namespace AssetStudio
}
flags &= 7;
var q = new Quaternion();
float sum = 0;
for (int j = 0; j < 4; j++)
for (var j = 0; j < 4; j++)
{
if ((flags & 3) != j)
{
@@ -256,15 +253,13 @@ namespace AssetStudio
sum += q[j] * q[j];
}
}
int lastComponent = (int)(flags & 3);
q[lastComponent] = (float)Math.Sqrt(1 - sum);
q[lastComponent] = MathF.Sqrt(1 - sum);
if ((flags & 4) != 0u)
q[lastComponent] = -q[lastComponent];
data[i] = q;
data.Add(q);
}
return data;
return data.ToArray();
}
}
@@ -317,13 +312,12 @@ namespace AssetStudio
public FloatCurve(ObjectReader reader)
{
var version = reader.version;
curve = new AnimationCurve<float>(reader, reader.ReadSingle);
attribute = reader.ReadAlignedString();
path = reader.ReadAlignedString();
classID = (ClassIDType)reader.ReadInt32();
script = new PPtr<MonoScript>(reader);
if (version >= (2022, 2)) //2022.2 and up
if (reader.version >= (2022, 2)) //2022.2 and up
{
flags = reader.ReadInt32();
}
@@ -346,7 +340,7 @@ namespace AssetStudio
public class PPtrCurve
{
public PPtrKeyframe[] curve;
public List<PPtrKeyframe> curve;
public string attribute;
public string path;
public int classID;
@@ -357,19 +351,18 @@ namespace AssetStudio
public PPtrCurve(ObjectReader reader)
{
var version = reader.version;
int numCurves = reader.ReadInt32();
curve = new PPtrKeyframe[numCurves];
for (int i = 0; i < numCurves; i++)
curve = new List<PPtrKeyframe>();
for (var i = 0; i < numCurves; i++)
{
curve[i] = new PPtrKeyframe(reader);
curve.Add(new PPtrKeyframe(reader));
}
attribute = reader.ReadAlignedString();
path = reader.ReadAlignedString();
classID = reader.ReadInt32();
script = new PPtr<MonoScript>(reader);
if (version >= (2022, 2)) //2022.2 and up
if (reader.version >= (2022, 2)) //2022.2 and up
{
flags = reader.ReadInt32();
}
@@ -474,11 +467,12 @@ namespace AssetStudio
m_LookAtWeight = reader.ReadVector4();
int numGoals = reader.ReadInt32();
m_GoalArray = new HumanGoal[numGoals];
for (int i = 0; i < numGoals; i++)
var goalList = new List<HumanGoal>();
for (var i = 0; i < numGoals; i++)
{
m_GoalArray[i] = new HumanGoal(reader);
goalList.Add(new HumanGoal(reader));
}
m_GoalArray = goalList.ToArray();
m_LeftHandPose = new HandPose(reader);
m_RightHandPose = new HandPose(reader);
@@ -488,11 +482,14 @@ namespace AssetStudio
if (version >= (5, 2))//5.2 and up
{
int numTDof = reader.ReadInt32();
m_TDoFArray = new Vector3[numTDof];
for (int i = 0; i < numTDof; i++)
var tDoFList = new List<Vector3>();
for (var i = 0; i < numTDof; i++)
{
m_TDoFArray[i] = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
tDoFList.Add(version >= (5, 4) //5.4 and up
? reader.ReadVector3()
: (Vector3) reader.ReadVector4());
}
m_TDoFArray = tDoFList.ToArray();
}
}
}
@@ -506,8 +503,18 @@ namespace AssetStudio
public StreamedClip(ObjectReader reader)
{
var version = reader.version;
data = reader.ReadUInt32Array();
curveCount = reader.ReadUInt32();
if (version.IsInRange((2022, 3, 19), 2023) //2022.3.19f1 to 2023
|| version >= (2023, 2, 8)) //2023.2.8f1 and up
{
curveCount = reader.ReadUInt16();
var discreteCurveCount = reader.ReadUInt16();
}
else
{
curveCount = reader.ReadUInt32();
}
}
public class StreamedCurveKey
@@ -550,7 +557,7 @@ namespace AssetStudio
public class StreamedFrame
{
public float time;
public StreamedCurveKey[] keyList;
public List<StreamedCurveKey> keyList;
public StreamedFrame() { }
@@ -559,10 +566,10 @@ namespace AssetStudio
time = reader.ReadSingle();
int numKeys = reader.ReadInt32();
keyList = new StreamedCurveKey[numKeys];
for (int i = 0; i < numKeys; i++)
keyList = new List<StreamedCurveKey>();
for (var i = 0; i < numKeys; i++)
{
keyList[i] = new StreamedCurveKey(reader);
keyList.Add(new StreamedCurveKey(reader));
}
}
}
@@ -580,12 +587,12 @@ namespace AssetStudio
}
}
for (int frameIndex = 2; frameIndex < frameList.Count - 1; frameIndex++)
for (var frameIndex = 2; frameIndex < frameList.Count - 1; frameIndex++)
{
var frame = frameList[frameIndex];
foreach (var curveKey in frame.keyList)
{
for (int i = frameIndex - 1; i >= 0; i--)
for (var i = frameIndex - 1; i >= 0; i--)
{
var preFrame = frameList[i];
var preCurveKey = preFrame.keyList.FirstOrDefault(x => x.index == curveKey.index);
@@ -644,9 +651,8 @@ namespace AssetStudio
public ValueConstant(ObjectReader reader)
{
var version = reader.version;
m_ID = reader.ReadUInt32();
if (version < (5, 5)) //5.5 down
if (reader.version < (5, 5)) //5.5 down
{
m_TypeID = reader.ReadUInt32();
}
@@ -664,10 +670,50 @@ namespace AssetStudio
public ValueArrayConstant(ObjectReader reader)
{
int numVals = reader.ReadInt32();
m_ValueArray = new ValueConstant[numVals];
for (int i = 0; i < numVals; i++)
var valueList = new List<ValueConstant>();
for (var i = 0; i < numVals; i++)
{
m_ValueArray[i] = new ValueConstant(reader);
valueList.Add(new ValueConstant(reader));
}
m_ValueArray = valueList.ToArray();
}
}
public class ACLClip //Tuanjie
{
public uint m_FrameCount;
public uint m_BoneCount;
public float m_SampleRate;
public uint m_CurveCount;
public byte[] m_Tracks;
public uint[] m_ACLDecoderMap;
public bool m_UseACLFastSampleMode;
public ACLClip() { }
public ACLClip(ObjectReader reader)
{
var version = reader.version;
m_FrameCount = reader.ReadUInt32();
m_BoneCount = reader.ReadUInt32();
m_SampleRate = reader.ReadSingle();
if (version >= (2022, 3, 55)) //2022.3.55t1(1.5.0) and up
{
m_CurveCount = reader.ReadUInt32();
}
m_Tracks = reader.ReadUInt8Array();
if (version >= (2022, 3, 61)) //2022.3.61t1(1.6.0) and up
{
reader.AlignStream();
}
m_ACLDecoderMap = reader.ReadUInt32Array();
if (version > (2022, 3, 55) || (version == (2022, 3, 55) && version.Build >= 4)) //2022.3.55t4(1.5.3) and up
{
m_UseACLFastSampleMode = reader.ReadBoolean();
if (version >= (2022, 3, 61)) //2022.3.61t1(1.6.0) and up
{
reader.AlignStream();
}
}
}
}
@@ -690,6 +736,7 @@ namespace AssetStudio
public DenseClip m_DenseClip;
public ConstantClip m_ConstantClip;
public ValueArrayConstant m_Binding;
public ACLClip m_ACLClip;
public Clip() { }
@@ -706,6 +753,10 @@ namespace AssetStudio
{
m_Binding = new ValueArrayConstant(reader);
}
if (version.IsTuanjie && (version > (2022, 3, 48) || (version == (2022, 3, 48) && version.Build >= 3))) //2022.3.48t3(1.4.0) and up
{
m_ACLClip = new ACLClip(reader);
}
}
public AnimationClipBindingConstant ConvertValueArrayToGenericBinding()
@@ -713,7 +764,7 @@ namespace AssetStudio
var bindings = new AnimationClipBindingConstant();
var genericBindings = new List<GenericBinding>();
var values = m_Binding;
for (int i = 0; i < values.m_ValueArray.Length;)
for (var i = 0; i < values.m_ValueArray.Length;)
{
var curveID = values.m_ValueArray[i].m_ID;
var curveTypeID = values.m_ValueArray[i].m_TypeID;
@@ -748,7 +799,7 @@ namespace AssetStudio
i++;
}
}
bindings.genericBindings = genericBindings.ToArray();
bindings.genericBindings = genericBindings;
return bindings;
}
}
@@ -829,11 +880,13 @@ namespace AssetStudio
m_IndexArray = reader.ReadInt32Array();
if (version < (4, 3)) //4.3 down
{
var m_AdditionalCurveIndexArray = reader.ReadInt32Array();
var m_AdditionalCurveIndexArrayNum = reader.ReadInt32();
reader.Position += m_AdditionalCurveIndexArrayNum * 4; //skip int[] m_AdditionalCurveIndexArray
}
int numDeltas = reader.ReadInt32();
reader.ThrowIfTooLarge(numDeltas * 8f);
m_ValueArrayDelta = new ValueDelta[numDeltas];
for (int i = 0; i < numDeltas; i++)
for (var i = 0; i < numDeltas; i++)
{
m_ValueArrayDelta[i] = new ValueDelta(reader);
}
@@ -872,6 +925,7 @@ namespace AssetStudio
public byte customType;
public byte isPPtrCurve;
public byte isIntCurve;
public byte isSerializeReferenceCurve;
public GenericBinding() { }
@@ -895,31 +949,35 @@ namespace AssetStudio
{
isIntCurve = reader.ReadByte();
}
if (version >= (2022, 2)) //2022.2 and up
{
isSerializeReferenceCurve = reader.ReadByte();
}
reader.AlignStream();
}
}
public class AnimationClipBindingConstant
{
public GenericBinding[] genericBindings;
public PPtr<Object>[] pptrCurveMapping;
public List<GenericBinding> genericBindings;
public List<PPtr<Object>> pptrCurveMapping;
public AnimationClipBindingConstant() { }
public AnimationClipBindingConstant(ObjectReader reader)
{
int numBindings = reader.ReadInt32();
genericBindings = new GenericBinding[numBindings];
for (int i = 0; i < numBindings; i++)
genericBindings = new List<GenericBinding>();
for (var i = 0; i < numBindings; i++)
{
genericBindings[i] = new GenericBinding(reader);
genericBindings.Add(new GenericBinding(reader));
}
int numMappings = reader.ReadInt32();
pptrCurveMapping = new PPtr<Object>[numMappings];
for (int i = 0; i < numMappings; i++)
pptrCurveMapping = new List<PPtr<Object>>();
for (var i = 0; i < numMappings; i++)
{
pptrCurveMapping[i] = new PPtr<Object>(reader);
pptrCurveMapping.Add(new PPtr<Object>(reader));
}
}
@@ -974,15 +1032,17 @@ namespace AssetStudio
public AnimationEvent(ObjectReader reader)
{
var version = reader.version;
time = reader.ReadSingle();
functionName = reader.ReadAlignedString();
data = reader.ReadAlignedString();
objectReferenceParameter = new PPtr<Object>(reader);
floatParameter = reader.ReadSingle();
if (version >= 3) //3 and up
if (version >= (2, 6)) //2.6 and up
{
intParameter = reader.ReadInt32();
objectReferenceParameter = new PPtr<Object>(reader);
floatParameter = reader.ReadSingle();
if (version >= 3) //3 and up
{
intParameter = reader.ReadInt32();
}
}
messageOptions = reader.ReadInt32();
}
@@ -1001,28 +1061,41 @@ namespace AssetStudio
public bool m_Legacy;
public bool m_Compressed;
public bool m_UseHighQualityCurve;
public QuaternionCurve[] m_RotationCurves;
public CompressedAnimationCurve[] m_CompressedRotationCurves;
public Vector3Curve[] m_EulerCurves;
public Vector3Curve[] m_PositionCurves;
public Vector3Curve[] m_ScaleCurves;
public FloatCurve[] m_FloatCurves;
public PPtrCurve[] m_PPtrCurves;
public List<QuaternionCurve> m_RotationCurves;
public List<CompressedAnimationCurve> m_CompressedRotationCurves;
public List<Vector3Curve> m_EulerCurves;
public List<Vector3Curve> m_PositionCurves;
public List<Vector3Curve> m_ScaleCurves;
public List<FloatCurve> m_FloatCurves;
public List<PPtrCurve> m_PPtrCurves;
public float m_SampleRate;
public int m_WrapMode;
public AABB m_Bounds;
public uint m_MuscleClipSize;
public ClipMuscleConstant m_MuscleClip;
public AnimationClipBindingConstant m_ClipBindingConstant;
public AnimationEvent[] m_Events;
public List<AnimationEvent> m_Events;
public byte[] m_AnimData;
public StreamingInfo m_StreamingInfo;
public AnimationClip() { }
public AnimationClip(ObjectReader reader, IDictionary typeDict) : base(reader)
public AnimationClip(ObjectReader reader, byte[] type, JsonSerializerOptions jsonOptions, ObjectInfo objInfo) : base(reader)
{
var parsedAnimClip = JsonConvert.DeserializeObject<AnimationClip>(JsonConvert.SerializeObject(typeDict));
var parsedAnimClip = JsonSerializer.Deserialize<AnimationClip>(type, jsonOptions);
m_AnimationType = parsedAnimClip.m_AnimationType;
m_Legacy = parsedAnimClip.m_Legacy;
if (version >= 5)//5.0 and up
{
m_Legacy = parsedAnimClip.m_Legacy;
}
else if (version >= 4)//4.0 and up
{
m_Legacy = m_AnimationType == AnimationType.Legacy;
}
else
{
m_Legacy = true;
}
m_Compressed = parsedAnimClip.m_Compressed;
m_UseHighQualityCurve = parsedAnimClip.m_UseHighQualityCurve;
m_RotationCurves = parsedAnimClip.m_RotationCurves;
@@ -1039,8 +1112,31 @@ namespace AssetStudio
m_MuscleClip = parsedAnimClip.m_MuscleClip;
m_ClipBindingConstant = parsedAnimClip.m_ClipBindingConstant;
m_Events = parsedAnimClip.m_Events;
typeDict.Clear();
if (!reader.version.IsTuanjie)
return;
m_AnimData = parsedAnimClip.m_AnimData;
m_StreamingInfo = parsedAnimClip.m_StreamingInfo;
if (!(m_AnimData?.Length > 0))
return;
m_MuscleClipSize = (uint)m_AnimData.Length;
using (var muscleStream = new MemoryStream(m_AnimData))
{
using (var muscleReader = new EndianBinaryReader(muscleStream, EndianType.LittleEndian))
{
var objReader = new ObjectReader(muscleReader, assetsFile, objInfo);
if (!m_Legacy)
{
_ = objReader.ReadUInt32();
m_MuscleClip = new ClipMuscleConstant(objReader);
}
else
{
m_EulerCurves = Vector3CurveList(objReader);
m_PositionCurves = Vector3CurveList(objReader);
m_ScaleCurves = Vector3CurveList(objReader);
}
}
}
}
public AnimationClip(ObjectReader reader) : base(reader)
@@ -1052,84 +1148,107 @@ namespace AssetStudio
else if (version >= 4)//4.0 and up
{
m_AnimationType = (AnimationType)reader.ReadInt32();
if (m_AnimationType == AnimationType.Legacy)
m_Legacy = true;
m_Legacy = m_AnimationType == AnimationType.Legacy;
}
else
{
m_Legacy = true;
}
m_Compressed = reader.ReadBoolean();
if (version >= (2, 6)) //2.6 and up
{
m_Compressed = reader.ReadBoolean();
}
if (version >= (4, 3))//4.3 and up
{
m_UseHighQualityCurve = reader.ReadBoolean();
}
reader.AlignStream();
int numRCurves = reader.ReadInt32();
m_RotationCurves = new QuaternionCurve[numRCurves];
for (int i = 0; i < numRCurves; i++)
m_RotationCurves = new List<QuaternionCurve>();
for (var i = 0; i < numRCurves; i++)
{
m_RotationCurves[i] = new QuaternionCurve(reader);
m_RotationCurves.Add(new QuaternionCurve(reader));
}
int numCRCurves = reader.ReadInt32();
m_CompressedRotationCurves = new CompressedAnimationCurve[numCRCurves];
for (int i = 0; i < numCRCurves; i++)
if (version >= (2, 6)) //2.6 and up
{
m_CompressedRotationCurves[i] = new CompressedAnimationCurve(reader);
}
if (version >= (5, 3))//5.3 and up
{
int numEulerCurves = reader.ReadInt32();
m_EulerCurves = new Vector3Curve[numEulerCurves];
for (int i = 0; i < numEulerCurves; i++)
int numCRCurves = reader.ReadInt32();
m_CompressedRotationCurves = new List<CompressedAnimationCurve>();
for (var i = 0; i < numCRCurves; i++)
{
m_EulerCurves[i] = new Vector3Curve(reader);
m_CompressedRotationCurves.Add(new CompressedAnimationCurve(reader));
}
}
int numPCurves = reader.ReadInt32();
m_PositionCurves = new Vector3Curve[numPCurves];
for (int i = 0; i < numPCurves; i++)
if (!version.IsTuanjie)
{
m_PositionCurves[i] = new Vector3Curve(reader);
}
int numSCurves = reader.ReadInt32();
m_ScaleCurves = new Vector3Curve[numSCurves];
for (int i = 0; i < numSCurves; i++)
{
m_ScaleCurves[i] = new Vector3Curve(reader);
if (version >= (5, 3)) //5.3 and up
{
m_EulerCurves = Vector3CurveList(reader);
}
m_PositionCurves = Vector3CurveList(reader);
m_ScaleCurves = Vector3CurveList(reader);
}
int numFCurves = reader.ReadInt32();
m_FloatCurves = new FloatCurve[numFCurves];
for (int i = 0; i < numFCurves; i++)
m_FloatCurves = new List<FloatCurve>();
for (var i = 0; i < numFCurves; i++)
{
m_FloatCurves[i] = new FloatCurve(reader);
m_FloatCurves.Add(new FloatCurve(reader));
}
if (version >= (4, 3)) //4.3 and up
{
int numPtrCurves = reader.ReadInt32();
m_PPtrCurves = new PPtrCurve[numPtrCurves];
for (int i = 0; i < numPtrCurves; i++)
m_PPtrCurves = new List<PPtrCurve>();
for (var i = 0; i < numPtrCurves; i++)
{
m_PPtrCurves[i] = new PPtrCurve(reader);
m_PPtrCurves.Add(new PPtrCurve(reader));
}
}
m_SampleRate = reader.ReadSingle();
m_WrapMode = reader.ReadInt32();
if (version >= (2, 6)) //2.6 and up
{
m_WrapMode = reader.ReadInt32();
}
if (version >= (3, 4)) //3.4 and up
{
m_Bounds = new AABB(reader);
}
if (version >= 4)//4.0 and up
{
m_MuscleClipSize = reader.ReadUInt32();
m_MuscleClip = new ClipMuscleConstant(reader);
if (version.IsTuanjie && version >= (2022, 3, 61)) //2022.3.61t1(1.6.0) and up
{
m_EulerCurves = Vector3CurveList(reader);
m_PositionCurves = Vector3CurveList(reader);
m_ScaleCurves = Vector3CurveList(reader);
}
m_MuscleClipSize = reader.ReadUInt32(); //m_AnimDataSize (Tuanjie 1.0-1.5)
if (!version.IsTuanjie || version >= (2022, 3, 61))
{
m_MuscleClip = new ClipMuscleConstant(reader);
if (version.IsTuanjie) //2022.3.61t1(1.6.0) and up
{
m_StreamingInfo = new StreamingInfo(reader);
}
}
else if (m_MuscleClipSize > 0)
{
if (!m_Legacy)
{
_ = reader.ReadInt32();
m_MuscleClip = new ClipMuscleConstant(reader); //m_AnimData (Tuanjie 1.0-1.5)
m_StreamingInfo = new StreamingInfo(reader);
}
else
{
m_EulerCurves = Vector3CurveList(reader);
m_PositionCurves = Vector3CurveList(reader);
m_ScaleCurves = Vector3CurveList(reader);
}
}
}
if (version >= (4, 3)) //4.3 and up
{
@@ -1142,15 +1261,42 @@ namespace AssetStudio
reader.AlignStream();
}
int numEvents = reader.ReadInt32();
m_Events = new AnimationEvent[numEvents];
for (int i = 0; i < numEvents; i++)
m_Events = new List<AnimationEvent>();
for (var i = 0; i < numEvents; i++)
{
m_Events[i] = new AnimationEvent(reader);
m_Events.Add(new AnimationEvent(reader));
}
if (version >= 2017) //2017 and up
{
reader.AlignStream();
}
}
private static List<Vector3Curve> Vector3CurveList(ObjectReader reader)
{
var curveNum = reader.ReadInt32();
var vector3Curve = new List<Vector3Curve>();
for (var i = 0; i < curveNum; i++)
{
vector3Curve.Add(new Vector3Curve(reader));
}
return vector3Curve;
}
public class EqComparer : IEqualityComparer<AnimationClip>
{
public bool Equals(AnimationClip clipA, AnimationClip clipB)
{
return clipA?.m_PathID == clipB?.m_PathID
&& clipA?.byteSize == clipB?.byteSize;
}
public int GetHashCode(AnimationClip obj)
{
var result = obj.m_PathID * 31;
result = result * 31 + obj.byteSize;
return result.GetHashCode();
}
}
}
}

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class Animator : Behaviour
{
@@ -22,6 +17,12 @@ namespace AssetStudio
var m_UpdateMode = reader.ReadInt32();
}
if (version.IsTuanjie && (version > (2022, 3, 48) || version == (2022, 3, 48) && version.Build >= 7)) //2022.3.48t7(1.4.4) and up
{
var m_UpdateFrequencyMode = reader.ReadInt32();
var m_UpdateFrequency = reader.ReadSingle();
}
var m_ApplyRootMotion = reader.ReadBoolean();
if (version == 4 && version.Minor >= 5) //4.5 and up - 5.0 down
{
@@ -35,6 +36,10 @@ namespace AssetStudio
{
var m_StabilizeFeet = reader.ReadBoolean();
}
if (version >= (2023, 1)) //2023.1 and up
{
var m_AnimatePhysics = reader.ReadBoolean();
}
reader.AlignStream();
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
namespace AssetStudio
{
@@ -38,15 +35,15 @@ namespace AssetStudio
public class SkeletonMask
{
public SkeletonMaskElement[] m_Data;
public List<SkeletonMaskElement> m_Data;
public SkeletonMask(ObjectReader reader)
{
int numElements = reader.ReadInt32();
m_Data = new SkeletonMaskElement[numElements];
for (int i = 0; i < numElements; i++)
m_Data = new List<SkeletonMaskElement>();
for (var i = 0; i < numElements; i++)
{
m_Data[i] = new SkeletonMaskElement(reader);
m_Data.Add(new SkeletonMaskElement(reader));
}
}
}
@@ -124,11 +121,12 @@ namespace AssetStudio
var version = reader.version;
int numConditions = reader.ReadInt32();
m_ConditionConstantArray = new ConditionConstant[numConditions];
for (int i = 0; i < numConditions; i++)
var conditionConstantList = new List<ConditionConstant>();
for (var i = 0; i < numConditions; i++)
{
m_ConditionConstantArray[i] = new ConditionConstant(reader);
conditionConstantList.Add(new ConditionConstant(reader));
}
m_ConditionConstantArray = conditionConstantList.ToArray();
m_DestinationState = reader.ReadUInt32();
if (version >= 5) //5.0 and up
@@ -201,11 +199,12 @@ namespace AssetStudio
m_ChildPairAvgMagInvArray = reader.ReadSingleArray();
int numNeighbours = reader.ReadInt32();
m_ChildNeighborListArray = new MotionNeighborList[numNeighbours];
for (int i = 0; i < numNeighbours; i++)
var childNeighborLists = new List<MotionNeighborList>();
for (var i = 0; i < numNeighbours; i++)
{
m_ChildNeighborListArray[i] = new MotionNeighborList(reader);
childNeighborLists.Add(new MotionNeighborList(reader));
}
m_ChildNeighborListArray = childNeighborLists.ToArray();
}
}
@@ -305,11 +304,12 @@ namespace AssetStudio
var version = reader.version;
int numNodes = reader.ReadInt32();
m_NodeArray = new BlendTreeNodeConstant[numNodes];
for (int i = 0; i < numNodes; i++)
var nodeList = new List<BlendTreeNodeConstant>();
for (var i = 0; i < numNodes; i++)
{
m_NodeArray[i] = new BlendTreeNodeConstant(reader);
nodeList.Add(new BlendTreeNodeConstant(reader));
}
m_NodeArray = nodeList.ToArray();
if (version < (4, 5)) //4.5 down
{
@@ -343,31 +343,34 @@ namespace AssetStudio
{
var version = reader.version;
int numTransistions = reader.ReadInt32();
m_TransitionConstantArray = new TransitionConstant[numTransistions];
for (int i = 0; i < numTransistions; i++)
int numTransitions = reader.ReadInt32();
var transitionConstantList = new List<TransitionConstant>();
for (var i = 0; i < numTransitions; i++)
{
m_TransitionConstantArray[i] = new TransitionConstant(reader);
transitionConstantList.Add(new TransitionConstant(reader));
}
m_TransitionConstantArray = transitionConstantList.ToArray();
m_BlendTreeConstantIndexArray = reader.ReadInt32Array();
if (version < (5, 2)) //5.2 down
{
int numInfos = reader.ReadInt32();
m_LeafInfoArray = new LeafInfoConstant[numInfos];
for (int i = 0; i < numInfos; i++)
var leafInfoList = new List<LeafInfoConstant>();
for (var i = 0; i < numInfos; i++)
{
m_LeafInfoArray[i] = new LeafInfoConstant(reader);
leafInfoList.Add(new LeafInfoConstant(reader));
}
m_LeafInfoArray = leafInfoList.ToArray();
}
int numBlends = reader.ReadInt32();
m_BlendTreeConstantArray = new BlendTreeConstant[numBlends];
for (int i = 0; i < numBlends; i++)
var blendTreeConstantList = new List<BlendTreeConstant>();
for (var i = 0; i < numBlends; i++)
{
m_BlendTreeConstantArray[i] = new BlendTreeConstant(reader);
blendTreeConstantList.Add(new BlendTreeConstant(reader));
}
m_BlendTreeConstantArray = blendTreeConstantList.ToArray();
m_NameID = reader.ReadUInt32();
if (version >= (4, 3)) //4.3 and up
@@ -423,11 +426,12 @@ namespace AssetStudio
m_Destination = reader.ReadUInt32();
int numConditions = reader.ReadInt32();
m_ConditionConstantArray = new ConditionConstant[numConditions];
for (int i = 0; i < numConditions; i++)
var conditionConstantList = new List<ConditionConstant>();
for (var i = 0; i < numConditions; i++)
{
m_ConditionConstantArray[i] = new ConditionConstant(reader);
conditionConstantList.Add(new ConditionConstant(reader));
}
m_ConditionConstantArray = conditionConstantList.ToArray();
}
}
@@ -440,11 +444,12 @@ namespace AssetStudio
public SelectorStateConstant(ObjectReader reader)
{
int numTransitions = reader.ReadInt32();
m_TransitionConstantArray = new SelectorTransitionConstant[numTransitions];
for (int i = 0; i < numTransitions; i++)
var transitionConstantList = new List<SelectorTransitionConstant>();
for (var i = 0; i < numTransitions; i++)
{
m_TransitionConstantArray[i] = new SelectorTransitionConstant(reader);
transitionConstantList.Add(new SelectorTransitionConstant(reader));
}
m_TransitionConstantArray = transitionConstantList.ToArray();
m_FullPathID = reader.ReadUInt32();
m_isEntry = reader.ReadBoolean();
@@ -465,27 +470,30 @@ namespace AssetStudio
var version = reader.version;
int numStates = reader.ReadInt32();
m_StateConstantArray = new StateConstant[numStates];
for (int i = 0; i < numStates; i++)
var stateConstantList = new List<StateConstant>();
for (var i = 0; i < numStates; i++)
{
m_StateConstantArray[i] = new StateConstant(reader);
stateConstantList.Add(new StateConstant(reader));
}
m_StateConstantArray = stateConstantList.ToArray();
int numAnyStates = reader.ReadInt32();
m_AnyStateTransitionConstantArray = new TransitionConstant[numAnyStates];
for (int i = 0; i < numAnyStates; i++)
var anyStateTransitionConstantList = new List<TransitionConstant>();
for (var i = 0; i < numAnyStates; i++)
{
m_AnyStateTransitionConstantArray[i] = new TransitionConstant(reader);
anyStateTransitionConstantList.Add(new TransitionConstant(reader));
}
m_AnyStateTransitionConstantArray = anyStateTransitionConstantList.ToArray();
if (version >= 5) //5.0 and up
{
int numSelectors = reader.ReadInt32();
m_SelectorStateConstantArray = new SelectorStateConstant[numSelectors];
for (int i = 0; i < numSelectors; i++)
var selectorStateConstantList = new List<SelectorStateConstant>();
for (var i = 0; i < numSelectors; i++)
{
m_SelectorStateConstantArray[i] = new SelectorStateConstant(reader);
selectorStateConstantList.Add(new SelectorStateConstant(reader));
}
m_SelectorStateConstantArray = selectorStateConstantList.ToArray();
}
m_DefaultState = reader.ReadUInt32();
@@ -522,20 +530,26 @@ namespace AssetStudio
else
{
int numPosValues = reader.ReadInt32();
m_PositionValues = new Vector3[numPosValues];
for (int i = 0; i < numPosValues; i++)
var positionValuesList = new List<Vector3>();
for (var i = 0; i < numPosValues; i++)
{
m_PositionValues[i] = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4(); //5.4 and up
positionValuesList.Add(version >= (5, 4) //5.4 and up
? reader.ReadVector3()
: (Vector3)reader.ReadVector4());
}
m_PositionValues = positionValuesList.ToArray();
m_QuaternionValues = reader.ReadVector4Array();
int numScaleValues = reader.ReadInt32();
m_ScaleValues = new Vector3[numScaleValues];
for (int i = 0; i < numScaleValues; i++)
var scaleValuesList = new List<Vector3>();
for (var i = 0; i < numScaleValues; i++)
{
m_ScaleValues[i] = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4(); //5.4 and up
scaleValuesList.Add(version >= (5, 4) //5.4 and up
? reader.ReadVector3()
: (Vector3)reader.ReadVector4());
}
m_ScaleValues = scaleValuesList.ToArray();
if (version >= (5, 5)) //5.5 and up
{
@@ -558,18 +572,20 @@ namespace AssetStudio
public ControllerConstant(ObjectReader reader)
{
int numLayers = reader.ReadInt32();
m_LayerArray = new LayerConstant[numLayers];
for (int i = 0; i < numLayers; i++)
var layerList = new List<LayerConstant>();
for (var i = 0; i < numLayers; i++)
{
m_LayerArray[i] = new LayerConstant(reader);
layerList.Add(new LayerConstant(reader));
}
m_LayerArray = layerList.ToArray();
int numStates = reader.ReadInt32();
m_StateMachineArray = new StateMachineConstant[numStates];
for (int i = 0; i < numStates; i++)
var stateMachineList = new List<StateMachineConstant>();
for (var i = 0; i < numStates; i++)
{
m_StateMachineArray[i] = new StateMachineConstant(reader);
stateMachineList.Add(new StateMachineConstant(reader));
}
m_StateMachineArray = stateMachineList.ToArray();
m_Values = new ValueArrayConstant(reader);
m_DefaultValues = new ValueArray(reader);
@@ -578,7 +594,8 @@ namespace AssetStudio
public sealed class AnimatorController : RuntimeAnimatorController
{
public PPtr<AnimationClip>[] m_AnimationClips;
public List<PPtr<AnimationClip>> m_AnimationClips;
public List<KeyValuePair<uint, string>> m_TOS;
public AnimatorController(ObjectReader reader) : base(reader)
{
@@ -586,17 +603,17 @@ namespace AssetStudio
var m_Controller = new ControllerConstant(reader);
int tosSize = reader.ReadInt32();
var m_TOS = new KeyValuePair<uint, string>[tosSize];
for (int i = 0; i < tosSize; i++)
m_TOS = new List<KeyValuePair<uint, string>>();
for (var i = 0; i < tosSize; i++)
{
m_TOS[i] = new KeyValuePair<uint, string>(reader.ReadUInt32(), reader.ReadAlignedString());
m_TOS.Add(new KeyValuePair<uint, string>(reader.ReadUInt32(), reader.ReadAlignedString()));
}
int numClips = reader.ReadInt32();
m_AnimationClips = new PPtr<AnimationClip>[numClips];
for (int i = 0; i < numClips; i++)
m_AnimationClips = new List<PPtr<AnimationClip>>();
for (var i = 0; i < numClips; i++)
{
m_AnimationClips[i] = new PPtr<AnimationClip>(reader);
m_AnimationClips.Add(new PPtr<AnimationClip>(reader));
}
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
namespace AssetStudio
{
@@ -20,17 +17,17 @@ namespace AssetStudio
public sealed class AnimatorOverrideController : RuntimeAnimatorController
{
public PPtr<RuntimeAnimatorController> m_Controller;
public AnimationClipOverride[] m_Clips;
public List<AnimationClipOverride> m_Clips;
public AnimatorOverrideController(ObjectReader reader) : base(reader)
{
m_Controller = new PPtr<RuntimeAnimatorController>(reader);
int numOverrides = reader.ReadInt32();
m_Clips = new AnimationClipOverride[numOverrides];
for (int i = 0; i < numOverrides; i++)
m_Clips = new List<AnimationClipOverride>();
for (var i = 0; i < numOverrides; i++)
{
m_Clips[i] = new AnimationClipOverride(reader);
m_Clips.Add(new AnimationClipOverride(reader));
}
}
}

View File

@@ -18,8 +18,8 @@ namespace AssetStudio
public sealed class AssetBundle : NamedObject
{
public PPtr<Object>[] m_PreloadTable;
public KeyValuePair<string, AssetInfo>[] m_Container;
public List<PPtr<Object>> m_PreloadTable;
public List<KeyValuePair<string, AssetInfo>> m_Container;
public string m_AssetBundleName;
public string[] m_Dependencies;
public bool m_IsStreamedSceneAssetBundle;
@@ -27,17 +27,17 @@ namespace AssetStudio
public AssetBundle(ObjectReader reader) : base(reader)
{
var m_PreloadTableSize = reader.ReadInt32();
m_PreloadTable = new PPtr<Object>[m_PreloadTableSize];
m_PreloadTable = new List<PPtr<Object>>();
for (var i = 0; i < m_PreloadTableSize; i++)
{
m_PreloadTable[i] = new PPtr<Object>(reader);
m_PreloadTable.Add(new PPtr<Object>(reader));
}
var m_ContainerSize = reader.ReadInt32();
m_Container = new KeyValuePair<string, AssetInfo>[m_ContainerSize];
m_Container = new List<KeyValuePair<string, AssetInfo>>();
for (var i = 0; i < m_ContainerSize; i++)
{
m_Container[i] = new KeyValuePair<string, AssetInfo>(reader.ReadAlignedString(), new AssetInfo(reader));
m_Container.Add(new KeyValuePair<string, AssetInfo>(reader.ReadAlignedString(), new AssetInfo(reader)));
}
var m_MainAsset = new AssetInfo(reader);
@@ -61,13 +61,7 @@ namespace AssetStudio
{
m_AssetBundleName = reader.ReadAlignedString();
var m_DependenciesSize = reader.ReadInt32();
m_Dependencies = new string[m_DependenciesSize];
for (var i = 0; i < m_DependenciesSize; i++)
{
m_Dependencies[i] = reader.ReadAlignedString();
}
m_Dependencies = reader.ReadStringArray();
m_IsStreamedSceneAssetBundle = reader.ReadBoolean();
}

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class AudioClip : NamedObject
{
@@ -36,11 +30,19 @@ namespace AssetStudio
if (version < 5)
{
m_Format = reader.ReadInt32();
m_Type = (FMODSoundType)reader.ReadInt32();
m_3D = reader.ReadBoolean();
m_UseHardware = reader.ReadBoolean();
reader.AlignStream();
if (version >= (2, 6)) //2.6 to 5
{
m_Type = (FMODSoundType)reader.ReadInt32();
m_3D = reader.ReadBoolean();
m_UseHardware = reader.ReadBoolean();
reader.AlignStream();
}
else
{
m_Length = reader.ReadSingle();
m_Frequency = reader.ReadInt32();
m_Channels = m_Format != 0x05 ? m_Format >> 1 : 0;
}
if (version >= (3, 2)) //3.2.0 to 5
{
int m_Stream = reader.ReadInt32();

View File

@@ -66,7 +66,7 @@ namespace AssetStudio
public class Skeleton
{
public Node[] m_Node;
public List<Node> m_Node;
public uint[] m_ID;
public Axes[] m_AxesArray;
@@ -74,34 +74,35 @@ namespace AssetStudio
public Skeleton(ObjectReader reader)
{
int numNodes = reader.ReadInt32();
m_Node = new Node[numNodes];
for (int i = 0; i < numNodes; i++)
m_Node = new List<Node>();
for (var i = 0; i < numNodes; i++)
{
m_Node[i] = new Node(reader);
m_Node.Add(new Node(reader));
}
m_ID = reader.ReadUInt32Array();
int numAxes = reader.ReadInt32();
m_AxesArray = new Axes[numAxes];
for (int i = 0; i < numAxes; i++)
var axesList = new List<Axes>();
for (var i = 0; i < numAxes; i++)
{
m_AxesArray[i] = new Axes(reader);
axesList.Add(new Axes(reader));
}
m_AxesArray = axesList.ToArray();
}
}
public class SkeletonPose
{
public xform[] m_X;
public List<xform> m_X;
public SkeletonPose(ObjectReader reader)
{
int numXforms = reader.ReadInt32();
m_X = new xform[numXforms];
for (int i = 0; i < numXforms; i++)
m_X = new List<xform>();
for (var i = 0; i < numXforms; i++)
{
m_X[i] = new xform(reader);
m_X.Add(new xform(reader));
}
}
}
@@ -163,7 +164,7 @@ namespace AssetStudio
public SkeletonPose m_SkeletonPose;
public Hand m_LeftHand;
public Hand m_RightHand;
public Handle[] m_Handles;
public List<Handle> m_Handles;
public Collider[] m_ColliderArray;
public int[] m_HumanBoneIndex;
public float[] m_HumanBoneMass;
@@ -192,18 +193,19 @@ namespace AssetStudio
if (version < (2018, 2)) //2018.2 down
{
int numHandles = reader.ReadInt32();
m_Handles = new Handle[numHandles];
for (int i = 0; i < numHandles; i++)
m_Handles = new List<Handle>();
for (var i = 0; i < numHandles; i++)
{
m_Handles[i] = new Handle(reader);
m_Handles.Add(new Handle(reader));
}
int numColliders = reader.ReadInt32();
m_ColliderArray = new Collider[numColliders];
for (int i = 0; i < numColliders; i++)
var colliderList = new List<Collider>();
for (var i = 0; i < numColliders; i++)
{
m_ColliderArray[i] = new Collider(reader);
colliderList.Add(new Collider(reader));
}
m_ColliderArray = colliderList.ToArray();
}
m_HumanBoneIndex = reader.ReadInt32Array();
@@ -287,7 +289,7 @@ namespace AssetStudio
{
public uint m_AvatarSize;
public AvatarConstant m_Avatar;
public KeyValuePair<uint, string>[] m_TOS;
public List<KeyValuePair<uint, string>> m_TOS;
public Avatar(ObjectReader reader) : base(reader)
{
@@ -295,10 +297,10 @@ namespace AssetStudio
m_Avatar = new AvatarConstant(reader);
int numTOS = reader.ReadInt32();
m_TOS = new KeyValuePair<uint, string>[numTOS];
for (int i = 0; i < numTOS; i++)
m_TOS = new List<KeyValuePair<uint, string>>();
for (var i = 0; i < numTOS; i++)
{
m_TOS[i] = new KeyValuePair<uint, string>(reader.ReadUInt32(), reader.ReadAlignedString());
m_TOS.Add(new KeyValuePair<uint, string>(reader.ReadUInt32(), reader.ReadAlignedString()));
}
//HumanDescription m_HumanDescription 2019 and up

View File

@@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public abstract class Behaviour : Component
{
public byte m_Enabled;
public Behaviour() { }
protected Behaviour(ObjectReader reader) : base(reader)
{
m_Enabled = reader.ReadByte();

View File

@@ -1,24 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class BuildSettings : Object
{
public string m_Version;
public string[] levels;
public string[] scenes;
public BuildSettings(ObjectReader reader) : base(reader)
{
var levels = reader.ReadStringArray();
var hasRenderTexture = reader.ReadBoolean();
var hasPROVersion = reader.ReadBoolean();
var hasPublishingRights = reader.ReadBoolean();
var hasShadows = reader.ReadBoolean();
m_Version = reader.ReadAlignedString();
if (reader.version < (5, 1)) //5.1 down
{
levels = reader.ReadStringArray();
}
else
{
scenes = reader.ReadStringArray();
}
}
}
}

View File

@@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public abstract class Component : EditorExtension
{
public PPtr<GameObject> m_GameObject;
public Component() { }
protected Component(ObjectReader reader) : base(reader)
{
m_GameObject = new PPtr<GameObject>(reader);

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public abstract class EditorExtension : Object
{

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class Font : NamedObject
{
@@ -23,12 +18,12 @@ namespace AssetStudio
var m_CharacterPadding = reader.ReadInt32();
var m_ConvertCase = reader.ReadInt32();
int m_CharacterRects_size = reader.ReadInt32();
for (int i = 0; i < m_CharacterRects_size; i++)
for (var i = 0; i < m_CharacterRects_size; i++)
{
reader.Position += 44;//CharacterInfo data 41
}
int m_KerningValues_size = reader.ReadInt32();
for (int i = 0; i < m_KerningValues_size; i++)
for (var i = 0; i < m_KerningValues_size; i++)
{
reader.Position += 8;
}
@@ -55,7 +50,7 @@ namespace AssetStudio
if (version <= 3)
{
int m_PerCharacterKerning_size = reader.ReadInt32();
for (int i = 0; i < m_PerCharacterKerning_size; i++)
for (var i = 0; i < m_PerCharacterKerning_size; i++)
{
int first = reader.ReadInt32();
float second = reader.ReadSingle();
@@ -71,7 +66,7 @@ namespace AssetStudio
var m_DefaultMaterial = new PPtr<Material>(reader);
int m_CharacterRects_size = reader.ReadInt32();
for (int i = 0; i < m_CharacterRects_size; i++)
for (var i = 0; i < m_CharacterRects_size; i++)
{
int index = reader.ReadInt32();
//Rectf uv
@@ -96,7 +91,7 @@ namespace AssetStudio
var m_Texture = new PPtr<Texture>(reader);
int m_KerningValues_size = reader.ReadInt32();
for (int i = 0; i < m_KerningValues_size; i++)
for (var i = 0; i < m_KerningValues_size; i++)
{
int pairfirst = reader.ReadInt16();
int pairsecond = reader.ReadInt16();

View File

@@ -1,4 +1,6 @@
namespace AssetStudio
using System.Text.Json.Serialization;
namespace AssetStudio
{
public class GLTextureSettings
{
@@ -6,6 +8,8 @@
public int m_Aniso;
public float m_MipBias;
public int m_WrapMode;
[JsonInclude]
private int m_WrapU { set => m_WrapMode = value; }
public GLTextureSettings() { }
@@ -19,8 +23,8 @@
if (version >= 2017)//2017.x and up
{
m_WrapMode = reader.ReadInt32(); //m_WrapU
int m_WrapV = reader.ReadInt32();
int m_WrapW = reader.ReadInt32();
var m_WrapV = reader.ReadInt32();
var m_WrapW = reader.ReadInt32();
}
else
{

View File

@@ -1,13 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace AssetStudio
{
public sealed class GameObject : EditorExtension
{
public PPtr<Component>[] m_Components;
public List<PPtr<Component>> m_Components;
public string m_Name;
public Transform m_Transform;
@@ -16,21 +14,28 @@ namespace AssetStudio
public SkinnedMeshRenderer m_SkinnedMeshRenderer;
public Animator m_Animator;
public Animation m_Animation;
[JsonIgnore]
public CubismModel CubismModel;
public GameObject(ObjectReader reader) : base(reader)
{
int m_Component_size = reader.ReadInt32();
m_Components = new PPtr<Component>[m_Component_size];
for (int i = 0; i < m_Component_size; i++)
var m_ComponentSize = reader.ReadInt32();
m_Components = new List<PPtr<Component>>();
for (var i = 0; i < m_ComponentSize; i++)
{
if (version < (5, 5)) //5.5 down
{
int first = reader.ReadInt32();
var first = reader.ReadInt32();
}
m_Components[i] = new PPtr<Component>(reader);
m_Components.Add(new PPtr<Component>(reader));
}
var m_Layer = reader.ReadInt32();
if (version.IsTuanjie && (version > (2022, 3, 2) || (version == (2022, 3, 2) && version.Build >= 11))) //2022.3.2t11(1.1.3) and up
{
var m_HasEditorInfo = reader.ReadBoolean();
reader.AlignStream();
}
m_Name = reader.ReadAlignedString();
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Text.Json;
namespace AssetStudio
{
@@ -8,6 +9,8 @@ namespace AssetStudio
public Vector2 m_Scale;
public Vector2 m_Offset;
public UnityTexEnv() { }
public UnityTexEnv(ObjectReader reader)
{
m_Texture = new PPtr<Texture>(reader);
@@ -18,44 +21,46 @@ namespace AssetStudio
public class UnityPropertySheet
{
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 List<KeyValuePair<string, UnityTexEnv>> m_TexEnvs;
public List<KeyValuePair<string, int>> m_Ints;
public List<KeyValuePair<string, float>> m_Floats;
public List<KeyValuePair<string, Color>> m_Colors;
public UnityPropertySheet() { }
public UnityPropertySheet(ObjectReader reader)
{
var version = reader.version;
int m_TexEnvsSize = reader.ReadInt32();
m_TexEnvs = new KeyValuePair<string, UnityTexEnv>[m_TexEnvsSize];
for (int i = 0; i < m_TexEnvsSize; i++)
m_TexEnvs = new List<KeyValuePair<string, UnityTexEnv>>();
for (var i = 0; i < m_TexEnvsSize; i++)
{
m_TexEnvs[i] = new KeyValuePair<string, UnityTexEnv>(reader.ReadAlignedString(), new UnityTexEnv(reader));
m_TexEnvs.Add(new KeyValuePair<string, UnityTexEnv>(reader.ReadAlignedString(), new UnityTexEnv(reader)));
}
if (version >= 2021) //2021.1 and up
{
int m_IntsSize = reader.ReadInt32();
m_Ints = new KeyValuePair<string, int>[m_IntsSize];
for (int i = 0; i < m_IntsSize; i++)
m_Ints = new List<KeyValuePair<string, int>>();
for (var i = 0; i < m_IntsSize; i++)
{
m_Ints[i] = new KeyValuePair<string, int>(reader.ReadAlignedString(), reader.ReadInt32());
m_Ints.Add(new KeyValuePair<string, int>(reader.ReadAlignedString(), reader.ReadInt32()));
}
}
int m_FloatsSize = reader.ReadInt32();
m_Floats = new KeyValuePair<string, float>[m_FloatsSize];
for (int i = 0; i < m_FloatsSize; i++)
m_Floats = new List<KeyValuePair<string, float>>();
for (var i = 0; i < m_FloatsSize; i++)
{
m_Floats[i] = new KeyValuePair<string, float>(reader.ReadAlignedString(), reader.ReadSingle());
m_Floats.Add(new KeyValuePair<string, float>(reader.ReadAlignedString(), reader.ReadSingle()));
}
int m_ColorsSize = reader.ReadInt32();
m_Colors = new KeyValuePair<string, Color>[m_ColorsSize];
for (int i = 0; i < m_ColorsSize; i++)
m_Colors = new List<KeyValuePair<string, Color>>();
for (var i = 0; i < m_ColorsSize; i++)
{
m_Colors[i] = new KeyValuePair<string, Color>(reader.ReadAlignedString(), reader.ReadColor4());
m_Colors.Add(new KeyValuePair<string, Color>(reader.ReadAlignedString(), reader.ReadColor4()));
}
}
}
@@ -65,21 +70,30 @@ namespace AssetStudio
public PPtr<Shader> m_Shader;
public UnityPropertySheet m_SavedProperties;
public Material() { }
public Material(ObjectReader reader, byte[] type, JsonSerializerOptions jsonOptions) : base(reader)
{
var parsedMaterial = JsonSerializer.Deserialize<Material>(type, jsonOptions);
m_Shader = parsedMaterial.m_Shader;
m_SavedProperties = parsedMaterial.m_SavedProperties;
}
public Material(ObjectReader reader) : base(reader)
{
m_Shader = new PPtr<Shader>(reader);
if (version == 4 && version.Minor >= 1) //4.x
if (version == 4 && version.Minor >= 1) //4.1 - 4.7.2
{
var m_ShaderKeywords = reader.ReadStringArray();
}
if (version >= (2021, 3)) //2021.3 and up
if (version >= (2021, 2, 18)) //2021.2.18 and up
{
var m_ValidKeywords = reader.ReadStringArray();
var m_InvalidKeywords = reader.ReadStringArray();
}
else if (version >= 5) //5.0 ~ 2021.2
else if (version >= 5) //5.0 - 2021.2.17
{
var m_ShaderKeywords = reader.ReadAlignedString();
}
@@ -104,7 +118,7 @@ namespace AssetStudio
if (version >= (5, 1)) //5.1 and up
{
var stringTagMapSize = reader.ReadInt32();
for (int i = 0; i < stringTagMapSize; i++)
for (var i = 0; i < stringTagMapSize; i++)
{
var first = reader.ReadAlignedString();
var second = reader.ReadAlignedString();

View File

@@ -1,4 +1,5 @@
using System;
using System.Buffers.Binary;
using System.Collections;
using System.Collections.Generic;
using System.IO;
@@ -123,8 +124,8 @@ namespace AssetStudio
{
public uint m_CurrentChannels;
public uint m_VertexCount;
public ChannelInfo[] m_Channels;
public StreamInfo[] m_Streams;
public List<ChannelInfo> m_Channels;
public List<StreamInfo> m_Streams;
public byte[] m_DataSize;
public VertexData(ObjectReader reader)
@@ -141,27 +142,23 @@ namespace AssetStudio
if (version >= 4) //4.0 and up
{
var m_ChannelsSize = reader.ReadInt32();
m_Channels = new ChannelInfo[m_ChannelsSize];
for (int i = 0; i < m_ChannelsSize; i++)
m_Channels = new List<ChannelInfo>();
for (var i = 0; i < m_ChannelsSize; i++)
{
m_Channels[i] = new ChannelInfo(reader);
m_Channels.Add(new ChannelInfo(reader));
}
}
if (version < 5) //5.0 down
{
if (version < 4)
{
m_Streams = new StreamInfo[4];
}
else
{
m_Streams = new StreamInfo[reader.ReadInt32()];
}
var streamCount = version < 4 //4.0 down
? 4
: reader.ReadInt32();
for (int i = 0; i < m_Streams.Length; i++)
m_Streams = new List<StreamInfo>();
for (var i = 0; i < streamCount; i++)
{
m_Streams[i] = new StreamInfo(reader);
m_Streams.Add(new StreamInfo(reader));
}
if (version < 4) //4.0 down
@@ -181,13 +178,13 @@ namespace AssetStudio
private void GetStreams(UnityVersion version)
{
var streamCount = m_Channels.Max(x => x.stream) + 1;
m_Streams = new StreamInfo[streamCount];
m_Streams = new List<StreamInfo>();
uint offset = 0;
for (int s = 0; s < streamCount; s++)
for (var s = 0; s < streamCount; s++)
{
uint chnMask = 0;
uint stride = 0;
for (int chn = 0; chn < m_Channels.Length; chn++)
for (var chn = 0; chn < m_Channels.Count; chn++)
{
var m_Channel = m_Channels[chn];
if (m_Channel.stream == s)
@@ -199,14 +196,14 @@ namespace AssetStudio
}
}
}
m_Streams[s] = new StreamInfo
m_Streams.Add(new StreamInfo
{
channelMask = chnMask,
offset = offset,
stride = stride,
dividerOp = 0,
frequency = 0
};
});
offset += m_VertexCount * stride;
//static size_t AlignStreamSize (size_t size) { return (size + (kVertexStreamAlign-1)) & ~(kVertexStreamAlign-1); }
offset = (offset + (16u - 1u)) & ~(16u - 1u);
@@ -215,17 +212,17 @@ namespace AssetStudio
private void GetChannels(UnityVersion version)
{
m_Channels = new ChannelInfo[6];
for (int i = 0; i < 6; i++)
m_Channels = new List<ChannelInfo>(6);
for (var i = 0; i < 6; i++)
{
m_Channels[i] = new ChannelInfo();
m_Channels.Add(new ChannelInfo());
}
for (var s = 0; s < m_Streams.Length; s++)
for (var s = 0; s < m_Streams.Count; s++)
{
var m_Stream = m_Streams[s];
var channelMask = new BitArray(new[] { (int)m_Stream.channelMask });
byte offset = 0;
for (int i = 0; i < 6; i++)
for (var i = 0; i < 6; i++)
{
if (channelMask.Get(i))
{
@@ -344,8 +341,8 @@ namespace AssetStudio
public class BlendShapeData
{
public BlendShapeVertex[] vertices;
public MeshBlendShape[] shapes;
public MeshBlendShapeChannel[] channels;
public List<MeshBlendShape> shapes;
public List<MeshBlendShapeChannel> channels;
public float[] fullWeights;
public BlendShapeData(ObjectReader reader)
@@ -355,24 +352,25 @@ namespace AssetStudio
if (version >= (4, 3)) //4.3 and up
{
int numVerts = reader.ReadInt32();
reader.ThrowIfTooLarge(numVerts * 40f);
vertices = new BlendShapeVertex[numVerts];
for (int i = 0; i < numVerts; i++)
for (var i = 0; i < numVerts; i++)
{
vertices[i] = new BlendShapeVertex(reader);
}
int numShapes = reader.ReadInt32();
shapes = new MeshBlendShape[numShapes];
for (int i = 0; i < numShapes; i++)
shapes = new List<MeshBlendShape>();
for (var i = 0; i < numShapes; i++)
{
shapes[i] = new MeshBlendShape(reader);
shapes.Add(new MeshBlendShape(reader));
}
int numChannels = reader.ReadInt32();
channels = new MeshBlendShapeChannel[numChannels];
for (int i = 0; i < numChannels; i++)
channels = new List<MeshBlendShapeChannel>();
for (var i = 0; i < numChannels; i++)
{
channels[i] = new MeshBlendShapeChannel(reader);
channels.Add(new MeshBlendShapeChannel(reader));
}
fullWeights = reader.ReadSingleArray();
@@ -380,17 +378,17 @@ namespace AssetStudio
else
{
var m_ShapesSize = reader.ReadInt32();
var m_Shapes = new MeshBlendShape[m_ShapesSize];
for (int i = 0; i < m_ShapesSize; i++)
var m_Shapes = new List<MeshBlendShape>();
for (var i = 0; i < m_ShapesSize; i++)
{
m_Shapes[i] = new MeshBlendShape(reader);
m_Shapes.Add(new MeshBlendShape(reader));
}
reader.AlignStream();
var m_ShapeVerticesSize = reader.ReadInt32();
var m_ShapeVertices = new BlendShapeVertex[m_ShapeVerticesSize]; //MeshBlendShapeVertex
for (int i = 0; i < m_ShapeVerticesSize; i++)
reader.ThrowIfTooLarge(m_ShapeVerticesSize * 40f);
for (var i = 0; i < m_ShapeVerticesSize; i++)
{
m_ShapeVertices[i] = new BlendShapeVertex(reader);
var m_ShapeVertices = new BlendShapeVertex(reader); //MeshBlendShapeVertex
}
}
}
@@ -444,10 +442,109 @@ namespace AssetStudio
}
}
public class VGPackedHierarchyNode
{
public Vector4[] LODBounds = new Vector4[8];
public Vector3[] AABBCenter = new Vector3[8];
public uint[] MinLODError_MaxParentLODError = new uint[8];
public Vector3[] AABBExtent = new Vector3[8];
public uint[] ChildStartIndex = new uint[8];
public uint[] PageIndex_PageCount_PageBlockCount = new uint[8];
public VGPackedHierarchyNode(BinaryReader reader)
{
for (var i = 0; i < 8; i++)
{
LODBounds[i] = reader.ReadVector4();
AABBCenter[i] = reader.ReadVector3();
MinLODError_MaxParentLODError[i] = reader.ReadUInt32();
AABBExtent[i] = reader.ReadVector3();
ChildStartIndex[i] = reader.ReadUInt32();
PageIndex_PageCount_PageBlockCount[i] = reader.ReadUInt32();
}
}
}
public class VGPageStreamingInfo
{
public uint offset;
public uint wholeSize;
public uint dataSize;
public uint dependencyOffset;
public uint dependencyCount;
public uint flags;
public VGPageStreamingInfo(BinaryReader reader)
{
offset = reader.ReadUInt32();
wholeSize = reader.ReadUInt32();
dataSize = reader.ReadUInt32();
dependencyOffset = reader.ReadUInt32();
dependencyCount = reader.ReadUInt32();
flags = reader.ReadUInt32();
}
}
public class SharedClusterData //Tuanjie
{
public SharedClusterData(ObjectReader reader, byte rev)
{
var m_LightmapUseUV1 = reader.ReadInt32();
var m_fileScale = reader.ReadSingle();
if (rev == 1)
{
var NumInputTriangles = reader.ReadUInt32();
var NumInputVertices = reader.ReadUInt32();
var NumInputMeshes = reader.ReadUInt16();
var NumInputTexCoords = reader.ReadUInt16();
var ResourceFlags = reader.ReadUInt32();
}
var rootClusterPageSize = reader.ReadInt32();
reader.Position += rootClusterPageSize; //skip byte[] rootClusterPage
if (rev == 1)
{
var imposterAtlasSize = reader.ReadInt32();
reader.Position += imposterAtlasSize * 2; //skip ushort[] imposterAtlas
}
var hierarchyNodesSize = reader.ReadInt32();
for (var i = 0; i < hierarchyNodesSize; i++)
{
var hierarchyNode = new VGPackedHierarchyNode(reader);
}
if (rev == 1)
{
var hierarchyRootOffsetsSize = reader.ReadInt32();
reader.Position += hierarchyRootOffsetsSize * 4; //skip uint[] hierarchyRootOffsets
}
var pageStreamingInfosSize = reader.ReadInt32();
for (var i = 0; i < pageStreamingInfosSize; i++)
{
var pageStreamingInfo = new VGPageStreamingInfo(reader);
}
var pageIndicesOfDependenciesSize = reader.ReadInt32();
reader.Position += pageIndicesOfDependenciesSize * 4; //skip uint[] pageIndicesOfDependencies
if (rev == 2)
{
var inputTrianglesCount = reader.ReadUInt32();
var inputVerticesCount = reader.ReadUInt32();
var inputMeshesCount = reader.ReadUInt16();
var inputTexCoordsCount = reader.ReadUInt16();
var resourceFlags = reader.ReadUInt32();
var imposterAtlasSize = reader.ReadInt32();
reader.Position += imposterAtlasSize * 2; //skip ushort[] imposterAtlas
var hierarchyRootOffsetsSize = reader.ReadInt32();
reader.Position += hierarchyRootOffsetsSize * 4; //skip uint[] hierarchyRootOffsets
}
var streamableClusterPageSize = reader.ReadInt32();
reader.Position += streamableClusterPageSize; //skip byte[] streamableClusterPageSize
}
}
public sealed class Mesh : NamedObject
{
private bool isLoaded;
private bool m_Use16BitIndices = true;
public SubMesh[] m_SubMeshes;
public List<SubMesh> m_SubMeshes;
private uint[] m_IndexBuffer;
public BlendShapeData m_Shapes;
public Matrix4x4[] m_BindPose;
@@ -466,6 +563,7 @@ namespace AssetStudio
public float[] m_UV6;
public float[] m_UV7;
public float[] m_Tangents;
private bool m_HasVirtualGeometryMesh;
private VertexData m_VertexData;
private CompressedMesh m_CompressedMesh;
private StreamingInfo m_StreamData;
@@ -481,28 +579,30 @@ namespace AssetStudio
if (version <= (2, 5)) //2.5 and down
{
int m_IndexBuffer_size = reader.ReadInt32();
int m_IndexBufferSize = reader.ReadInt32();
if (m_Use16BitIndices)
{
m_IndexBuffer = new uint[m_IndexBuffer_size / 2];
for (int i = 0; i < m_IndexBuffer_size / 2; i++)
var indexBufferList = new List<uint>();
for (var i = 0; i < m_IndexBufferSize / 2; i++)
{
m_IndexBuffer[i] = reader.ReadUInt16();
indexBufferList.Add(reader.ReadUInt16());
}
reader.AlignStream();
m_IndexBuffer = indexBufferList.ToArray();
}
else
{
m_IndexBuffer = reader.ReadUInt32Array(m_IndexBuffer_size / 4);
m_IndexBuffer = reader.ReadUInt32Array(m_IndexBufferSize / 4);
}
}
int m_SubMeshesSize = reader.ReadInt32();
m_SubMeshes = new SubMesh[m_SubMeshesSize];
for (int i = 0; i < m_SubMeshesSize; i++)
m_SubMeshes = new List<SubMesh>();
for (var i = 0; i < m_SubMeshesSize; i++)
{
m_SubMeshes[i] = new SubMesh(reader);
m_SubMeshes.Add(new SubMesh(reader));
}
if (version >= (4, 1)) //4.1 and up
@@ -522,13 +622,13 @@ namespace AssetStudio
if (version >= 2019) //2019 and up
{
var m_BonesAABBSize = reader.ReadInt32();
var m_BonesAABB = new MinMaxAABB[m_BonesAABBSize];
for (int i = 0; i < m_BonesAABBSize; i++)
for (var i = 0; i < m_BonesAABBSize; i++)
{
m_BonesAABB[i] = new MinMaxAABB(reader);
var m_BonesAABB = new MinMaxAABB(reader);
}
var m_VariableBoneCountWeights = reader.ReadUInt32Array();
var m_VariableBoneCountWeightsSize = reader.ReadInt32();
reader.Position += m_VariableBoneCountWeightsSize * 4; //skip uint[] m_VariableBoneCountWeights
}
var m_MeshCompression = reader.ReadByte();
@@ -542,30 +642,49 @@ namespace AssetStudio
var m_KeepVertices = reader.ReadBoolean();
var m_KeepIndices = reader.ReadBoolean();
}
if (version.IsTuanjie)
{
if (version < (2022, 3, 48) || (version == (2022, 3, 48) && version.Build < 3)) //2022.3.48t3(1.4.0) down
{
_ = new SharedClusterData(reader, rev: 1);
}
else if (version < (2022, 3, 61) || (version == (2022, 3, 61) && version.Build < 2)) //2022.3.48t3(1.4.0) - 2022.3.61t1(1.6.0)
{
_ = new SharedClusterData(reader, rev: 2);
}
else //2022.3.61t2(1.6.1) and up
{
reader.AlignStream();
_ = new SharedClusterData(reader, rev: 3);
}
}
reader.AlignStream();
//Unity fixed it in 2017.3.1p1 and later versions
if (version >= (2017, 4) //2017.4
|| version == (2017, 3, 1) && buildType.IsPatch //fixed after 2017.3.1px
|| version == (2017, 3) && m_MeshCompression == 0)//2017.3.xfx with no compression
|| version == (2017, 3, 1) && version.IsPatch //fixed after 2017.3.1px
|| version == (2017, 3) && m_MeshCompression == 0) //2017.3.xfx with no compression
{
var m_IndexFormat = reader.ReadInt32();
m_Use16BitIndices = m_IndexFormat == 0;
}
int m_IndexBuffer_size = reader.ReadInt32();
int m_IndexBufferSize = reader.ReadInt32();
if (m_Use16BitIndices)
{
m_IndexBuffer = new uint[m_IndexBuffer_size / 2];
for (int i = 0; i < m_IndexBuffer_size / 2; i++)
var indexBufferList = new List<uint>();
for (var i = 0; i < m_IndexBufferSize / 2; i++)
{
m_IndexBuffer[i] = reader.ReadUInt16();
indexBufferList.Add(reader.ReadUInt16());
}
reader.AlignStream();
m_IndexBuffer = indexBufferList.ToArray();
}
else
{
m_IndexBuffer = reader.ReadUInt32Array(m_IndexBuffer_size / 4);
m_IndexBuffer = reader.ReadUInt32Array(m_IndexBufferSize / 4);
}
}
@@ -574,8 +693,10 @@ namespace AssetStudio
m_VertexCount = reader.ReadInt32();
m_Vertices = reader.ReadSingleArray(m_VertexCount * 3); //Vector3
m_Skin = new BoneWeights4[reader.ReadInt32()];
for (int s = 0; s < m_Skin.Length; s++)
var numSkin = reader.ReadInt32();
reader.ThrowIfTooLarge(numSkin * 32f);
m_Skin = new BoneWeights4[numSkin];
for (var s = 0; s < numSkin; s++)
{
m_Skin[s] = new BoneWeights4(reader);
}
@@ -588,10 +709,11 @@ namespace AssetStudio
if (version <= (2, 5)) //2.5 and down
{
int m_TangentSpace_size = reader.ReadInt32();
m_Normals = new float[m_TangentSpace_size * 3];
m_Tangents = new float[m_TangentSpace_size * 4];
for (int v = 0; v < m_TangentSpace_size; v++)
int m_TangentSpaceSize = reader.ReadInt32();
reader.ThrowIfTooLarge(m_TangentSpaceSize * 28f);
m_Normals = new float[m_TangentSpaceSize * 3];
m_Tangents = new float[m_TangentSpaceSize * 4];
for (var v = 0; v < m_TangentSpaceSize; v++)
{
m_Normals[v * 3] = reader.ReadSingle();
m_Normals[v * 3 + 1] = reader.ReadSingle();
@@ -613,8 +735,10 @@ namespace AssetStudio
{
if (version < (2018, 2)) //2018.2 down
{
m_Skin = new BoneWeights4[reader.ReadInt32()];
for (int s = 0; s < m_Skin.Length; s++)
var numSkin = reader.ReadInt32();
reader.ThrowIfTooLarge(numSkin * 32f);
m_Skin = new BoneWeights4[numSkin];
for (var s = 0; s < numSkin; s++)
{
m_Skin[s] = new BoneWeights4(reader);
}
@@ -637,15 +761,16 @@ namespace AssetStudio
if (version <= (3, 4)) //3.4.2 and earlier
{
int m_Colors_size = reader.ReadInt32();
m_Colors = new float[m_Colors_size * 4];
for (int v = 0; v < m_Colors_size * 4; v++)
int m_ColorsSize = reader.ReadInt32();
var m_ColorsList = new List<float>();
for (var v = 0; v < m_ColorsSize * 4; v++)
{
m_Colors[v] = (float)reader.ReadByte() / 0xFF;
m_ColorsList.Add((float)reader.ReadByte() / 0xFF);
}
m_Colors = m_ColorsList.ToArray();
int m_CollisionTriangles_size = reader.ReadInt32();
reader.Position += m_CollisionTriangles_size * 4; //UInt32 indices
int m_CollisionTrianglesSize = reader.ReadInt32();
reader.Position += m_CollisionTrianglesSize * 4; //UInt32 indices
int m_CollisionVertexCount = reader.ReadInt32();
}
@@ -658,9 +783,12 @@ namespace AssetStudio
if (version >= 5) //5.0 and up
{
var m_BakedConvexCollisionMesh = reader.ReadUInt8Array();
var m_BakedConvexCollisionMeshSize = reader.ReadInt32();
reader.Position += m_BakedConvexCollisionMeshSize; //skip byte[] m_BakedConvexCollisionMesh
reader.AlignStream();
var m_BakedTriangleCollisionMesh = reader.ReadUInt8Array();
var m_BakedTriangleCollisionMeshSize = reader.ReadInt32();
reader.Position += m_BakedTriangleCollisionMeshSize; //skip byte[] m_BakedTriangleCollisionMesh
reader.AlignStream();
}
@@ -677,17 +805,30 @@ namespace AssetStudio
m_StreamData = new StreamingInfo(reader);
}
ProcessData();
if (version.IsTuanjie && (version > (2022, 3, 2) || (version == (2022, 3, 2) && version.Build >= 13))) //2022.3.2t13(1.2.0) and up
{
var m_GenerateGeometryBuffer = reader.ReadBoolean();
m_HasVirtualGeometryMesh = reader.ReadBoolean();
}
if (!assetsFile.assetsManager.MeshLazyLoad)
ProcessData();
}
private void ProcessData()
public void ProcessData()
{
if (isLoaded)
return;
var isStreamedDataSize = false;
if (!string.IsNullOrEmpty(m_StreamData?.path))
{
if (m_VertexData.m_VertexCount > 0)
{
m_VertexData.m_DataSize = BigArrayPool<byte>.Shared.Rent((int)m_StreamData.size);
var resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size);
m_VertexData.m_DataSize = resourceReader.GetData();
resourceReader.GetData(m_VertexData.m_DataSize);
isStreamedDataSize = true;
}
}
if (version >= (3, 5)) //3.5 and up
@@ -700,14 +841,28 @@ namespace AssetStudio
DecompressCompressedMesh();
}
GetTriangles();
if (m_IndexBuffer.Length == 0)
{
var msg = m_HasVirtualGeometryMesh
? "Unsupported mesh type: Virtual Geometry"
: "Cannot process empty mesh";
Logger.Warning($"{msg} | PathID: {m_PathID} | Name: \"{m_Name}\"");
}
else
{
GetTriangles();
}
isLoaded = true;
if (isStreamedDataSize)
BigArrayPool<byte>.Shared.Return(m_VertexData.m_DataSize, clearArray: true);
}
private void ReadVertexData()
{
m_VertexCount = (int)m_VertexData.m_VertexCount;
for (var chn = 0; chn < m_VertexData.m_Channels.Length; chn++)
for (var chn = 0; chn < m_VertexData.m_Channels.Count; chn++)
{
var m_Channel = m_VertexData.m_Channels[chn];
if (m_Channel.dimension > 0)
@@ -724,13 +879,14 @@ namespace AssetStudio
var vertexFormat = MeshHelper.ToVertexFormat(m_Channel.format, version);
var componentByteSize = (int)MeshHelper.GetFormatSize(vertexFormat);
var componentBytes = new byte[m_VertexCount * m_Channel.dimension * componentByteSize];
for (int v = 0; v < m_VertexCount; v++)
for (var v = 0; v < m_VertexCount; v++)
{
var vertexOffset = (int)m_Stream.offset + m_Channel.offset + (int)m_Stream.stride * v;
for (int d = 0; d < m_Channel.dimension; d++)
for (var d = 0; d < m_Channel.dimension; d++)
{
var componentOffset = vertexOffset + componentByteSize * d;
Buffer.BlockCopy(m_VertexData.m_DataSize, componentOffset, componentBytes, componentByteSize * (v * m_Channel.dimension + d), componentByteSize);
var dstOffset = componentByteSize * (v * m_Channel.dimension + d);
m_VertexData.m_DataSize.AsSpan(componentOffset, componentByteSize).CopyTo(componentBytes.AsSpan(dstOffset));
}
}
@@ -738,10 +894,7 @@ namespace AssetStudio
{
for (var i = 0; i < componentBytes.Length / componentByteSize; i++)
{
var buff = new byte[componentByteSize];
Buffer.BlockCopy(componentBytes, i * componentByteSize, buff, 0, componentByteSize);
buff = buff.Reverse().ToArray();
Buffer.BlockCopy(buff, 0, componentBytes, i * componentByteSize, componentByteSize);
componentBytes.AsSpan(i * componentByteSize, componentByteSize).Reverse();
}
}
@@ -798,9 +951,9 @@ namespace AssetStudio
{
InitMSkin();
}
for (int i = 0; i < m_VertexCount; i++)
for (var i = 0; i < m_VertexCount; i++)
{
for (int j = 0; j < m_Channel.dimension; j++)
for (var j = 0; j < m_Channel.dimension; j++)
{
m_Skin[i].weight[j] = componentsFloatArray[i * m_Channel.dimension + j];
}
@@ -810,15 +963,25 @@ namespace AssetStudio
if (m_Skin == null)
{
InitMSkin();
if (m_Channel.dimension == 1)
{
for (var i = 0; i < m_VertexCount; i++)
{
m_Skin[i].weight[0] = 1f;
}
}
}
for (int i = 0; i < m_VertexCount; i++)
for (var i = 0; i < m_VertexCount; i++)
{
for (int j = 0; j < m_Channel.dimension; j++)
for (var j = 0; j < m_Channel.dimension; j++)
{
m_Skin[i].boneIndex[j] = componentsIntArray[i * m_Channel.dimension + j];
}
}
break;
default:
Logger.Warning($"Unknown vertex attribute: {chn}");
break;
}
}
else
@@ -856,6 +1019,9 @@ namespace AssetStudio
case 7: //kShaderChannelTangent
m_Tangents = componentsFloatArray;
break;
default:
Logger.Warning($"Unknown vertex attribute: {chn}");
break;
}
}
}
@@ -883,7 +1049,7 @@ namespace AssetStudio
const int kMaxTexCoordShaderChannels = 8;
int uvSrcOffset = 0;
for (int uv = 0; uv < kMaxTexCoordShaderChannels; uv++)
for (var uv = 0; uv < kMaxTexCoordShaderChannels; uv++)
{
var texCoordBits = m_UVInfo >> (uv * kInfoBitsPerUV);
texCoordBits &= (1u << kInfoBitsPerUV) - 1u;
@@ -912,11 +1078,9 @@ namespace AssetStudio
{
m_BindPose = new Matrix4x4[m_CompressedMesh.m_BindPoses.m_NumItems / 16];
var m_BindPoses_Unpacked = m_CompressedMesh.m_BindPoses.UnpackFloats(16, 4 * 16);
var buffer = new float[16];
for (int i = 0; i < m_BindPose.Length; i++)
for (var i = 0; i < m_BindPose.Length; i++)
{
Array.Copy(m_BindPoses_Unpacked, i * 16, buffer, 0, 16);
m_BindPose[i] = new Matrix4x4(buffer);
m_BindPose[i] = new Matrix4x4(m_BindPoses_Unpacked.AsSpan(i * 16, 16));
}
}
}
@@ -926,14 +1090,14 @@ namespace AssetStudio
var normalData = m_CompressedMesh.m_Normals.UnpackFloats(2, 4 * 2);
var signs = m_CompressedMesh.m_NormalSigns.UnpackInts();
m_Normals = new float[m_CompressedMesh.m_Normals.m_NumItems / 2 * 3];
for (int i = 0; i < m_CompressedMesh.m_Normals.m_NumItems / 2; ++i)
for (var i = 0; i < m_CompressedMesh.m_Normals.m_NumItems / 2; ++i)
{
var x = normalData[i * 2 + 0];
var y = normalData[i * 2 + 1];
var zsqr = 1 - x * x - y * y;
float z;
if (zsqr >= 0f)
z = (float)Math.Sqrt(zsqr);
z = MathF.Sqrt(zsqr);
else
{
z = 0;
@@ -956,14 +1120,14 @@ namespace AssetStudio
var tangentData = m_CompressedMesh.m_Tangents.UnpackFloats(2, 4 * 2);
var signs = m_CompressedMesh.m_TangentSigns.UnpackInts();
m_Tangents = new float[m_CompressedMesh.m_Tangents.m_NumItems / 2 * 4];
for (int i = 0; i < m_CompressedMesh.m_Tangents.m_NumItems / 2; ++i)
for (var i = 0; i < m_CompressedMesh.m_Tangents.m_NumItems / 2; ++i)
{
var x = tangentData[i * 2 + 0];
var y = tangentData[i * 2 + 1];
var zsqr = 1 - x * x - y * y;
float z;
if (zsqr >= 0f)
z = (float)Math.Sqrt(zsqr);
z = MathF.Sqrt(zsqr);
else
{
z = 0;
@@ -1003,7 +1167,7 @@ namespace AssetStudio
int j = 0;
int sum = 0;
for (int i = 0; i < m_CompressedMesh.m_Weights.m_NumItems; i++)
for (var i = 0; i < m_CompressedMesh.m_Weights.m_NumItems; i++)
{
//read bone index and weight.
m_Skin[bonePos].weight[j] = weights[i] / 31.0f;
@@ -1047,7 +1211,7 @@ namespace AssetStudio
m_CompressedMesh.m_Colors.m_BitSize /= 4;
var tempColors = m_CompressedMesh.m_Colors.UnpackInts();
m_Colors = new float[m_CompressedMesh.m_Colors.m_NumItems];
for (int v = 0; v < m_CompressedMesh.m_Colors.m_NumItems; v++)
for (var v = 0; v < m_CompressedMesh.m_Colors.m_NumItems; v++)
{
m_Colors[v] = tempColors[v] / 255f;
}
@@ -1067,7 +1231,7 @@ namespace AssetStudio
var topology = m_SubMesh.topology;
if (topology == GfxPrimitiveType.Triangles)
{
for (int i = 0; i < indexCount; i += 3)
for (var i = 0; i < indexCount; i += 3)
{
m_Indices.Add(m_IndexBuffer[firstIndex + i]);
m_Indices.Add(m_IndexBuffer[firstIndex + i + 1]);
@@ -1078,7 +1242,7 @@ namespace AssetStudio
{
// de-stripify :
uint triIndex = 0;
for (int i = 0; i < indexCount - 2; i++)
for (var i = 0; i < indexCount - 2; i++)
{
var a = m_IndexBuffer[firstIndex + i];
var b = m_IndexBuffer[firstIndex + i + 1];
@@ -1107,7 +1271,7 @@ namespace AssetStudio
}
else if (topology == GfxPrimitiveType.Quads)
{
for (int q = 0; q < indexCount; q += 4)
for (var q = 0; q < indexCount; q += 4)
{
m_Indices.Add(m_IndexBuffer[firstIndex + q]);
m_Indices.Add(m_IndexBuffer[firstIndex + q + 1]);
@@ -1129,7 +1293,7 @@ namespace AssetStudio
private void InitMSkin()
{
m_Skin = new BoneWeights4[m_VertexCount];
for (int i = 0; i < m_VertexCount; i++)
for (var i = 0; i < m_VertexCount; i++)
{
m_Skin[i] = new BoneWeights4();
}
@@ -1332,15 +1496,19 @@ namespace AssetStudio
var size = GetFormatSize(format);
var len = inputBytes.Length / size;
var result = new float[len];
for (int i = 0; i < len; i++)
for (var i = 0; i < len; i++)
{
switch (format)
{
case VertexFormat.Float:
#if NET
result[i] = BinaryPrimitives.ReadSingleLittleEndian(inputBytes.AsSpan(i * 4));
#else
result[i] = BitConverter.ToSingle(inputBytes, i * 4);
#endif
break;
case VertexFormat.Float16:
result[i] = Half.ToHalf(inputBytes, i * 2);
result[i] = (float)HalfHelper.ToHalf(inputBytes, i * 2);
break;
case VertexFormat.UNorm8:
result[i] = inputBytes[i] / 255f;
@@ -1349,22 +1517,22 @@ 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;
}
}
return result;
}
public static int[] BytesToIntArray(byte[] inputBytes, VertexFormat format)
public static int[] BytesToIntArray(ReadOnlySpan<byte> inputBytes, VertexFormat format)
{
var size = GetFormatSize(format);
var len = inputBytes.Length / size;
var result = new int[len];
for (int i = 0; i < len; i++)
for (var i = 0; i < len; i++)
{
switch (format)
{
@@ -1373,12 +1541,16 @@ namespace AssetStudio
result[i] = inputBytes[i];
break;
case VertexFormat.UInt16:
result[i] = BinaryPrimitives.ReadUInt16LittleEndian(inputBytes.Slice(i * 2));
break;
case VertexFormat.SInt16:
result[i] = BitConverter.ToInt16(inputBytes, i * 2);
result[i] = BinaryPrimitives.ReadInt16LittleEndian(inputBytes.Slice(i * 2));
break;
case VertexFormat.UInt32:
result[i] = (int)BinaryPrimitives.ReadUInt32LittleEndian(inputBytes.Slice(i * 4));
break;
case VertexFormat.SInt32:
result[i] = BitConverter.ToInt32(inputBytes, i * 4);
result[i] = BinaryPrimitives.ReadInt32LittleEndian(inputBytes.Slice(i * 4));
break;
}
}

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class MeshFilter : Component
{

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class MeshRenderer : Renderer
{

View File

@@ -1,15 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class MonoBehaviour : Behaviour
public class MonoBehaviour : Behaviour
{
public PPtr<MonoScript> m_Script;
public string m_Name;
public MonoBehaviour() { }
public MonoBehaviour(ObjectReader reader) : base(reader)
{
m_Script = new PPtr<MonoScript>(reader);

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class MonoScript : NamedObject
{
@@ -16,15 +11,17 @@ namespace AssetStudio
if (version >= (3, 4)) //3.4 and up
{
var m_ExecutionOrder = reader.ReadInt32();
if (version < 5) //5.0 down
{
var m_PropertiesHash = reader.ReadUInt32();
}
else
{
var m_PropertiesHash = reader.ReadBytes(16);
}
}
if (version < 5) //5.0 down
{
var m_PropertiesHash = reader.ReadUInt32();
}
else
{
var m_PropertiesHash = reader.ReadBytes(16);
}
if (version < 3) //3.0 down
{
var m_PathName = reader.ReadAlignedString();

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class MovieTexture : Texture
{
@@ -12,10 +7,13 @@ namespace AssetStudio
public MovieTexture(ObjectReader reader) : base(reader)
{
var m_Loop = reader.ReadBoolean();
reader.AlignStream();
m_AudioClip = new PPtr<AudioClip>(reader);
m_MovieData = reader.ReadUInt8Array();
if (reader.version < (2019, 3)) //2019.3 down
{
var m_Loop = reader.ReadBoolean();
reader.AlignStream();
m_AudioClip = new PPtr<AudioClip>(reader);
m_MovieData = reader.ReadUInt8Array();
}
}
}
}

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public class NamedObject : EditorExtension
{

View File

@@ -1,5 +1,8 @@
using Newtonsoft.Json;
using System.Collections.Specialized;
using System.Collections.Specialized;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace AssetStudio
{
@@ -12,13 +15,31 @@ namespace AssetStudio
public long m_PathID;
[JsonIgnore]
public UnityVersion version;
protected BuildType buildType;
[JsonIgnore]
public BuildTarget platform;
[JsonConverter(typeof(JsonStringEnumConverter))]
public ClassIDType type;
[JsonIgnore]
public SerializedType serializedType;
public int classID;
public uint byteSize;
[JsonIgnore]
public string Name;
private static readonly JsonSerializerOptions jsonOptions;
static Object()
{
jsonOptions = new JsonSerializerOptions
{
Converters = { new JsonConverterHelper.FloatConverter() },
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
PropertyNameCaseInsensitive = true,
IncludeFields = true,
WriteIndented = true,
};
}
public Object() { }
@@ -30,9 +51,9 @@ namespace AssetStudio
type = reader.type;
m_PathID = reader.m_PathID;
version = reader.version;
buildType = reader.buildType;
platform = reader.platform;
serializedType = reader.serializedType;
classID = reader.classID;
byteSize = reader.byteSize;
if (platform == BuildTarget.NoTarget)
@@ -41,57 +62,67 @@ namespace AssetStudio
}
}
public string Dump()
{
if (serializedType?.m_Type != null)
{
return TypeTreeHelper.ReadTypeString(serializedType.m_Type, reader);
}
return null;
}
public string Dump(TypeTree m_Type)
{
if (m_Type != null)
{
return TypeTreeHelper.ReadTypeString(m_Type, reader);
}
return null;
}
public string DumpObject()
{
string str = null;
try
{
str = JsonConvert.SerializeObject(this, new JsonSerializerSettings
if (this is Mesh m_Mesh)
{
Formatting = Formatting.Indented,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
}).Replace(" ", " ");
m_Mesh.ProcessData();
}
str = JsonSerializer.Deserialize<JsonObject>(JsonSerializer.SerializeToUtf8Bytes(this, GetType(), jsonOptions))
.ToJsonString(jsonOptions).Replace(" ", " ");
}
catch
{
//ignore
}
return str;
}
public OrderedDictionary ToType()
public string Dump(TypeTree m_Type = null)
{
if (serializedType?.m_Type != null)
{
return TypeTreeHelper.ReadType(serializedType.m_Type, reader);
}
return null;
m_Type = m_Type ?? serializedType?.m_Type;
if (m_Type == null)
return null;
return TypeTreeHelper.ReadTypeString(m_Type, reader);
}
public OrderedDictionary ToType(TypeTree m_Type)
public OrderedDictionary ToType(TypeTree m_Type = null)
{
if (m_Type != null)
m_Type = m_Type ?? serializedType?.m_Type;
if (m_Type == null)
return null;
return TypeTreeHelper.ReadType(m_Type, reader);
}
public JsonDocument ToJsonDoc(TypeTree m_Type = null)
{
var typeDict = ToType(m_Type);
try
{
return TypeTreeHelper.ReadType(m_Type, reader);
if (typeDict != null)
{
return JsonSerializer.SerializeToDocument(typeDict, jsonOptions);
}
if (this is Mesh m_Mesh)
{
m_Mesh.ProcessData();
}
return JsonSerializer.SerializeToDocument(this, GetType(), jsonOptions);
}
catch
{
//ignore
}
return null;
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Text.Json.Serialization;
namespace AssetStudio
{
@@ -6,9 +7,16 @@ namespace AssetStudio
{
public int m_FileID;
public long m_PathID;
public string Name => _assetsFile != null && TryGet(out var result) ? result.Name : string.Empty;
private SerializedFile _assetsFile;
private int _index = -2; //-2 - Prepare, -1 - Missing
private SerializedFile _assetsFile;
[JsonIgnore]
public SerializedFile AssetsFile
{
get => _assetsFile;
set => _assetsFile = value;
}
public PPtr(ObjectReader reader)
{
@@ -31,7 +39,7 @@ namespace AssetStudio
if (m_FileID > 0 && m_FileID - 1 < _assetsFile.m_Externals.Count)
{
var assetsManager = _assetsFile.assetsManager;
var assetsFileList = assetsManager.assetsFileList;
var assetsFileList = assetsManager.AssetsFileList;
var assetsFileIndexCache = assetsManager.assetsFileIndexCache;
if (_index == -2)
@@ -58,7 +66,7 @@ namespace AssetStudio
public bool TryGet(out T result, SerializedFile assetsFile = null)
{
_assetsFile = _assetsFile ?? assetsFile;
if (TryGetAssetsFile(out var sourceFile))
if (!IsNull && TryGetAssetsFile(out var sourceFile))
{
if (sourceFile.ObjectsDic.TryGetValue(m_PathID, out var obj))
{
@@ -77,7 +85,7 @@ namespace AssetStudio
public bool TryGet<T2>(out T2 result, SerializedFile assetsFile = null) where T2 : Object
{
_assetsFile = _assetsFile ?? assetsFile;
if (TryGetAssetsFile(out var sourceFile))
if (!IsNull && TryGetAssetsFile(out var sourceFile))
{
if (sourceFile.ObjectsDic.TryGetValue(m_PathID, out var obj))
{
@@ -118,7 +126,7 @@ namespace AssetStudio
}
var assetsManager = _assetsFile.assetsManager;
var assetsFileList = assetsManager.assetsFileList;
var assetsFileList = assetsManager.AssetsFileList;
var assetsFileIndexCache = assetsManager.assetsFileIndexCache;
if (!assetsFileIndexCache.TryGetValue(name, out _index))

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class PlayerSettings : Object
{
@@ -12,37 +7,40 @@ namespace AssetStudio
public PlayerSettings(ObjectReader reader) : base(reader)
{
if (version >= (5, 4)) //5.4.0 and up
if (version >= (3, 0))
{
var productGUID = reader.ReadBytes(16);
}
var AndroidProfiler = reader.ReadBoolean();
//bool AndroidFilterTouchesWhenObscured 2017.2 and up
//bool AndroidEnableSustainedPerformanceMode 2018 and up
reader.AlignStream();
int defaultScreenOrientation = reader.ReadInt32();
int targetDevice = reader.ReadInt32();
if (version < (5, 3)) //5.3 down
{
if (version < 5) //5.0 down
if (version >= (5, 4)) //5.4.0 and up
{
int targetPlatform = reader.ReadInt32(); //4.0 and up targetGlesGraphics
if (version >= (4, 6)) //4.6 and up
{
var targetIOSGraphics = reader.ReadInt32();
}
var productGUID = reader.ReadBytes(16);
}
int targetResolution = reader.ReadInt32();
}
else
{
var useOnDemandResources = reader.ReadBoolean();
var AndroidProfiler = reader.ReadBoolean();
//bool AndroidFilterTouchesWhenObscured 2017.2 and up
//bool AndroidEnableSustainedPerformanceMode 2018 and up
reader.AlignStream();
}
if (version >= (3, 5)) //3.5 and up
{
var accelerometerFrequency = reader.ReadInt32();
int defaultScreenOrientation = reader.ReadInt32();
int targetDevice = reader.ReadInt32();
if (version < (5, 3)) //5.3 down
{
if (version < 5) //5.0 down
{
int targetPlatform = reader.ReadInt32(); //4.0 and up targetGlesGraphics
if (version >= (4, 6)) //4.6 and up
{
var targetIOSGraphics = reader.ReadInt32();
}
}
int targetResolution = reader.ReadInt32();
}
else
{
var useOnDemandResources = reader.ReadBoolean();
reader.AlignStream();
}
if (version >= (3, 5)) //3.5 and up
{
var accelerometerFrequency = reader.ReadInt32();
}
}
companyName = reader.ReadAlignedString();
productName = reader.ReadAlignedString();

View File

@@ -1,16 +1,18 @@
namespace AssetStudio
using System.Collections.Generic;
namespace AssetStudio
{
public sealed class PreloadData : NamedObject
{
public PPtr<Object>[] m_Assets;
public List<PPtr<Object>> m_Assets;
public PreloadData(ObjectReader reader) : base(reader)
{
var m_PreloadTableSize = reader.ReadInt32();
m_Assets = new PPtr<Object>[m_PreloadTableSize];
m_Assets = new List<PPtr<Object>>();
for (var i = 0; i < m_PreloadTableSize; i++)
{
m_Assets[i] = new PPtr<Object>(reader);
m_Assets.Add(new PPtr<Object>(reader));
}
/*

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class RectTransform : Transform
{

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
namespace AssetStudio
{
@@ -19,7 +16,7 @@ namespace AssetStudio
public abstract class Renderer : Component
{
public PPtr<Material>[] m_Materials;
public List<PPtr<Material>> m_Materials;
public StaticBatchInfo m_StaticBatchInfo;
public uint[] m_SubsetIndices;
@@ -58,6 +55,29 @@ namespace AssetStudio
{
var m_RayTraceProcedural = reader.ReadByte();
}
if (version.IsTuanjie) //2022.3.2t3(1.0.0) and up
{
var m_virtualGeometry = reader.ReadByte();
var m_virtualGeometryShadow = reader.ReadByte();
if (version > (2022, 3, 48) || (version == (2022, 3, 48) && version.Build >= 3)) //2022.3.48t3(1.4.0) and up
{
reader.AlignStream();
var m_ShadingRate = reader.ReadByte();
if (version >= (2022, 3, 61)) //2022.3.61t1(1.6.0) and up
{
var m_ForceDisableGRD = reader.ReadByte();
}
}
}
if (version >= (2023, 2)) //2023.2 and up
{
var m_RayTracingAccelStructBuildFlagsOverride = reader.ReadByte();
var m_RayTracingAccelStructBuildFlags = reader.ReadByte();
}
if (version >= (2023, 3)) //2023.3 and up
{
var m_SmallMeshCulling = reader.ReadByte();
}
reader.AlignStream();
}
else
@@ -94,10 +114,10 @@ namespace AssetStudio
}
var m_MaterialsSize = reader.ReadInt32();
m_Materials = new PPtr<Material>[m_MaterialsSize];
for (int i = 0; i < m_MaterialsSize; i++)
m_Materials = new List<PPtr<Material>>();
for (var i = 0; i < m_MaterialsSize; i++)
{
m_Materials[i] = new PPtr<Material>(reader);
m_Materials.Add(new PPtr<Material>(reader));
}
if (version < 3) //3.0 down
@@ -128,7 +148,7 @@ namespace AssetStudio
var m_UseLightProbes = reader.ReadBoolean();
reader.AlignStream();
if (version >= 5)//5.0 and up
if (version >= 5) //5.0 and up
{
var m_ReflectionProbeUsage = reader.ReadInt32();
}
@@ -144,10 +164,14 @@ namespace AssetStudio
}
else
{
var m_SortingLayerID = reader.ReadUInt32();
var m_SortingLayerID = reader.ReadInt32();
}
if (version > (5, 6) || (version == (5, 6) && version.Build >= 3)) //5.6.0f3 and up
{
var m_SortingLayer = reader.ReadInt16();
}
//SInt16 m_SortingLayer 5.6 and up
var m_SortingOrder = reader.ReadInt16();
reader.AlignStream();
}

View File

@@ -4,15 +4,15 @@ namespace AssetStudio
{
public class ResourceManager : Object
{
public KeyValuePair<string, PPtr<Object>>[] m_Container;
public List<KeyValuePair<string, PPtr<Object>>> m_Container;
public ResourceManager(ObjectReader reader) : base(reader)
{
var m_ContainerSize = reader.ReadInt32();
m_Container = new KeyValuePair<string, PPtr<Object>>[m_ContainerSize];
for (int i = 0; i < m_ContainerSize; i++)
m_Container = new List<KeyValuePair<string, PPtr<Object>>>();
for (var i = 0; i < m_ContainerSize; i++)
{
m_Container[i] = new KeyValuePair<string, PPtr<Object>>(reader.ReadAlignedString(), new PPtr<Object>(reader));
m_Container.Add(new KeyValuePair<string, PPtr<Object>>(reader.ReadAlignedString(), new PPtr<Object>(reader)));
}
}
}

View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public abstract class RuntimeAnimatorController : NamedObject
{

View File

@@ -17,8 +17,8 @@ namespace AssetStudio
public class StructParameter
{
public MatrixParameter[] m_MatrixParams;
public VectorParameter[] m_VectorParams;
public List<MatrixParameter> m_MatrixParams;
public List<VectorParameter> m_VectorParams;
public StructParameter(BinaryReader reader)
{
@@ -28,17 +28,17 @@ namespace AssetStudio
var m_StructSize = reader.ReadInt32();
int numVectorParams = reader.ReadInt32();
m_VectorParams = new VectorParameter[numVectorParams];
for (int i = 0; i < numVectorParams; i++)
m_VectorParams = new List<VectorParameter>();
for (var i = 0; i < numVectorParams; i++)
{
m_VectorParams[i] = new VectorParameter(reader);
m_VectorParams.Add(new VectorParameter(reader));
}
int numMatrixParams = reader.ReadInt32();
m_MatrixParams = new MatrixParameter[numMatrixParams];
for (int i = 0; i < numMatrixParams; i++)
m_MatrixParams = new List<MatrixParameter>();
for (var i = 0; i < numMatrixParams; i++)
{
m_MatrixParams[i] = new MatrixParameter(reader);
m_MatrixParams.Add(new MatrixParameter(reader));
}
}
}
@@ -112,15 +112,15 @@ namespace AssetStudio
public class SerializedProperties
{
public SerializedProperty[] m_Props;
public List<SerializedProperty> m_Props;
public SerializedProperties(BinaryReader reader)
{
int numProps = reader.ReadInt32();
m_Props = new SerializedProperty[numProps];
for (int i = 0; i < numProps; i++)
m_Props = new List<SerializedProperty>();
for (var i = 0; i < numProps; i++)
{
m_Props[i] = new SerializedProperty(reader);
m_Props.Add(new SerializedProperty(reader));
}
}
}
@@ -237,7 +237,7 @@ namespace AssetStudio
m_Name = reader.ReadAlignedString();
rtBlend = new SerializedShaderRTBlendState[8];
for (int i = 0; i < 8; i++)
for (var i = 0; i < 8; i++)
{
rtBlend[i] = new SerializedShaderRTBlendState(reader);
}
@@ -290,16 +290,16 @@ namespace AssetStudio
public class ParserBindChannels
{
public ShaderBindChannel[] m_Channels;
public List<ShaderBindChannel> m_Channels;
public uint m_SourceMap;
public ParserBindChannels(BinaryReader reader)
{
int numChannels = reader.ReadInt32();
m_Channels = new ShaderBindChannel[numChannels];
for (int i = 0; i < numChannels; i++)
m_Channels = new List<ShaderBindChannel>();
for (var i = 0; i < numChannels; i++)
{
m_Channels[i] = new ShaderBindChannel(reader);
m_Channels.Add(new ShaderBindChannel(reader));
}
reader.AlignStream();
@@ -390,9 +390,9 @@ namespace AssetStudio
public class ConstantBuffer
{
public int m_NameIndex;
public MatrixParameter[] m_MatrixParams;
public VectorParameter[] m_VectorParams;
public StructParameter[] m_StructParams;
public List<MatrixParameter> m_MatrixParams;
public List<VectorParameter> m_VectorParams;
public List<StructParameter> m_StructParams;
public int m_Size;
public bool m_IsPartialCB;
@@ -403,25 +403,25 @@ namespace AssetStudio
m_NameIndex = reader.ReadInt32();
int numMatrixParams = reader.ReadInt32();
m_MatrixParams = new MatrixParameter[numMatrixParams];
for (int i = 0; i < numMatrixParams; i++)
m_MatrixParams = new List<MatrixParameter>();
for (var i = 0; i < numMatrixParams; i++)
{
m_MatrixParams[i] = new MatrixParameter(reader);
m_MatrixParams.Add(new MatrixParameter(reader));
}
int numVectorParams = reader.ReadInt32();
m_VectorParams = new VectorParameter[numVectorParams];
for (int i = 0; i < numVectorParams; i++)
m_VectorParams = new List<VectorParameter>();
for (var i = 0; i < numVectorParams; i++)
{
m_VectorParams[i] = new VectorParameter(reader);
m_VectorParams.Add(new VectorParameter(reader));
}
if (version >= (2017, 3)) //2017.3 and up
{
int numStructParams = reader.ReadInt32();
m_StructParams = new StructParameter[numStructParams];
for (int i = 0; i < numStructParams; i++)
m_StructParams = new List<StructParameter>();
for (var i = 0; i < numStructParams; i++)
{
m_StructParams[i] = new StructParameter(reader);
m_StructParams.Add(new StructParameter(reader));
}
}
m_Size = reader.ReadInt32();
@@ -488,71 +488,74 @@ namespace AssetStudio
public class SerializedProgramParameters
{
public VectorParameter[] m_VectorParams;
public MatrixParameter[] m_MatrixParams;
public TextureParameter[] m_TextureParams;
public BufferBinding[] m_BufferParams;
public ConstantBuffer[] m_ConstantBuffers;
public BufferBinding[] m_ConstantBufferBindings;
public UAVParameter[] m_UAVParams;
public SamplerParameter[] m_Samplers;
public List<VectorParameter> m_VectorParams;
public List<MatrixParameter> m_MatrixParams;
public List<TextureParameter> m_TextureParams;
public List<BufferBinding> m_BufferParams;
public List<ConstantBuffer> m_ConstantBuffers;
public List<BufferBinding> m_ConstantBufferBindings;
public List<UAVParameter> m_UAVParams;
public List<SamplerParameter> m_Samplers;
public SerializedProgramParameters(ObjectReader reader)
{
int numVectorParams = reader.ReadInt32();
m_VectorParams = new VectorParameter[numVectorParams];
for (int i = 0; i < numVectorParams; i++)
m_VectorParams = new List<VectorParameter>();
for (var i = 0; i < numVectorParams; i++)
{
m_VectorParams[i] = new VectorParameter(reader);
m_VectorParams.Add(new VectorParameter(reader));
}
int numMatrixParams = reader.ReadInt32();
m_MatrixParams = new MatrixParameter[numMatrixParams];
for (int i = 0; i < numMatrixParams; i++)
m_MatrixParams = new List<MatrixParameter>();
for (var i = 0; i < numMatrixParams; i++)
{
m_MatrixParams[i] = new MatrixParameter(reader);
m_MatrixParams.Add(new MatrixParameter(reader));
}
int numTextureParams = reader.ReadInt32();
m_TextureParams = new TextureParameter[numTextureParams];
for (int i = 0; i < numTextureParams; i++)
m_TextureParams = new List<TextureParameter>();
for (var i = 0; i < numTextureParams; i++)
{
m_TextureParams[i] = new TextureParameter(reader);
m_TextureParams.Add(new TextureParameter(reader));
}
int numBufferParams = reader.ReadInt32();
m_BufferParams = new BufferBinding[numBufferParams];
for (int i = 0; i < numBufferParams; i++)
m_BufferParams = new List<BufferBinding>();
for (var i = 0; i < numBufferParams; i++)
{
m_BufferParams[i] = new BufferBinding(reader);
m_BufferParams.Add(new BufferBinding(reader));
}
int numConstantBuffers = reader.ReadInt32();
m_ConstantBuffers = new ConstantBuffer[numConstantBuffers];
for (int i = 0; i < numConstantBuffers; i++)
m_ConstantBuffers = new List<ConstantBuffer>();
for (var i = 0; i < numConstantBuffers; i++)
{
m_ConstantBuffers[i] = new ConstantBuffer(reader);
m_ConstantBuffers.Add(new ConstantBuffer(reader));
}
int numConstantBufferBindings = reader.ReadInt32();
m_ConstantBufferBindings = new BufferBinding[numConstantBufferBindings];
for (int i = 0; i < numConstantBufferBindings; i++)
m_ConstantBufferBindings = new List<BufferBinding>();
for (var i = 0; i < numConstantBufferBindings; i++)
{
m_ConstantBufferBindings[i] = new BufferBinding(reader);
m_ConstantBufferBindings.Add(new BufferBinding(reader));
}
int numUAVParams = reader.ReadInt32();
m_UAVParams = new UAVParameter[numUAVParams];
for (int i = 0; i < numUAVParams; i++)
m_UAVParams = new List<UAVParameter>();
for (var i = 0; i < numUAVParams; i++)
{
m_UAVParams[i] = new UAVParameter(reader);
m_UAVParams.Add(new UAVParameter(reader));
}
int numSamplers = reader.ReadInt32();
m_Samplers = new SamplerParameter[numSamplers];
for (int i = 0; i < numSamplers; i++)
if (reader.version >= 2017) //2017 and up
{
m_Samplers[i] = new SamplerParameter(reader);
int numSamplers = reader.ReadInt32();
m_Samplers = new List<SamplerParameter>();
for (var i = 0; i < numSamplers; i++)
{
m_Samplers.Add(new SamplerParameter(reader));
}
}
}
}
@@ -564,15 +567,7 @@ namespace AssetStudio
public ushort[] m_KeywordIndices;
public sbyte m_ShaderHardwareTier;
public ShaderGpuProgramType m_GpuProgramType;
public SerializedProgramParameters m_Parameters;
public VectorParameter[] m_VectorParams;
public MatrixParameter[] m_MatrixParams;
public TextureParameter[] m_TextureParams;
public BufferBinding[] m_BufferParams;
public ConstantBuffer[] m_ConstantBuffers;
public BufferBinding[] m_ConstantBufferBindings;
public UAVParameter[] m_UAVParams;
public SamplerParameter[] m_Samplers;
public SerializedProgramParameters m_Parameters; // nested since 2020.3.2f1 and up; 2021.1.1f1 and up
public SerializedSubProgram(ObjectReader reader)
{
@@ -601,72 +596,7 @@ namespace AssetStudio
m_GpuProgramType = (ShaderGpuProgramType)reader.ReadSByte();
reader.AlignStream();
if (version.IsInRange((2020, 3, 2), 2021) //2020.3.2f1 and up
|| version >= (2021, 1, 1)) //2021.1.1f1 and up
{
m_Parameters = new SerializedProgramParameters(reader);
}
else
{
int numVectorParams = reader.ReadInt32();
m_VectorParams = new VectorParameter[numVectorParams];
for (int i = 0; i < numVectorParams; i++)
{
m_VectorParams[i] = new VectorParameter(reader);
}
int numMatrixParams = reader.ReadInt32();
m_MatrixParams = new MatrixParameter[numMatrixParams];
for (int i = 0; i < numMatrixParams; i++)
{
m_MatrixParams[i] = new MatrixParameter(reader);
}
int numTextureParams = reader.ReadInt32();
m_TextureParams = new TextureParameter[numTextureParams];
for (int i = 0; i < numTextureParams; i++)
{
m_TextureParams[i] = new TextureParameter(reader);
}
int numBufferParams = reader.ReadInt32();
m_BufferParams = new BufferBinding[numBufferParams];
for (int i = 0; i < numBufferParams; i++)
{
m_BufferParams[i] = new BufferBinding(reader);
}
int numConstantBuffers = reader.ReadInt32();
m_ConstantBuffers = new ConstantBuffer[numConstantBuffers];
for (int i = 0; i < numConstantBuffers; i++)
{
m_ConstantBuffers[i] = new ConstantBuffer(reader);
}
int numConstantBufferBindings = reader.ReadInt32();
m_ConstantBufferBindings = new BufferBinding[numConstantBufferBindings];
for (int i = 0; i < numConstantBufferBindings; i++)
{
m_ConstantBufferBindings[i] = new BufferBinding(reader);
}
int numUAVParams = reader.ReadInt32();
m_UAVParams = new UAVParameter[numUAVParams];
for (int i = 0; i < numUAVParams; i++)
{
m_UAVParams[i] = new UAVParameter(reader);
}
if (version >= 2017) //2017 and up
{
int numSamplers = reader.ReadInt32();
m_Samplers = new SamplerParameter[numSamplers];
for (int i = 0; i < numSamplers; i++)
{
m_Samplers[i] = new SamplerParameter(reader);
}
}
}
m_Parameters = new SerializedProgramParameters(reader);
if (version >= (2017, 2)) //2017.2 and up
{
@@ -684,7 +614,7 @@ namespace AssetStudio
public class SerializedProgram
{
public SerializedSubProgram[] m_SubPrograms;
public List<SerializedSubProgram> m_SubPrograms;
public SerializedProgramParameters m_CommonParameters;
public ushort[] m_SerializedKeywordStateMask;
@@ -693,10 +623,10 @@ namespace AssetStudio
var version = reader.version;
int numSubPrograms = reader.ReadInt32();
m_SubPrograms = new SerializedSubProgram[numSubPrograms];
for (int i = 0; i < numSubPrograms; i++)
m_SubPrograms = new List<SerializedSubProgram>();
for (var i = 0; i < numSubPrograms; i++)
{
m_SubPrograms[i] = new SerializedSubProgram(reader);
m_SubPrograms.Add(new SerializedSubProgram(reader));
}
if (version.IsInRange((2020, 3, 2), 2021) //2020.3.2f1 and up
@@ -722,11 +652,11 @@ namespace AssetStudio
public class SerializedPass
{
public Hash128[] m_EditorDataHash;
public List<Hash128> m_EditorDataHash;
public byte[] m_Platforms;
public ushort[] m_LocalKeywordMask;
public ushort[] m_GlobalKeywordMask;
public KeyValuePair<string, int>[] m_NameIndices;
public List<KeyValuePair<string, int>> m_NameIndices;
public PassType m_Type;
public SerializedShaderState m_State;
public uint m_ProgramMask;
@@ -750,10 +680,10 @@ namespace AssetStudio
if (version >= (2020, 2)) //2020.2 and up
{
int numEditorDataHash = reader.ReadInt32();
m_EditorDataHash = new Hash128[numEditorDataHash];
for (int i = 0; i < numEditorDataHash; i++)
m_EditorDataHash = new List<Hash128>();
for (var i = 0; i < numEditorDataHash; i++)
{
m_EditorDataHash[i] = new Hash128(reader);
m_EditorDataHash.Add(new Hash128(reader));
}
reader.AlignStream();
m_Platforms = reader.ReadUInt8Array();
@@ -768,10 +698,10 @@ namespace AssetStudio
}
int numIndices = reader.ReadInt32();
m_NameIndices = new KeyValuePair<string, int>[numIndices];
for (int i = 0; i < numIndices; i++)
m_NameIndices = new List<KeyValuePair<string, int>>();
for (var i = 0; i < numIndices; i++)
{
m_NameIndices[i] = new KeyValuePair<string, int>(reader.ReadAlignedString(), reader.ReadInt32());
m_NameIndices.Add(new KeyValuePair<string, int>(reader.ReadAlignedString(), reader.ReadInt32()));
}
m_Type = (PassType)reader.ReadInt32();
@@ -806,32 +736,32 @@ namespace AssetStudio
public class SerializedTagMap
{
public KeyValuePair<string, string>[] tags;
public List<KeyValuePair<string, string>> tags;
public SerializedTagMap(BinaryReader reader)
{
int numTags = reader.ReadInt32();
tags = new KeyValuePair<string, string>[numTags];
for (int i = 0; i < numTags; i++)
tags = new List<KeyValuePair<string, string>>();
for (var i = 0; i < numTags; i++)
{
tags[i] = new KeyValuePair<string, string>(reader.ReadAlignedString(), reader.ReadAlignedString());
tags.Add(new KeyValuePair<string, string>(reader.ReadAlignedString(), reader.ReadAlignedString()));
}
}
}
public class SerializedSubShader
{
public SerializedPass[] m_Passes;
public List<SerializedPass> m_Passes;
public SerializedTagMap m_Tags;
public int m_LOD;
public SerializedSubShader(ObjectReader reader)
{
int numPasses = reader.ReadInt32();
m_Passes = new SerializedPass[numPasses];
for (int i = 0; i < numPasses; i++)
m_Passes = new List<SerializedPass>();
for (var i = 0; i < numPasses; i++)
{
m_Passes[i] = new SerializedPass(reader);
m_Passes.Add(new SerializedPass(reader));
}
m_Tags = new SerializedTagMap(reader);
@@ -866,14 +796,14 @@ namespace AssetStudio
public class SerializedShader
{
public SerializedProperties m_PropInfo;
public SerializedSubShader[] m_SubShaders;
public List<SerializedSubShader> m_SubShaders;
public string[] m_KeywordNames;
public byte[] m_KeywordFlags;
public string m_Name;
public string m_CustomEditorName;
public string m_FallbackName;
public SerializedShaderDependency[] m_Dependencies;
public SerializedCustomEditorForRenderPipeline[] m_CustomEditorForRenderPipelines;
public List<SerializedShaderDependency> m_Dependencies;
public List<SerializedCustomEditorForRenderPipeline> m_CustomEditorForRenderPipelines;
public bool m_DisableNoSubshadersMessage;
public SerializedShader(ObjectReader reader)
@@ -883,10 +813,10 @@ namespace AssetStudio
m_PropInfo = new SerializedProperties(reader);
int numSubShaders = reader.ReadInt32();
m_SubShaders = new SerializedSubShader[numSubShaders];
for (int i = 0; i < numSubShaders; i++)
m_SubShaders = new List<SerializedSubShader>();
for (var i = 0; i < numSubShaders; i++)
{
m_SubShaders[i] = new SerializedSubShader(reader);
m_SubShaders.Add(new SerializedSubShader(reader));
}
if (version >= (2021, 2)) //2021.2 and up
@@ -901,19 +831,19 @@ namespace AssetStudio
m_FallbackName = reader.ReadAlignedString();
int numDependencies = reader.ReadInt32();
m_Dependencies = new SerializedShaderDependency[numDependencies];
for (int i = 0; i < numDependencies; i++)
m_Dependencies = new List<SerializedShaderDependency>();
for (var i = 0; i < numDependencies; i++)
{
m_Dependencies[i] = new SerializedShaderDependency(reader);
m_Dependencies.Add(new SerializedShaderDependency(reader));
}
if (version >= 2021) //2021.1 and up
{
int m_CustomEditorForRenderPipelinesSize = reader.ReadInt32();
m_CustomEditorForRenderPipelines = new SerializedCustomEditorForRenderPipeline[m_CustomEditorForRenderPipelinesSize];
for (int i = 0; i < m_CustomEditorForRenderPipelinesSize; i++)
m_CustomEditorForRenderPipelines = new List<SerializedCustomEditorForRenderPipeline>();
for (var i = 0; i < m_CustomEditorForRenderPipelinesSize; i++)
{
m_CustomEditorForRenderPipelines[i] = new SerializedCustomEditorForRenderPipeline(reader);
m_CustomEditorForRenderPipelines.Add(new SerializedCustomEditorForRenderPipeline(reader));
}
}
@@ -988,18 +918,18 @@ namespace AssetStudio
reader.AlignStream();
var m_DependenciesCount = reader.ReadInt32();
for (int i = 0; i < m_DependenciesCount; i++)
for (var i = 0; i < m_DependenciesCount; i++)
{
new PPtr<Shader>(reader);
var m_Dependencies = new PPtr<Shader>(reader);
}
if (version >= 2018)
{
var m_NonModifiableTexturesCount = reader.ReadInt32();
for (int i = 0; i < m_NonModifiableTexturesCount; i++)
for (var i = 0; i < m_NonModifiableTexturesCount; i++)
{
var first = reader.ReadAlignedString();
new PPtr<Texture>(reader);
var second = new PPtr<Texture>(reader);
}
}

View File

@@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
namespace AssetStudio
{
public sealed class SkinnedMeshRenderer : Renderer
{
public PPtr<Mesh> m_Mesh;
public PPtr<Transform>[] m_Bones;
public List<PPtr<Transform>> m_Bones;
public float[] m_BlendShapeWeights;
public SkinnedMeshRenderer(ObjectReader reader) : base(reader)
@@ -25,10 +22,11 @@ namespace AssetStudio
m_Mesh = new PPtr<Mesh>(reader);
m_Bones = new PPtr<Transform>[reader.ReadInt32()];
for (int b = 0; b < m_Bones.Length; b++)
var numBones = reader.ReadInt32();
m_Bones = new List<PPtr<Transform>>();
for (var b = 0; b < numBones; b++)
{
m_Bones[b] = new PPtr<Transform>(reader);
m_Bones.Add(new PPtr<Transform>(reader));
}
if (version >= (4, 3)) //4.3 and up

View File

@@ -79,11 +79,11 @@ namespace AssetStudio
{
public PPtr<Texture2D> texture;
public PPtr<Texture2D> alphaTexture;
public SecondarySpriteTexture[] secondaryTextures;
public SubMesh[] m_SubMeshes;
public List<SecondarySpriteTexture> secondaryTextures;
public List<SubMesh> m_SubMeshes;
public byte[] m_IndexBuffer;
public VertexData m_VertexData;
public SpriteVertex[] vertices;
public List<SpriteVertex> vertices;
public ushort[] indices;
public Matrix4x4[] m_Bindpose;
public BoneWeights4[] m_SourceSkin;
@@ -99,28 +99,25 @@ namespace AssetStudio
var version = reader.version;
texture = new PPtr<Texture2D>(reader);
if (version >= (5, 2)) //5.2 and up
{
alphaTexture = new PPtr<Texture2D>(reader);
}
alphaTexture = version >= (5, 2) ? new PPtr<Texture2D>(reader) : new PPtr<Texture2D>(); //5.2 and up
if (version >= 2019) //2019 and up
{
var secondaryTexturesSize = reader.ReadInt32();
secondaryTextures = new SecondarySpriteTexture[secondaryTexturesSize];
for (int i = 0; i < secondaryTexturesSize; i++)
secondaryTextures = new List<SecondarySpriteTexture>();
for (var i = 0; i < secondaryTexturesSize; i++)
{
secondaryTextures[i] = new SecondarySpriteTexture(reader);
secondaryTextures.Add(new SecondarySpriteTexture(reader));
}
}
if (version >= (5, 6)) //5.6 and up
{
var m_SubMeshesSize = reader.ReadInt32();
m_SubMeshes = new SubMesh[m_SubMeshesSize];
for (int i = 0; i < m_SubMeshesSize; i++)
m_SubMeshes = new List<SubMesh>();
for (var i = 0; i < m_SubMeshesSize; i++)
{
m_SubMeshes[i] = new SubMesh(reader);
m_SubMeshes.Add(new SubMesh(reader));
}
m_IndexBuffer = reader.ReadUInt8Array();
@@ -131,10 +128,10 @@ namespace AssetStudio
else
{
var verticesSize = reader.ReadInt32();
vertices = new SpriteVertex[verticesSize];
for (int i = 0; i < verticesSize; i++)
vertices = new List<SpriteVertex>();
for (var i = 0; i < verticesSize; i++)
{
vertices[i] = new SpriteVertex(reader);
vertices.Add(new SpriteVertex(reader));
}
indices = reader.ReadUInt16Array();
@@ -148,7 +145,9 @@ namespace AssetStudio
if (version < (2018, 2)) //2018.2 down
{
var m_SourceSkinSize = reader.ReadInt32();
for (int i = 0; i < m_SourceSkinSize; i++)
reader.ThrowIfTooLarge(m_SourceSkinSize * 32f);
m_SourceSkin = new BoneWeights4[m_SourceSkinSize];
for (var i = 0; i < m_SourceSkinSize; i++)
{
m_SourceSkin[i] = new BoneWeights4(reader);
}
@@ -204,7 +203,7 @@ namespace AssetStudio
public string[] m_AtlasTags;
public PPtr<SpriteAtlas> m_SpriteAtlas;
public SpriteRenderData m_RD;
public Vector2[][] m_PhysicsShape;
//public Vector2[][] m_PhysicsShape;
public Sprite(ObjectReader reader) : base(reader)
{
@@ -217,7 +216,7 @@ namespace AssetStudio
m_PixelsToUnits = reader.ReadSingle();
if (version >= (5, 4, 2)
|| version == (5, 4, 1) && buildType.IsPatch && version.Build >= 3) //5.4.1p3 and up
|| version == (5, 4, 1) && version.IsPatch && version.Build >= 3) //5.4.1p3 and up
{
m_Pivot = reader.ReadVector2();
}
@@ -241,17 +240,18 @@ namespace AssetStudio
}
m_RD = new SpriteRenderData(reader);
/*
if (version >= 2017) //2017 and up
{
var m_PhysicsShapeSize = reader.ReadInt32();
m_PhysicsShape = new Vector2[m_PhysicsShapeSize][];
for (int i = 0; i < m_PhysicsShapeSize; i++)
var physicsShapeList = new List<Vector2[]>();
for (var i = 0; i < m_PhysicsShapeSize; i++)
{
m_PhysicsShape[i] = reader.ReadVector2Array();
physicsShapeList.Add(reader.ReadVector2Array());
}
m_PhysicsShape = physicsShapeList.ToArray();
}
*/
//vector m_Bones 2018 and up
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace AssetStudio
{
@@ -13,7 +14,7 @@ namespace AssetStudio
public Vector4 uvTransform;
public float downscaleMultiplier;
public SpriteSettings settingsRaw;
public SecondarySpriteTexture[] secondaryTextures;
public List<SecondarySpriteTexture> secondaryTextures;
public SpriteAtlasData(ObjectReader reader)
{
@@ -32,10 +33,10 @@ namespace AssetStudio
if (version >= (2020, 2)) //2020.2 and up
{
var secondaryTexturesSize = reader.ReadInt32();
secondaryTextures = new SecondarySpriteTexture[secondaryTexturesSize];
for (int i = 0; i < secondaryTexturesSize; i++)
secondaryTextures = new List<SecondarySpriteTexture>();
for (var i = 0; i < secondaryTexturesSize; i++)
{
secondaryTextures[i] = new SecondarySpriteTexture(reader);
secondaryTextures.Add(new SecondarySpriteTexture(reader));
}
reader.AlignStream();
}
@@ -44,24 +45,25 @@ namespace AssetStudio
public sealed class SpriteAtlas : NamedObject
{
public PPtr<Sprite>[] m_PackedSprites;
public List<PPtr<Sprite>> m_PackedSprites;
[JsonConverter(typeof(JsonConverterHelper.RenderDataMapConverter))]
public Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData> m_RenderDataMap;
public bool m_IsVariant;
public SpriteAtlas(ObjectReader reader) : base(reader)
{
var m_PackedSpritesSize = reader.ReadInt32();
m_PackedSprites = new PPtr<Sprite>[m_PackedSpritesSize];
for (int i = 0; i < m_PackedSpritesSize; i++)
m_PackedSprites = new List<PPtr<Sprite>>();
for (var i = 0; i < m_PackedSpritesSize; i++)
{
m_PackedSprites[i] = new PPtr<Sprite>(reader);
m_PackedSprites.Add(new PPtr<Sprite>(reader));
}
var m_PackedSpriteNamesToIndex = reader.ReadStringArray();
var m_RenderDataMapSize = reader.ReadInt32();
m_RenderDataMap = new Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData>(m_RenderDataMapSize);
for (int i = 0; i < m_RenderDataMapSize; i++)
m_RenderDataMap = new Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData>();
for (var i = 0; i < m_RenderDataMapSize; i++)
{
var first = new Guid(reader.ReadBytes(16));
var second = reader.ReadInt64();

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public sealed class TextAsset : NamedObject
{

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections;
using Newtonsoft.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio
{
@@ -8,15 +8,17 @@ namespace AssetStudio
{
public int m_Width;
public int m_Height;
public int m_CompleteImageSize;
public uint m_CompleteImageSize;
public TextureFormat m_TextureFormat;
public bool m_MipMap;
public int m_MipCount;
public GLTextureSettings m_TextureSettings;
public int m_ImageCount;
public byte[] m_PlatformBlob;
[JsonPropertyName("image data")]
public ResourceReader image_data;
public StreamingInfo m_StreamData;
public StreamingInfo m_DataStreamData; //Tuanjie
public Texture2D() { }
@@ -43,7 +45,7 @@ namespace AssetStudio
//var imgActualDataSize = GetImageDataSize(m_TextureFormat);
//var mipmapSize = (int)(m_Texture2DArray.m_DataSize / m_Texture2DArray.m_Depth - imgActualDataSize);
m_CompleteImageSize = (int)m_Texture2DArray.m_DataSize / m_Texture2DArray.m_Depth;
m_CompleteImageSize = (uint)(m_Texture2DArray.m_DataSize / m_Texture2DArray.m_Depth);
var offset = layer * m_CompleteImageSize + m_Texture2DArray.image_data.Offset;
image_data = !string.IsNullOrEmpty(m_StreamData?.path)
@@ -53,9 +55,9 @@ namespace AssetStudio
byteSize = (uint)(m_Width * m_Height) * 4;
}
public Texture2D(ObjectReader reader, IDictionary typeDict) : base(reader)
public Texture2D(ObjectReader reader, byte[] type, JsonSerializerOptions jsonOptions) : base(reader)
{
var parsedTex2d = JsonConvert.DeserializeObject<Texture2D>(JsonConvert.SerializeObject(typeDict));
var parsedTex2d = JsonSerializer.Deserialize<Texture2D>(type, jsonOptions);
m_Width = parsedTex2d.m_Width;
m_Height = parsedTex2d.m_Height;
m_CompleteImageSize = parsedTex2d.m_CompleteImageSize;
@@ -70,19 +72,37 @@ namespace AssetStudio
image_data = !string.IsNullOrEmpty(m_StreamData?.path)
? new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size)
: new ResourceReader(reader, parsedTex2d.image_data.Offset, parsedTex2d.image_data.Size);
typeDict.Clear();
}
public Texture2D(ObjectReader reader) : base(reader)
{
m_Width = reader.ReadInt32();
m_Height = reader.ReadInt32();
m_CompleteImageSize = reader.ReadInt32();
m_CompleteImageSize = reader.ReadUInt32();
if (version >= 2020) //2020.1 and up
{
var m_MipsStripped = reader.ReadInt32();
}
if (version.IsTuanjie && (version > (2022, 3, 2) || version.Build >= 8)) //2022.3.2t8(1.1.0) and up
{
var m_WebStreaming = reader.ReadBoolean();
reader.AlignStream();
var m_PriorityLevel = reader.ReadInt32();
var m_UploadedMode = reader.ReadInt32();
m_DataStreamData = new StreamingInfo //sample is needed
{
offset = 0,
size = reader.ReadUInt32(),
path = reader.ReadAlignedString()
};
}
m_TextureFormat = (TextureFormat)reader.ReadInt32();
if (version.IsTuanjie && version >= (2022, 3, 62)) //2022.3.62t1(1.7.0) and up
{
var m_TextureManagerMultiFormatSettingSize = reader.ReadInt32();
reader.Position += m_TextureManagerMultiFormatSettingSize; //skip byte[] m_TextureManagerMultiFormatSetting
reader.AlignStream();
}
if (version < (5, 2)) //5.2 down
{
m_MipMap = reader.ReadBoolean();
@@ -167,19 +187,19 @@ namespace AssetStudio
{
case TextureFormat.ASTC_RGBA_5x5:
// https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_astc/
imgDataSize = (int)(Math.Floor((m_Width + 4) / 5f) * Math.Floor((m_Height + 4) / 5f) * 16);
imgDataSize = (int)(MathF.Floor((m_Width + 4) / 5f) * MathF.Floor((m_Height + 4) / 5f) * 16);
break;
case TextureFormat.ASTC_RGBA_6x6:
imgDataSize = (int)(Math.Floor((m_Width + 5) / 6f) * Math.Floor((m_Height + 5) / 6f) * 16);
imgDataSize = (int)(MathF.Floor((m_Width + 5) / 6f) * MathF.Floor((m_Height + 5) / 6f) * 16);
break;
case TextureFormat.ASTC_RGBA_8x8:
imgDataSize = (int)(Math.Floor((m_Width + 7) / 8f) * Math.Floor((m_Height + 7) / 8f) * 16);
imgDataSize = (int)(MathF.Floor((m_Width + 7) / 8f) * MathF.Floor((m_Height + 7) / 8f) * 16);
break;
case TextureFormat.ASTC_RGBA_10x10:
imgDataSize = (int)(Math.Floor((m_Width + 9) / 10f) * Math.Floor((m_Height + 9) / 10f) * 16);
imgDataSize = (int)(MathF.Floor((m_Width + 9) / 10f) * MathF.Floor((m_Height + 9) / 10f) * 16);
break;
case TextureFormat.ASTC_RGBA_12x12:
imgDataSize = (int)(Math.Floor((m_Width + 11) / 12f) * Math.Floor((m_Height + 11) / 12f) * 16);
imgDataSize = (int)(MathF.Floor((m_Width + 11) / 12f) * MathF.Floor((m_Height + 11) / 12f) * 16);
break;
case TextureFormat.DXT1:
case TextureFormat.EAC_R:

View File

@@ -1,6 +1,6 @@
using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio
{
@@ -14,6 +14,7 @@ namespace AssetStudio
public uint m_DataSize;
public GLTextureSettings m_TextureSettings;
public int m_ColorSpace;
[JsonPropertyName("image data")]
public ResourceReader image_data;
public StreamingInfo m_StreamData;
public List<Texture2D> TextureList;
@@ -22,23 +23,46 @@ namespace AssetStudio
public Texture2DArray(ObjectReader reader) : base(reader)
{
m_ColorSpace = reader.ReadInt32();
m_Format = (GraphicsFormat)reader.ReadInt32();
if (version >= 2019) //2019 and up
{
m_ColorSpace = reader.ReadInt32();
m_Format = (GraphicsFormat)reader.ReadInt32();
}
m_Width = reader.ReadInt32();
m_Height = reader.ReadInt32();
m_Depth = reader.ReadInt32();
if (version < 2019) //2019 down
{
m_Format = (GraphicsFormat)reader.ReadInt32();
}
m_MipCount = reader.ReadInt32();
if (version >= (2023, 2)) //2023.2 and up
{
var m_MipsStripped = reader.ReadInt32();
}
m_DataSize = reader.ReadUInt32();
m_TextureSettings = new GLTextureSettings(reader);
if (version < 2019) //2019 down
{
m_ColorSpace = reader.ReadInt32();
}
if (version >= (2020, 2)) //2020.2 and up
{
var m_UsageMode = reader.ReadInt32();
}
var m_IsReadable = reader.ReadBoolean();
reader.AlignStream();
if (version > (2023, 2)) //2023.2 and up
{
var m_IgnoreMipmapLimit = reader.ReadBoolean();
reader.AlignStream();
var m_MipmapLimitGroupName = reader.ReadAlignedString();
}
else
{
reader.AlignStream();
}
var image_data_size = reader.ReadInt32();
if (image_data_size == 0)
if (image_data_size == 0 && version >= (5, 6)) //5.6 and up
{
m_StreamData = new StreamingInfo(reader);
}
@@ -50,9 +74,9 @@ namespace AssetStudio
TextureList = new List<Texture2D>();
}
public Texture2DArray(ObjectReader reader, IDictionary typeDict) : base(reader)
public Texture2DArray(ObjectReader reader, byte[] type, JsonSerializerOptions jsonOptions) : base(reader)
{
var parsedTex2dArray = JsonConvert.DeserializeObject<Texture2DArray>(JsonConvert.SerializeObject(typeDict));
var parsedTex2dArray = JsonSerializer.Deserialize<Texture2DArray>(type, jsonOptions);
m_Width = parsedTex2dArray.m_Width;
m_Height = parsedTex2dArray.m_Height;
m_Depth = parsedTex2dArray.m_Depth;
@@ -65,7 +89,6 @@ namespace AssetStudio
image_data = !string.IsNullOrEmpty(m_StreamData?.path)
? new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size)
: new ResourceReader(reader, parsedTex2dArray.image_data.Offset, parsedTex2dArray.image_data.Size);
typeDict.Clear();
TextureList = new List<Texture2D>();
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
namespace AssetStudio
{
@@ -10,7 +7,7 @@ namespace AssetStudio
public Quaternion m_LocalRotation;
public Vector3 m_LocalPosition;
public Vector3 m_LocalScale;
public PPtr<Transform>[] m_Children;
public List<PPtr<Transform>> m_Children;
public PPtr<Transform> m_Father;
public Transform(ObjectReader reader) : base(reader)
@@ -20,10 +17,10 @@ namespace AssetStudio
m_LocalScale = reader.ReadVector3();
int m_ChildrenCount = reader.ReadInt32();
m_Children = new PPtr<Transform>[m_ChildrenCount];
for (int i = 0; i < m_ChildrenCount; i++)
m_Children = new List<PPtr<Transform>>();
for (var i = 0; i < m_ChildrenCount; i++)
{
m_Children[i] = new PPtr<Transform>(reader);
m_Children.Add(new PPtr<Transform>(reader));
}
m_Father = new PPtr<Transform>(reader);
}

View File

@@ -49,10 +49,9 @@ namespace AssetStudio
if (version >= 2020) //2020.1 and up
{
var m_VideoShadersSize = reader.ReadInt32();
var m_VideoShaders = new PPtr<Shader>[m_VideoShadersSize];
for (int i = 0; i < m_VideoShadersSize; i++)
for (var i = 0; i < m_VideoShadersSize; i++)
{
m_VideoShaders[i] = new PPtr<Shader>(reader);
var m_VideoShaders = new PPtr<Shader>(reader);
}
}
m_ExternalResources = new StreamedResource(reader);

View File

@@ -114,7 +114,8 @@ namespace AssetStudio
{1121, "m_PrefabInstance"},
{1138, "m_PrefabAsset"},
{1152, "FileSize"},
{1161, "Hash128"}
{1161, "Hash128"},
{1169, "RenderingLayerMask"},
};
}
}

133
AssetStudio/CubismMoc.cs Normal file
View File

@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace AssetStudio
{
public enum CubismSDKVersion : byte
{
V30 = 1,
V33,
V40,
V42,
V50
}
public sealed class CubismMoc : IDisposable
{
public CubismSDKVersion Version { get; }
public string VersionDescription { get; }
public float CanvasWidth { get; }
public float CanvasHeight { get; }
public float CentralPosX { get; }
public float CentralPosY { get; }
public float PixelPerUnit { get; }
public uint PartCount { get; }
public uint ParamCount { get; }
public HashSet<string> PartNames { get; }
public HashSet<string> ParamNames { get; }
private byte[] modelData;
private int modelDataSize;
private bool isBigEndian;
public CubismMoc(MonoBehaviour moc)
{
var reader = moc.reader;
reader.Reset();
reader.Position += 28; //PPtr<GameObject> m_GameObject, m_Enabled, PPtr<MonoScript>
reader.ReadAlignedString(); //m_Name
modelDataSize = (int)reader.ReadUInt32();
modelData = BigArrayPool<byte>.Shared.Rent(modelDataSize);
_ = reader.Read(modelData, 0, modelDataSize);
var sdkVer = modelData[4];
if (Enum.IsDefined(typeof(CubismSDKVersion), sdkVer))
{
Version = (CubismSDKVersion)sdkVer;
VersionDescription = ParseVersion();
}
else
{
var msg = $"Unknown SDK version ({sdkVer})";
VersionDescription = msg;
Version = 0;
Logger.Warning($"Live2D model \"{moc.m_Name}\": " + msg);
return;
}
isBigEndian = BitConverter.ToBoolean(modelData, 5);
var modelDataSpan = new ReadOnlySpan<byte>(modelData, 0, modelDataSize);
//offsets
var countInfoTableOffset = (int)modelDataSpan.ReadUInt32(64, isBigEndian);
var canvasInfoOffset = (int)modelDataSpan.ReadUInt32(68, isBigEndian);
var partIdsOffset = modelDataSpan.ReadUInt32(76, isBigEndian);
var parameterIdsOffset = modelDataSpan.ReadUInt32(264, isBigEndian);
//canvas
PixelPerUnit = modelDataSpan.ReadSingle(canvasInfoOffset, isBigEndian);
CentralPosX = modelDataSpan.ReadSingle(canvasInfoOffset + 4, isBigEndian);
CentralPosY = modelDataSpan.ReadSingle(canvasInfoOffset + 8, isBigEndian);
CanvasWidth = modelDataSpan.ReadSingle(canvasInfoOffset + 12, isBigEndian);
CanvasHeight = modelDataSpan.ReadSingle(canvasInfoOffset + 16, isBigEndian);
//model
PartCount = modelDataSpan.ReadUInt32(countInfoTableOffset, isBigEndian);
ParamCount = modelDataSpan.ReadUInt32(countInfoTableOffset + 20, isBigEndian);
PartNames = ReadMocStrings(modelDataSpan, (int)partIdsOffset, (int)PartCount);
ParamNames = ReadMocStrings(modelDataSpan, (int)parameterIdsOffset, (int)ParamCount);
}
public void SaveMoc3(string savePath)
{
if (!savePath.EndsWith(".moc3"))
savePath += ".moc3";
using (var file = File.OpenWrite(savePath))
{
file.Write(modelData, 0, modelDataSize);
}
}
private string ParseVersion()
{
switch (Version)
{
case CubismSDKVersion.V30: return "SDK3.0/Cubism3.0(3.2)";
case CubismSDKVersion.V33: return "SDK3.3/Cubism3.3";
case CubismSDKVersion.V40: return "SDK4.0/Cubism4.0";
case CubismSDKVersion.V42: return "SDK4.2/Cubism4.2";
case CubismSDKVersion.V50: return "SDK5.0/Cubism5.0";
default: return "";
}
}
private static HashSet<string> ReadMocStrings(ReadOnlySpan<byte> data, int index, int count)
{
const int strLen = 64;
var strHashSet = new HashSet<string>();
for (var i = 0; i < count; i++)
{
if (index + i * strLen <= data.Length)
{
var str = data.Slice(index + i * strLen, strLen).ReadStringToNull();
strHashSet.Add(str);
}
}
return strHashSet;
}
private void Dispose(bool disposing)
{
if (disposing)
{
BigArrayPool<byte>.Shared.Return(modelData, clearArray: true);
}
}
public void Dispose()
{
Dispose(true);
}
}
}

View File

@@ -1,149 +1,31 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using static AssetStudio.EndianSpanReader;
using System.Collections.Generic;
namespace AssetStudio
{
public enum CubismSDKVersion : byte
public class CubismModel
{
V30 = 1,
V33,
V40,
V42,
V50
}
public string Name { get; set; }
public string Container { get; set; }
public MonoBehaviour CubismModelMono { get; set; }
public MonoBehaviour PhysicsController { get; set; }
public MonoBehaviour FadeController { get; set; }
public MonoBehaviour ExpressionController { get; set; }
public List<MonoBehaviour> RenderTextureList { get; set; }
public List<MonoBehaviour> ParamDisplayInfoList { get; set; }
public List<MonoBehaviour> PartDisplayInfoList { get; set; }
public List<MonoBehaviour> PosePartList { get; set; }
public List<AnimationClip> ClipMotionList { get; set; }
public GameObject ModelGameObject { get; set; }
public sealed class CubismModel : IDisposable
{
public CubismSDKVersion Version { get; }
public string VersionDescription { get; }
public float CanvasWidth { get; }
public float CanvasHeight { get; }
public float CentralPosX { get; }
public float CentralPosY { get; }
public float PixelPerUnit { get; }
public uint PartCount { get; }
public uint ParamCount { get; }
public HashSet<string> PartNames { get; }
public HashSet<string> ParamNames { get; }
private byte[] modelData;
private int modelDataSize;
private bool isBigEndian;
public CubismModel(MonoBehaviour moc)
public CubismModel(GameObject m_GameObject)
{
var reader = moc.reader;
reader.Reset();
reader.Position += 28; //PPtr<GameObject> m_GameObject, m_Enabled, PPtr<MonoScript>
reader.ReadAlignedString(); //m_Name
modelDataSize = (int)reader.ReadUInt32();
modelData = BigArrayPool<byte>.Shared.Rent(modelDataSize);
_ = reader.Read(modelData, 0, modelDataSize);
var sdkVer = modelData[4];
if (Enum.IsDefined(typeof(CubismSDKVersion), sdkVer))
{
Version = (CubismSDKVersion)sdkVer;
VersionDescription = ParseVersion();
}
else
{
var msg = $"Unknown SDK version ({sdkVer})";
VersionDescription = msg;
Version = 0;
Logger.Warning($"Live2D model \"{moc.m_Name}\": " + msg);
return;
}
isBigEndian = BitConverter.ToBoolean(modelData, 5);
//offsets
var countInfoTableOffset = (int)SpanToUint32(modelData, 64, isBigEndian);
var canvasInfoOffset = (int)SpanToUint32(modelData, 68, isBigEndian);
var partIdsOffset = SpanToUint32(modelData, 76, isBigEndian);
var parameterIdsOffset = SpanToUint32(modelData, 264, isBigEndian);
//canvas
PixelPerUnit = ToSingle(modelData, canvasInfoOffset, isBigEndian);
CentralPosX = ToSingle(modelData, canvasInfoOffset + 4, isBigEndian);
CentralPosY = ToSingle(modelData, canvasInfoOffset + 8, isBigEndian);
CanvasWidth = ToSingle(modelData, canvasInfoOffset + 12, isBigEndian);
CanvasHeight = ToSingle(modelData, canvasInfoOffset + 16, isBigEndian);
//model
PartCount = SpanToUint32(modelData, countInfoTableOffset, isBigEndian);
ParamCount = SpanToUint32(modelData, countInfoTableOffset + 20, isBigEndian);
PartNames = ReadMocStringHashSet(modelData, (int)partIdsOffset, (int)PartCount);
ParamNames = ReadMocStringHashSet(modelData, (int)parameterIdsOffset, (int)ParamCount);
}
public void SaveMoc3(string savePath)
{
if (!savePath.EndsWith(".moc3"))
savePath += ".moc3";
using (var file = File.OpenWrite(savePath))
{
file.Write(modelData, 0, modelDataSize);
}
}
private string ParseVersion()
{
switch (Version)
{
case CubismSDKVersion.V30:
return "SDK3.0/Cubism3.0(3.2)";
case CubismSDKVersion.V33:
return "SDK3.3/Cubism3.3";
case CubismSDKVersion.V40:
return "SDK4.0/Cubism4.0";
case CubismSDKVersion.V42:
return "SDK4.2/Cubism4.2";
case CubismSDKVersion.V50:
return "SDK5.0/Cubism5.0";
default:
return "";
}
}
private static float ToSingle(ReadOnlySpan<byte> data, int index, bool isBigEndian) //net framework ver
{
var bytes = data.Slice(index, index + 4).ToArray();
if ((isBigEndian && BitConverter.IsLittleEndian) || (!isBigEndian && !BitConverter.IsLittleEndian))
(bytes[0], bytes[1], bytes[2], bytes[3]) = (bytes[3], bytes[2], bytes[1], bytes[0]);
return BitConverter.ToSingle(bytes, 0);
}
private static HashSet<string> ReadMocStringHashSet(ReadOnlySpan<byte> data, int index, int count)
{
const int strLen = 64;
var strHashSet = new HashSet<string>();
for (var i = 0; i < count; i++)
{
if (index + i * strLen <= data.Length)
{
var buff = data.Slice(index + i * strLen, strLen);
strHashSet.Add(Encoding.UTF8.GetString(buff.ToArray()).TrimEnd('\0'));
}
}
return strHashSet;
}
private void Dispose(bool disposing)
{
if (disposing)
{
BigArrayPool<byte>.Shared.Return(modelData, clearArray: true);
}
}
public void Dispose()
{
Dispose(true);
Name = m_GameObject.m_Name;
ModelGameObject = m_GameObject;
RenderTextureList = new List<MonoBehaviour>();
ParamDisplayInfoList = new List<MonoBehaviour>();
PartDisplayInfoList = new List<MonoBehaviour>();
PosePartList = new List<MonoBehaviour>();
ClipMotionList = new List<AnimationClip>();
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Buffers.Binary;
namespace AssetStudio.CustomOptions.Asmo
{
public class OptionsFile
{
private static readonly byte[] OptionsFileSign = new byte[] { 0x41, 0x53, 0x4D, 0x4F }; //ASMO
public static readonly string Extension = ".asmo";
public byte[] Signature { get; private set; } = OptionsFileSign;
public short Version { get; private set; } = 1;
public short Reserved { get; set; }
public uint DataCrc { get; set; }
public int DataSize { get; set; }
public byte[] Data { get; set; }
public OptionsFile() { }
public OptionsFile(EndianBinaryReader reader)
{
Signature = reader.ReadBytes(4);
Version = reader.ReadInt16();
Reserved = reader.ReadInt16();
DataCrc = reader.ReadUInt32();
DataSize = reader.ReadInt32();
CheckHeader(reader.BaseStream.Length);
Data = reader.ReadBytes(DataSize);
}
public void CheckHeader(long fileLength)
{
if (!Signature.AsSpan().SequenceEqual(OptionsFileSign))
throw new NotSupportedException("Incorrect options file signature.");
if (Version != 1)
throw new NotSupportedException("Incorrect options file version.");
if (DataSize <= 0 || DataSize > fileLength)
throw new NotSupportedException("Incorrect data size.");
}
public byte[] ToByteArray()
{
var buffer = new byte[16 + DataSize];
OptionsFileSign.AsSpan().CopyTo(buffer);
BinaryPrimitives.WriteInt16BigEndian(buffer.AsSpan(4), Version);
BinaryPrimitives.WriteInt16BigEndian(buffer.AsSpan(6), Reserved);
BinaryPrimitives.WriteUInt32BigEndian(buffer.AsSpan(8), DataCrc);
BinaryPrimitives.WriteInt32BigEndian(buffer.AsSpan(12), DataSize);
Data.AsSpan().CopyTo(buffer.AsSpan(16));
return buffer;
}
}
}

View File

@@ -0,0 +1,40 @@
namespace AssetStudio.CustomOptions
{
public class CustomBundleOptions
{
private CompressionType _customBlockCompression = CompressionType.Auto;
private CompressionType _customBlockInfoCompression = CompressionType.Auto;
private bool _decompressToDisk;
public ImportOptions Options;
public CompressionType CustomBlockCompression
{
get => _customBlockCompression;
set => _customBlockCompression = SetOption(nameof(CustomBlockCompression), value);
}
public CompressionType CustomBlockInfoCompression
{
get => _customBlockInfoCompression;
set => _customBlockInfoCompression = SetOption(nameof(CustomBlockInfoCompression), value);
}
public bool DecompressToDisk
{
get => _decompressToDisk;
set => _decompressToDisk = SetOption(nameof(DecompressToDisk), value);
}
public CustomBundleOptions() { }
public CustomBundleOptions(ImportOptions importOptions)
{
Options = importOptions;
}
private static T SetOption<T>(string option, T value)
{
Logger.Info($"- {option}: {value}");
return value;
}
}
}

View File

@@ -0,0 +1,94 @@
using AssetStudio.CustomOptions.Asmo;
using SevenZip;
using System;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio.CustomOptions
{
public class ImportOptions
{
private static JsonSerializerOptions jsonOptions;
private static string fileName = "ImportOptions";
private UnityVersion _customUnityVer;
public CustomBundleOptions BundleOptions { get; set; }
public UnityVersion CustomUnityVersion { get => _customUnityVer; set => SetUnityVersion(value); }
static ImportOptions()
{
jsonOptions = new JsonSerializerOptions
{
ReferenceHandler = ReferenceHandler.Preserve,
IncludeFields = true,
};
}
public ImportOptions()
{
BundleOptions = new CustomBundleOptions(this);
CustomUnityVersion = null;
}
public static ImportOptions FromOptionsFile(OptionsFile optionsFile)
{
if (optionsFile.Reserved != 0)
{
Logger.Debug("Skipped. Not an import options file.");
return null;
}
var utf8Bytes = Convert.FromBase64String(Encoding.ASCII.GetString(optionsFile.Data));
var dataCrc = CRC.CalculateDigest(utf8Bytes, 0, (uint)utf8Bytes.Length);
if (optionsFile.DataCrc != dataCrc)
throw new IOException("Options file data is corrupted.");
return JsonSerializer.Deserialize<ImportOptions>(utf8Bytes, jsonOptions);
}
public void SaveToFile(string outputFolder)
{
var utf8Bytes = JsonSerializer.SerializeToUtf8Bytes(this, jsonOptions);
var dataCrc = CRC.CalculateDigest(utf8Bytes, 0, (uint)utf8Bytes.Length);
var base64String = Convert.ToBase64String(utf8Bytes);
var optionsFile = new OptionsFile
{
DataCrc = dataCrc,
DataSize = base64String.Length,
Data = Encoding.ASCII.GetBytes(base64String),
};
var unityVer = _customUnityVer != null
? $"_{_customUnityVer}"
: "";
var path = Path.Combine(outputFolder, $"{fileName}{unityVer}{OptionsFile.Extension}");
File.WriteAllBytes(path, optionsFile.ToByteArray());
Logger.Info($"Options file saved to \"{path}\"");
}
private void SetUnityVersion(UnityVersion value)
{
if (_customUnityVer == value)
return;
if (value == null)
{
_customUnityVer = null;
Logger.Info("- Specified Unity version: None");
return;
}
if (string.IsNullOrEmpty(value.BuildType))
{
throw new NotSupportedException("Specified Unity version is not in a correct format.\n" +
"Specify full Unity version, including letters at the end.\n" +
"Example: 2017.4.39f1");
}
_customUnityVer = value;
Logger.Info($"- Specified Unity version: {_customUnityVer}");
}
}
}

View File

@@ -82,12 +82,13 @@ namespace AssetStudio
return base.ReadUInt64();
}
#if NETFRAMEWORK
public override float ReadSingle()
{
if (Endian == EndianType.BigEndian)
{
Read(buffer, 0, 4);
Array.Reverse(buffer, 0, 4);
buffer.AsSpan(0, 4).Reverse();
return BitConverter.ToSingle(buffer, 0);
}
return base.ReadSingle();
@@ -98,10 +99,31 @@ namespace AssetStudio
if (Endian == EndianType.BigEndian)
{
Read(buffer, 0, 8);
Array.Reverse(buffer);
buffer.AsSpan().Reverse();
return BitConverter.ToDouble(buffer, 0);
}
return base.ReadDouble();
}
#else
public override float ReadSingle()
{
if (Endian == EndianType.BigEndian)
{
Read(buffer, 0, 4);
return BinaryPrimitives.ReadSingleBigEndian(buffer);
}
return base.ReadSingle();
}
public override double ReadDouble()
{
if (Endian == EndianType.BigEndian)
{
Read(buffer, 0, 8);
return BinaryPrimitives.ReadDoubleBigEndian(buffer);
}
return base.ReadDouble();
}
#endif
}
}

View File

@@ -1,29 +1,95 @@
using System;
using System.Buffers.Binary;
using System.Text;
namespace AssetStudio
{
public static class EndianSpanReader
{
public static uint SpanToUint32(Span<byte> data, int start, bool isBigEndian)
public static uint ReadUInt32(this Span<byte> data, int start, bool isBigEndian)
{
return ReadUInt32((ReadOnlySpan<byte>)data, start, isBigEndian);
}
public static uint ReadUInt32(this ReadOnlySpan<byte> data, int start, bool isBigEndian)
{
return isBigEndian
? BinaryPrimitives.ReadUInt32BigEndian(data.Slice(start))
: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(start));
}
public static uint SpanToUint16(Span<byte> data, int start, bool isBigEndian)
public static long ReadUInt16(this Span<byte> data, int start, bool isBigEndian)
{
return ReadUInt16((ReadOnlySpan<byte>)data, start, isBigEndian);
}
public static uint ReadUInt16(this ReadOnlySpan<byte> data, int start, bool isBigEndian)
{
return isBigEndian
? BinaryPrimitives.ReadUInt16BigEndian(data.Slice(start))
: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(start));
}
public static long SpanToInt64(Span<byte> data, int start, bool isBigEndian)
public static long ReadInt64(this Span<byte> data, int start, bool isBigEndian)
{
return ReadInt64((ReadOnlySpan<byte>)data, start, isBigEndian);
}
public static long ReadInt64(this ReadOnlySpan<byte> data, int start, bool isBigEndian)
{
return isBigEndian
? BinaryPrimitives.ReadInt64BigEndian(data.Slice(start))
: BinaryPrimitives.ReadInt64LittleEndian(data.Slice(start));
}
public static float ReadSingle(this Span<byte> data, int start, bool isBigEndian)
{
return ReadSingle((ReadOnlySpan<byte>)data, start, isBigEndian);
}
#if NETFRAMEWORK
public static float ReadSingle(this ReadOnlySpan<byte> data, int start, bool isBigEndian)
{
var bytes = data.Slice(start, 4).ToArray();
if ((isBigEndian && BitConverter.IsLittleEndian) || (!isBigEndian && !BitConverter.IsLittleEndian))
bytes.AsSpan().Reverse();
return BitConverter.ToSingle(bytes, 0);
}
#else
public static float ReadSingle(this ReadOnlySpan<byte> data, int start, bool isBigEndian)
{
return isBigEndian
? BinaryPrimitives.ReadSingleBigEndian(data[start..])
: BinaryPrimitives.ReadSingleLittleEndian(data[start..]);
}
#endif
public static string ReadStringToNull(this Span<byte> data, int maxLength = 32767)
{
return ReadStringToNull((ReadOnlySpan<byte>)data, maxLength);
}
public static string ReadStringToNull(this ReadOnlySpan<byte> data, int maxLength = 32767)
{
Span<byte> bytes = stackalloc byte[maxLength];
var count = 0;
while (count != data.Length && count < maxLength)
{
var b = data[count];
if (b == 0)
{
break;
}
bytes[count] = b;
count++;
}
bytes = bytes.Slice(0, count);
#if NETFRAMEWORK
return Encoding.UTF8.GetString(bytes.ToArray());
#else
return Encoding.UTF8.GetString(bytes);
#endif
}
}
}

View File

@@ -1,18 +1,14 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
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)
public static void AlignStream(this BinaryReader reader, int alignment = 4)
{
var pos = reader.BaseStream.Position;
var mod = pos % alignment;
@@ -25,11 +21,13 @@ namespace AssetStudio
public static string ReadAlignedString(this BinaryReader reader)
{
var length = reader.ReadInt32();
if (length > 0 && length <= reader.BaseStream.Length - reader.BaseStream.Position)
if (length > reader.BaseStream.Length - reader.BaseStream.Position)
throw new EndOfStreamException();
if (length > 0)
{
var stringData = reader.ReadBytes(length);
var result = Encoding.UTF8.GetString(stringData);
reader.AlignStream(4);
reader.AlignStream();
return result;
}
return "";
@@ -53,7 +51,11 @@ namespace AssetStudio
count++;
}
bytes = bytes.Slice(0, count);
#if NETFRAMEWORK
return encoding?.GetString(bytes.ToArray()) ?? Encoding.UTF8.GetString(bytes.ToArray());
#else
return encoding?.GetString(bytes) ?? Encoding.UTF8.GetString(bytes);
#endif
}
private static string ReadUnicodeStringToNull(this BinaryReader reader, int maxLength)
@@ -98,24 +100,22 @@ namespace AssetStudio
return new Color(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
}
public static Matrix4x4 ReadMatrix(this BinaryReader reader)
private static T[] ReadArray<T>(BinaryReader reader, int byteLen) where T : struct
{
return new Matrix4x4(reader.ReadSingleArray(16));
}
if (byteLen < 0)
throw new ArgumentOutOfRangeException(nameof(byteLen));
if (reader.BaseStream.Position + byteLen > reader.BaseStream.Length)
throw new EndOfStreamException();
var bytes = reader.ReadBytes(byteLen);
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;
var span = MemoryMarshal.Cast<byte, T>(bytes);
return span.ToArray();
}
public static bool[] ReadBooleanArray(this BinaryReader reader)
{
return ReadArray(reader.ReadBoolean, reader.ReadInt32());
return ReadArray<bool>(reader, reader.ReadInt32());
}
public static byte[] ReadUInt8Array(this BinaryReader reader)
@@ -125,62 +125,65 @@ namespace AssetStudio
public static ushort[] ReadUInt16Array(this BinaryReader reader)
{
return ReadArray(reader.ReadUInt16, reader.ReadInt32());
return ReadArray<ushort>(reader, reader.ReadInt32() * 2);
}
public static int[] ReadInt32Array(this BinaryReader reader)
public static int[] ReadInt32Array(this BinaryReader reader, int length = -1)
{
return ReadArray(reader.ReadInt32, reader.ReadInt32());
if (length == -1)
length = reader.ReadInt32();
return ReadArray<int>(reader, length * 4);
}
public static int[] ReadInt32Array(this BinaryReader reader, int length)
public static uint[] ReadUInt32Array(this BinaryReader reader, int length = -1)
{
return ReadArray(reader.ReadInt32, length);
}
public static uint[] ReadUInt32Array(this BinaryReader reader)
{
return ReadArray(reader.ReadUInt32, reader.ReadInt32());
if (length == -1)
length = reader.ReadInt32();
return ReadArray<uint>(reader, length * 4);
}
public static uint[][] ReadUInt32ArrayArray(this BinaryReader reader)
{
return ReadArray(reader.ReadUInt32Array, reader.ReadInt32());
var length = reader.ReadInt32();
var list = new List<uint[]>();
for (var i = 0; i < length; i++)
{
list.Add(ReadArray<uint>(reader, reader.ReadInt32() * 4));
}
return list.ToArray();
}
public static uint[] ReadUInt32Array(this BinaryReader reader, int length)
public static float[] ReadSingleArray(this BinaryReader reader, int length = -1)
{
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);
if (length == -1)
length = reader.ReadInt32();
return ReadArray<float>(reader, length * 4);
}
public static string[] ReadStringArray(this BinaryReader reader)
{
return ReadArray(reader.ReadAlignedString, reader.ReadInt32());
var length = reader.ReadInt32();
var list = new List<string>();
for (var i = 0; i < length; i++)
{
list.Add(reader.ReadAlignedString());
}
return list.ToArray();
}
public static Vector2[] ReadVector2Array(this BinaryReader reader)
{
return ReadArray(reader.ReadVector2, reader.ReadInt32());
return ReadArray<Vector2>(reader, reader.ReadInt32() * 8);
}
public static Vector4[] ReadVector4Array(this BinaryReader reader)
{
return ReadArray(reader.ReadVector4, reader.ReadInt32());
return ReadArray<Vector4>(reader, reader.ReadInt32() * 16);
}
public static Matrix4x4[] ReadMatrixArray(this BinaryReader reader)
{
return ReadArray(reader.ReadMatrix, reader.ReadInt32());
return ReadArray<Matrix4x4>(reader, reader.ReadInt32() * 64);
}
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.IO;
using static AssetStudio.EndianSpanReader;
namespace AssetStudio
{
@@ -14,6 +13,9 @@ namespace AssetStudio
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[] unityFsMagic = {0x55, 0x6E, 0x69, 0x74, 0x79, 0x46, 0x53, 0x00};
private static readonly int headerBuffLen = 1152;
private static byte[] headerBuff = new byte[headerBuffLen];
public FileReader(string path) : this(path, File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { }
@@ -26,30 +28,34 @@ namespace AssetStudio
private FileType CheckFileType()
{
var signature = this.ReadStringToNull(20);
var buff = headerBuff.AsSpan();
buff.Clear();
var dataLen = Read(headerBuff, 0, headerBuffLen);
Position = 0;
var signature = buff.ReadStringToNull(20);
switch (signature)
{
case "UnityWeb":
case "UnityRaw":
case "UnityArchive":
case "UnityFS":
CheckBundleDataOffset(buff);
return FileType.BundleFile;
case "UnityWebData1.0":
case "TuanjieWebData1.0":
return FileType.WebFile;
default:
{
var buff = ReadBytes(40).AsSpan();
var magic = Span<byte>.Empty;
Position = 0;
magic = buff.Length > 2 ? buff.Slice(0, 2) : magic;
magic = dataLen > 2 ? buff.Slice(0, 2) : magic;
if (magic.SequenceEqual(gzipMagic))
{
return FileType.GZipFile;
}
magic = buff.Length > 38 ? buff.Slice(32, 6) : magic;
magic = dataLen > 38 ? buff.Slice(32, 6) : magic;
if (magic.SequenceEqual(brotliMagic))
{
return FileType.BrotliFile;
@@ -60,18 +66,23 @@ namespace AssetStudio
return FileType.AssetsFile;
}
magic = buff.Length > 4 ? buff.Slice(0, 4): magic;
magic = dataLen > 4 ? buff.Slice(0, 4): magic;
if (magic.SequenceEqual(zipMagic) || magic.SequenceEqual(zipSpannedMagic))
{
return FileType.ZipFile;
}
if (CheckBundleDataOffset(buff))
{
return FileType.BundleFile;
}
return FileType.ResourceFile;
}
}
}
private bool IsSerializedFile(Span<byte> buff)
private bool IsSerializedFile(ReadOnlySpan<byte> buff)
{
var fileSize = BaseStream.Length;
if (fileSize < 20)
@@ -80,10 +91,10 @@ namespace AssetStudio
}
var isBigEndian = Endian == EndianType.BigEndian;
//var m_MetadataSize = SpanToUint32(buff, 0, isBigEndian);
long m_FileSize = SpanToUint32(buff, 4, isBigEndian);
var m_Version = SpanToUint32(buff, 8, isBigEndian);
long m_DataOffset = SpanToUint32(buff, 12, isBigEndian);
//var m_MetadataSize = buff.ReadUInt32(0, isBigEndian);
long m_FileSize = buff.ReadUInt32(4, isBigEndian);
var m_Version = buff.ReadUInt32(8, isBigEndian);
long m_DataOffset = buff.ReadUInt32(12, isBigEndian);
//var m_Endianess = buff[16];
//var m_Reserved = buff.Slice(17, 3);
if (m_Version >= 22)
@@ -92,15 +103,42 @@ namespace AssetStudio
{
return false;
}
//m_MetadataSize = SpanToUint32(buff, 20, isBigEndian);
m_FileSize = SpanToInt64(buff, 24, isBigEndian);
m_DataOffset = SpanToInt64(buff, 32, isBigEndian);
//m_MetadataSize = buff.ReadUInt32(20, isBigEndian);
m_FileSize = buff.ReadInt64(24, isBigEndian);
m_DataOffset = buff.ReadInt64(32, isBigEndian);
}
if (m_FileSize != fileSize || m_DataOffset > fileSize)
{
return false;
}
return true;
}
private bool CheckBundleDataOffset(ReadOnlySpan<byte> buff)
{
var lastOffset = buff.LastIndexOf(unityFsMagic);
if (lastOffset <= 0)
return false;
var firstOffset = buff.IndexOf(unityFsMagic);
if (firstOffset == lastOffset || lastOffset - firstOffset < 200)
{
Position = lastOffset;
return true;
}
var pos = firstOffset + 12;
pos += buff.Slice(pos).ReadStringToNull().Length + 1;
pos += buff.Slice(pos).ReadStringToNull().Length + 1;
var bundleSize = buff.ReadInt64(pos, Endian == EndianType.BigEndian);
if (bundleSize > 200 && firstOffset + bundleSize < lastOffset)
{
Position = firstOffset;
return true;
}
Position = lastOffset;
return true;
}
}

View File

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AssetStudio
namespace AssetStudio
{
public enum FileType
{

View File

@@ -68,7 +68,7 @@ namespace AssetStudio
}
catch (System.Exception e)
{
Logger.Warning($"Error while decompressing gzip file {reader.FullPath}\r\n{e}");
Logger.Warning($"Error while decompressing Gzip file {reader.FullPath}\n{e}");
reader.Dispose();
return null;
}
@@ -76,15 +76,24 @@ namespace AssetStudio
public static FileReader DecompressBrotli(FileReader reader)
{
using (reader)
try
{
var stream = new MemoryStream();
using (var brotliStream = new BrotliInputStream(reader.BaseStream))
using (reader)
{
brotliStream.CopyTo(stream);
var stream = new MemoryStream();
using (var brotliStream = new BrotliInputStream(reader.BaseStream))
{
brotliStream.CopyTo(stream);
}
stream.Position = 0;
return new FileReader(reader.FullPath, stream);
}
stream.Position = 0;
return new FileReader(reader.FullPath, stream);
}
catch (System.Exception e)
{
Logger.Warning($"Error while decompressing Brotli file {reader.FullPath}\n{e}");
reader.Dispose();
return null;
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio
{
public static partial class JsonConverterHelper
{
public class ByteArrayConverter : JsonConverter<byte[]>
{
public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.TokenType == JsonTokenType.StartArray
? JsonSerializer.Deserialize<List<byte>>(ref reader).ToArray() //JsonArray to ByteArray
: JsonSerializer.Deserialize<byte[]>(ref reader);
}
public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options)
{
writer.WriteBase64StringValue(value);
}
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio
{
public static partial class JsonConverterHelper
{
public class FloatConverter : JsonConverter<float>
{
public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<float>(ref reader, new JsonSerializerOptions
{
NumberHandling = options.NumberHandling
});
}
public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options)
{
if (float.IsNaN(value) || float.IsInfinity(value))
{
if (options.NumberHandling == JsonNumberHandling.AllowNamedFloatingPointLiterals)
{
writer.WriteStringValue($"{value.ToString(CultureInfo.InvariantCulture)}");
}
else
{
writer.WriteStringValue(JsonSerializer.Serialize(value));
}
}
else
{
writer.WriteNumberValue((decimal)value + 0.0m);
}
}
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio
{
public static partial class JsonConverterHelper
{
public class KVPConverter : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
return false;
var generic = typeToConvert.GetGenericTypeDefinition();
return generic == typeof(KeyValuePair<,>);
}
public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options)
{
var kvpArgs = type.GetGenericArguments();
return (JsonConverter)Activator.CreateInstance(typeof(KVPConverter<,>).MakeGenericType(kvpArgs));
}
}
private class KVPConverter<TKey, TValue> : JsonConverter<KeyValuePair<TKey, TValue>>
{
public override KeyValuePair<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
//startKvpObject
reader.Read(); //propName
reader.Read(); //keyType
var key = reader.TokenType == JsonTokenType.StartObject
? JsonSerializer.Deserialize<Dictionary<string, TKey>>(ref reader).Values.First()
: JsonSerializer.Deserialize<TKey>(ref reader);
reader.Read(); //propName
reader.Read(); //startObject
var value = JsonSerializer.Deserialize<TValue>(ref reader, options);
reader.Read(); //endKvpObject
return new KeyValuePair<TKey, TValue>(key, value);
}
public override void Write(Utf8JsonWriter writer, KeyValuePair<TKey, TValue> value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio
{
public static partial class JsonConverterHelper
{
public static SerializedFile AssetsFile { get; set; }
public class PPtrConverter : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
return false;
var generic = typeToConvert.GetGenericTypeDefinition();
return generic == typeof(PPtr<>);
}
public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options)
{
var elementType = type.GetGenericArguments()[0];
var converter = (JsonConverter)Activator.CreateInstance(typeof(PPtrConverter<>).MakeGenericType(elementType));
return converter;
}
}
private class PPtrConverter<T> : JsonConverter<PPtr<T>> where T : Object
{
public override PPtr<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var pptrObj = JsonSerializer.Deserialize<PPtr<T>>(ref reader, new JsonSerializerOptions { IncludeFields = true });
pptrObj.AssetsFile = AssetsFile;
return pptrObj;
}
public override void Write(Utf8JsonWriter writer, PPtr<T> value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace AssetStudio
{
public static partial class JsonConverterHelper
{
public class RenderDataMapConverter : JsonConverter<Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData>>
{
public override Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var dataArray = JsonSerializer.Deserialize<KeyValuePair<JsonObject, SpriteAtlasData>[]>(ref reader, options);
var renderDataMap = new Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData>(dataArray.Length);
foreach (var kvp in dataArray)
{
var jsonFirst = kvp.Key["first"];
var first = jsonFirst.Deserialize<GUID>(options).Convert();
var second = (long) kvp.Key["second"];
renderDataMap.Add(new KeyValuePair<Guid, long>(first, second), kvp.Value);
}
return renderDataMap;
}
public override void Write(Utf8JsonWriter writer, Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData> value, JsonSerializerOptions options)
{
var jsonDict = new Dictionary<string, SpriteAtlasData>();
foreach (var kv in value)
{
var strKey = $"{kv.Key.Key}, {kv.Key.Value}";
jsonDict.Add(strKey, kv.Value);
}
var strValue = JsonSerializer.SerializeToUtf8Bytes(jsonDict, options);
writer.WriteRawValue(strValue);
}
}
private class GUID
{
[JsonPropertyName("data[0]")] public uint data0 { get; set; }
[JsonPropertyName("data[1]")] public uint data1 { get; set; }
[JsonPropertyName("data[2]")] public uint data2 { get; set; }
[JsonPropertyName("data[3]")] public uint data3 { get; set; }
public Guid Convert()
{
var guidBytes = new byte[16];
BitConverter.GetBytes(data0).CopyTo(guidBytes, 0);
BitConverter.GetBytes(data1).CopyTo(guidBytes, 4);
BitConverter.GetBytes(data2).CopyTo(guidBytes, 8);
BitConverter.GetBytes(data3).CopyTo(guidBytes, 12);
return new Guid(guidBytes);
}
}
}
}

18
AssetStudio/Math/Clamp.cs Normal file
View File

@@ -0,0 +1,18 @@
using System;
using System.Runtime.CompilerServices;
namespace AssetStudio
{
public static partial class MathHelper
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Clamp(float value, float minValue, float maxValue)
{
#if NETFRAMEWORK
return Math.Max(minValue, Math.Min(value, maxValue));
#else
return Math.Clamp(value, minValue, maxValue);
#endif
}
}
}

View File

@@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Globalization;
#if NETFRAMEWORK
namespace AssetStudio
{
/// <summary>
@@ -35,27 +36,27 @@ namespace AssetStudio
/// <summary>
/// Represents the smallest positive System.Half value greater than zero. This field is constant.
/// </summary>
public static readonly Half Epsilon = Half.ToHalf(0x0001);
public static readonly Half Epsilon = HalfHelper.ToHalf(0x0001);
/// <summary>
/// Represents the largest possible value of System.Half. This field is constant.
/// </summary>
public static readonly Half MaxValue = Half.ToHalf(0x7bff);
public static readonly Half MaxValue = HalfHelper.ToHalf(0x7bff);
/// <summary>
/// Represents the smallest possible value of System.Half. This field is constant.
/// </summary>
public static readonly Half MinValue = Half.ToHalf(0xfbff);
public static readonly Half MinValue = HalfHelper.ToHalf(0xfbff);
/// <summary>
/// Represents not a number (NaN). This field is constant.
/// </summary>
public static readonly Half NaN = Half.ToHalf(0xfe00);
public static readonly Half NaN = HalfHelper.ToHalf(0xfe00);
/// <summary>
/// Represents negative infinity. This field is constant.
/// </summary>
public static readonly Half NegativeInfinity = Half.ToHalf(0xfc00);
public static readonly Half NegativeInfinity = HalfHelper.ToHalf(0xfc00);
/// <summary>
/// Represents positive infinity. This field is constant.
/// </summary>
public static readonly Half PositiveInfinity = Half.ToHalf(0x7c00);
public static readonly Half PositiveInfinity = HalfHelper.ToHalf(0x7c00);
#endregion
#region Constructors
@@ -63,7 +64,7 @@ namespace AssetStudio
/// Initializes a new instance of System.Half to the value of the specified single-precision floating-point number.
/// </summary>
/// <param name="value">The value to represent as a System.Half.</param>
public Half(float value) { this = HalfHelper.SingleToHalf(value); }
public Half(float value) { this = HalfHelper.ToHalf(value); }
/// <summary>
/// Initializes a new instance of System.Half to the value of the specified 32-bit signed integer.
/// </summary>
@@ -314,7 +315,7 @@ namespace AssetStudio
/// </summary>
/// <param name="value">A System.Half to convert.</param>
/// <returns>A single-precision floating-point number that represents the converted System.Half.</returns>
public static implicit operator float(Half value) { return (float)HalfHelper.HalfToSingle(value); }
public static implicit operator float(Half value) { return value.ToSingle(); }
/// <summary>
/// Converts a System.Half to a double-precision floating-point number.
/// </summary>
@@ -508,32 +509,6 @@ namespace AssetStudio
{
return value.value;
}
/// <summary>
/// Returns a half-precision floating point number converted from two bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A half-precision floating point number formed by two bytes beginning at startIndex.</returns>
/// <exception cref="System.ArgumentException">
/// startIndex is greater than or equal to the length of value minus 1, and is
/// less than or equal to the length of value minus 1.
/// </exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">startIndex is less than zero or greater than the length of value minus 1.</exception>
public static Half ToHalf(byte[] value, int startIndex)
{
return Half.ToHalf((ushort)BitConverter.ToInt16(value, startIndex));
}
/// <summary>
/// Returns a half-precision floating point number converted from its binary representation.
/// </summary>
/// <param name="bits">Binary representation of System.Half value</param>
/// <returns>A half-precision floating point number formed by its binary representation.</returns>
public static Half ToHalf(ushort bits)
{
return new Half { value = bits };
}
/// <summary>
/// Returns a value indicating the sign of a half-precision floating-point number.
@@ -886,3 +861,4 @@ namespace AssetStudio
#endregion
}
}
#endif

View File

@@ -1,18 +1,32 @@
using System;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
namespace AssetStudio
{
#if NET
public static class HalfHelper
{
public static Half ToHalf(ushort bits)
{
return (Half)bits;
}
public static Half ToHalf(ReadOnlySpan<byte> bytes, int startIndex)
{
return BinaryPrimitives.ReadHalfLittleEndian(bytes[startIndex..]);
}
}
#else
/// <summary>
/// Helper class for Half conversions and some low level operations.
/// This class is internally used in the Half class.
/// </summary>
/// <remarks>
/// References:
/// - Fast Half Float Conversions, Jeroen van der Zijp, link: http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
/// </remarks>
[ComVisible(false)]
internal static class HalfHelper
public static class HalfHelper
{
private static uint[] mantissaTable = GenerateMantissaTable();
private static uint[] exponentTable = GenerateExponentTable();
@@ -169,27 +183,54 @@ namespace AssetStudio
ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23]));
return Half.ToHalf(result);
}*/
public static float HalfToSingle(Half half)
/// <summary>
/// Returns a half-precision floating point number converted from two bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A half-precision floating point number formed by two bytes beginning at startIndex.</returns>
/// <exception cref="System.ArgumentException">
/// startIndex is greater than or equal to the length of value minus 1, and is
/// less than or equal to the length of value minus 1.
/// </exception>
/// <exception cref="System.ArgumentNullException">value is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">startIndex is less than zero or greater than the length of value minus 1.</exception>
public static Half ToHalf(ReadOnlySpan<byte> value, int startIndex)
{
return ToHalf((ushort)BinaryPrimitives.ReadInt16LittleEndian(value.Slice(startIndex)));
}
/// <summary>
/// Returns a half-precision floating point number converted from its binary representation.
/// </summary>
/// <param name="bits">Binary representation of System.Half value</param>
/// <returns>A half-precision floating point number formed by its binary representation.</returns>
public static Half ToHalf(ushort bits)
{
return new Half { value = bits };
}
public static Half ToHalf(float single)
{
byte[] singleBytes = BitConverter.GetBytes(single);
uint value = BitConverter.ToUInt32(singleBytes, 0);
ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23]));
return ToHalf(result);
}
public static float ToSingle(this Half half)
{
uint result = mantissaTable[offsetTable[half.value >> 10] + (half.value & 0x3ff)] + exponentTable[half.value >> 10];
byte[] uintBytes = BitConverter.GetBytes(result);
return BitConverter.ToSingle(uintBytes, 0);
}
public static Half SingleToHalf(float single)
{
byte[] singleBytes = BitConverter.GetBytes(single);
uint value = BitConverter.ToUInt32(singleBytes, 0);
ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23]));
return Half.ToHalf(result);
}
public static Half Negate(Half half)
{
return Half.ToHalf((ushort)(half.value ^ 0x8000));
return ToHalf((ushort)(half.value ^ 0x8000));
}
public static Half Abs(Half half)
{
return Half.ToHalf((ushort)(half.value & 0x7fff));
return ToHalf((ushort)(half.value & 0x7fff));
}
public static bool IsNaN(Half half)
@@ -209,4 +250,5 @@ namespace AssetStudio
return (half.value == 0xfc00);
}
}
#endif
}

View File

@@ -26,9 +26,9 @@ namespace AssetStudio
public float M23;
public float M33;
public Matrix4x4(float[] values)
public Matrix4x4(Span<float> values)
{
if (values == null)
if (values.IsEmpty)
throw new ArgumentNullException(nameof(values));
if (values.Length != 16)
throw new ArgumentOutOfRangeException(nameof(values), "There must be sixteen and only sixteen input values for Matrix.");

View File

@@ -45,9 +45,9 @@ namespace AssetStudio
public override bool Equals(object other)
{
if (!(other is Vector2))
if (!(other is Vector2 vector2))
return false;
return Equals((Vector2)other);
return Equals(vector2);
}
public bool Equals(Vector2 other)
@@ -73,7 +73,7 @@ namespace AssetStudio
public float Length()
{
return (float)Math.Sqrt(LengthSquared());
return MathF.Sqrt(LengthSquared());
}
public float LengthSquared()

View File

@@ -49,9 +49,9 @@ namespace AssetStudio
public override bool Equals(object other)
{
if (!(other is Vector3))
if (!(other is Vector3 vector3))
return false;
return Equals((Vector3)other);
return Equals(vector3);
}
public bool Equals(Vector3 other)
@@ -79,7 +79,7 @@ namespace AssetStudio
public float Length()
{
return (float)Math.Sqrt(LengthSquared());
return MathF.Sqrt(LengthSquared());
}
public float LengthSquared()

View File

@@ -61,9 +61,9 @@ namespace AssetStudio
public override bool Equals(object other)
{
if (!(other is Vector4))
if (!(other is Vector4 vector4))
return false;
return Equals((Vector4)other);
return Equals(vector4);
}
public bool Equals(Vector4 other)
@@ -93,7 +93,7 @@ namespace AssetStudio
public float Length()
{
return (float)Math.Sqrt(LengthSquared());
return MathF.Sqrt(LengthSquared());
}
public float LengthSquared()

View File

@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace AssetStudio
{
@@ -12,13 +9,15 @@ namespace AssetStudio
public long m_PathID;
public long byteStart;
public uint byteSize;
public int classID;
public ClassIDType type;
public SerializedType serializedType;
public BuildTarget platform;
public SerializedFileFormatVersion m_Version;
public UnityVersion version => assetsFile.version;
public BuildType buildType => assetsFile.buildType;
public long Remaining => byteStart + byteSize - Position;
public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo) : base(reader.BaseStream, reader.Endian)
{
@@ -26,6 +25,7 @@ namespace AssetStudio
m_PathID = objectInfo.m_PathID;
byteStart = objectInfo.byteStart;
byteSize = objectInfo.byteSize;
classID = objectInfo.classID;
if (Enum.IsDefined(typeof(ClassIDType), objectInfo.classID))
{
type = (ClassIDType)objectInfo.classID;
@@ -39,6 +39,12 @@ namespace AssetStudio
m_Version = assetsFile.header.m_Version;
}
public void ThrowIfTooLarge(float val)
{
if (val < 0 || val > Remaining)
throw new EndOfStreamException();
}
public void Reset()
{
Position = byteStart;

102
AssetStudio/OffsetStream.cs Normal file
View File

@@ -0,0 +1,102 @@
using System.IO;
namespace AssetStudio
{
public class OffsetStream : Stream
{
private readonly Stream _baseStream;
private readonly long _length;
private long _offset;
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => false;
public override long Length => _length > 0
? _length
: _baseStream.Length - _offset;
public override long Position
{
get => _baseStream.Position - _offset;
set => Seek(value, SeekOrigin.Begin);
}
public long BasePosition => _baseStream.Position;
public long Offset
{
get => _offset;
set
{
if (value < 0 || value > _baseStream.Length)
{
throw new IOException($"{nameof(Offset)} is out of stream bound");
}
_offset = value;
Seek(0, SeekOrigin.Begin);
}
}
public OffsetStream(FileReader reader)
{
_baseStream = reader.BaseStream;
Offset = reader.Position;
}
public OffsetStream(Stream stream, long offset, long length)
{
_baseStream = stream;
_length = length;
Offset = offset;
}
public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count)
{
return _baseStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
if (offset > _baseStream.Length)
{
throw new IOException("Unable to seek beyond stream bound");
}
switch (origin)
{
case SeekOrigin.Begin:
_baseStream.Seek(offset + _offset, SeekOrigin.Begin);
break;
case SeekOrigin.Current:
_baseStream.Seek(offset + Position, SeekOrigin.Begin);
break;
case SeekOrigin.End:
_baseStream.Seek(offset + _baseStream.Length, SeekOrigin.Begin);
break;
}
return Position;
}
public override void SetLength(long value)
{
throw new System.NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new System.NotImplementedException();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_baseStream.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@@ -80,18 +80,20 @@ namespace AssetStudio
lock (binaryReader)
{
binaryReader.BaseStream.Position = Offset;
return binaryReader.ReadBytes((int) size);
return binaryReader.ReadBytes((int)size);
}
}
public void GetData(byte[] buff)
public int GetData(byte[] buff, int startIndex = 0)
{
int dataLen;
var binaryReader = GetReader();
lock (binaryReader)
{
binaryReader.BaseStream.Position = Offset;
binaryReader.Read(buff, 0, (int) size);
dataLen = binaryReader.Read(buff, startIndex, (int)size);
}
return dataLen;
}
public void WriteData(string path)

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace AssetStudio
{
@@ -14,14 +12,13 @@ namespace AssetStudio
public string originalPath;
public string fileName;
public UnityVersion version = new UnityVersion();
public BuildType buildType;
public List<Object> Objects;
public Dictionary<long, Object> ObjectsDic;
public SerializedFileHeader header;
private byte m_FileEndianess;
public string unityVersion = "2.5.0f5";
public BuildTarget m_TargetPlatform = BuildTarget.UnknownPlatform;
public string targetPlatformString;
private bool m_EnableTypeTree = true;
public List<SerializedType> m_Types;
public int bigIDEnabled = 0;
@@ -72,16 +69,39 @@ namespace AssetStudio
}
if (header.m_Version >= SerializedFileFormatVersion.Unknown_7)
{
unityVersion = reader.ReadStringToNull();
SetVersion(new UnityVersion(unityVersion));
var versionPos = reader.Position;
var verStr = reader.ReadStringToNull();
if (!UnityVersion.TryParse(verStr, out version))
{
if (assetsManager.Options.CustomUnityVersion == null)
{
Logger.Warning($"Failed to parse Unity version: \"{verStr}\"");
version = new UnityVersion();
return;
}
version = assetsManager.Options.CustomUnityVersion;
reader.Position = versionPos;
reader.ReadBytes(version.ToString().Length + 1);
}
}
else
{
version = new UnityVersion(2, 5, 0);
}
if (header.m_Version >= SerializedFileFormatVersion.Unknown_8)
{
m_TargetPlatform = (BuildTarget)reader.ReadInt32();
var target = reader.ReadInt32();
m_TargetPlatform = (BuildTarget)target;
if (!Enum.IsDefined(typeof(BuildTarget), m_TargetPlatform))
{
m_TargetPlatform = BuildTarget.UnknownPlatform;
}
targetPlatformString = version.IsTuanjie && Enum.IsDefined(typeof(TuanjieBuildTarget), target)
? ((TuanjieBuildTarget)target).ToString()
: m_TargetPlatform.ToString();
}
if (header.m_Version >= SerializedFileFormatVersion.HasTypeTreeHashes)
{
@@ -90,8 +110,8 @@ namespace AssetStudio
// Read Types
int typeCount = reader.ReadInt32();
m_Types = new List<SerializedType>(typeCount);
for (int i = 0; i < typeCount; i++)
m_Types = new List<SerializedType>();
for (var i = 0; i < typeCount; i++)
{
m_Types.Add(ReadSerializedType(false));
}
@@ -103,10 +123,10 @@ namespace AssetStudio
// Read Objects
int objectCount = reader.ReadInt32();
m_Objects = new List<ObjectInfo>(objectCount);
Objects = new List<Object>(objectCount);
ObjectsDic = new Dictionary<long, Object>(objectCount);
for (int i = 0; i < objectCount; i++)
m_Objects = new List<ObjectInfo>();
Objects = new List<Object>();
ObjectsDic = new Dictionary<long, Object>();
for (var i = 0; i < objectCount; i++)
{
var objectInfo = new ObjectInfo();
if (bigIDEnabled != 0)
@@ -162,8 +182,8 @@ namespace AssetStudio
if (header.m_Version >= SerializedFileFormatVersion.HasScriptTypeIndex)
{
int scriptCount = reader.ReadInt32();
m_ScriptTypes = new List<LocalSerializedObjectIdentifier>(scriptCount);
for (int i = 0; i < scriptCount; i++)
m_ScriptTypes = new List<LocalSerializedObjectIdentifier>();
for (var i = 0; i < scriptCount; i++)
{
var m_ScriptType = new LocalSerializedObjectIdentifier();
m_ScriptType.localSerializedFileIndex = reader.ReadInt32();
@@ -181,8 +201,8 @@ namespace AssetStudio
}
int externalsCount = reader.ReadInt32();
m_Externals = new List<FileIdentifier>(externalsCount);
for (int i = 0; i < externalsCount; i++)
m_Externals = new List<FileIdentifier>();
for (var i = 0; i < externalsCount; i++)
{
var m_External = new FileIdentifier();
if (header.m_Version >= SerializedFileFormatVersion.Unknown_6)
@@ -202,8 +222,8 @@ namespace AssetStudio
if (header.m_Version >= SerializedFileFormatVersion.SupportsRefObject)
{
int refTypesCount = reader.ReadInt32();
m_RefTypes = new List<SerializedType>(refTypesCount);
for (int i = 0; i < refTypesCount; i++)
m_RefTypes = new List<SerializedType>();
for (var i = 0; i < refTypesCount; i++)
{
m_RefTypes.Add(ReadSerializedType(true));
}
@@ -217,16 +237,6 @@ namespace AssetStudio
//reader.AlignStream(16);
}
public void SetVersion(UnityVersion unityVer)
{
if (unityVer != null && !unityVer.IsStripped)
{
unityVersion = unityVer.FullVersion;
buildType = new BuildType(unityVer.BuildType);
version = unityVer;
}
}
private SerializedType ReadSerializedType(bool isRefType)
{
var type = new SerializedType();
@@ -310,7 +320,7 @@ namespace AssetStudio
}
int childrenCount = reader.ReadInt32();
for (int i = 0; i < childrenCount; i++)
for (var i = 0; i < childrenCount; i++)
{
ReadTypeTree(m_Type, level + 1);
}
@@ -320,7 +330,7 @@ namespace AssetStudio
{
int numberOfNodes = reader.ReadInt32();
int stringBufferSize = reader.ReadInt32();
for (int i = 0; i < numberOfNodes; i++)
for (var i = 0; i < numberOfNodes; i++)
{
var typeTreeNode = new TypeTreeNode();
m_Type.m_Nodes.Add(typeTreeNode);
@@ -341,7 +351,7 @@ namespace AssetStudio
using (var stringBufferReader = new BinaryReader(new MemoryStream(m_Type.m_StringBuffer)))
{
for (int i = 0; i < numberOfNodes; i++)
for (var i = 0; i < numberOfNodes; i++)
{
var m_Node = m_Type.m_Nodes[i];
m_Node.m_Type = ReadString(stringBufferReader, m_Node.m_TypeStrOffset);

View File

@@ -0,0 +1,40 @@
using System.IO;
using System.Runtime.InteropServices;
namespace AssetStudio
{
public class TempFileStream : FileStream
{
private readonly string _tempFilePath;
private bool _disposed;
public TempFileStream(string path, FileMode fileMode, FileAccess fileAccess = FileAccess.ReadWrite, FileShare fileShare = FileShare.Read, int bufferSize = 4096)
: base(path, fileMode, fileAccess, fileShare, bufferSize, FileOptions.DeleteOnClose)
{
_tempFilePath = path;
}
public override void Close()
{
Dispose(true);
}
protected override void Dispose(bool disposing)
{
if (_disposed)
return;
base.Dispose(disposing);
if (disposing)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && File.Exists(_tempFilePath))
{
File.Delete(_tempFilePath);
}
_disposed = true;
}
}
}
}

View File

@@ -3,25 +3,47 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio
{
public static class TypeTreeHelper
{
private static readonly JsonSerializerOptions JsonOptions;
static TypeTreeHelper()
{
JsonOptions = new JsonSerializerOptions
{
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
IncludeFields = true,
};
}
public static string ReadTypeString(TypeTree m_Type, ObjectReader reader)
{
reader.Reset();
var readed = 0L;
var sb = new StringBuilder();
var m_Nodes = m_Type.m_Nodes;
for (int i = 0; i < m_Nodes.Count; i++)
try
{
ReadStringValue(sb, m_Nodes, reader, ref i);
for (var i = 0; i < m_Nodes.Count; i++)
{
ReadStringValue(sb, m_Nodes, reader, ref i);
readed = reader.Position - reader.byteStart;
}
}
catch (Exception)
{
//Ignore
}
var readed = reader.Position - reader.byteStart;
if (readed != reader.byteSize)
{
Logger.Info($"Failed to read type, read {readed} bytes but expected {reader.byteSize} bytes");
}
return sb.ToString();
}
@@ -80,7 +102,7 @@ namespace AssetStudio
case "bool":
value = reader.ReadBoolean();
break;
case "string":
case "string" when m_Nodes[i + 1].m_Type == "Array":
append = false;
var str = reader.ReadAlignedString();
sb.AppendFormat("{0}{1} {2} = \"{3}\"\r\n", (new string('\t', level)), varTypeStr, varNameStr, str);
@@ -145,6 +167,8 @@ namespace AssetStudio
}
else //Class
{
if (m_Node.m_Type == "string")
m_Node.m_Type = "CustomType";
append = false;
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr);
var @class = GetNodes(m_Nodes, i);
@@ -163,18 +187,34 @@ namespace AssetStudio
reader.AlignStream();
}
public static byte[] ReadTypeByteArray(TypeTree m_Types, ObjectReader reader)
{
var type = ReadType(m_Types, reader);
var bytes = JsonSerializer.SerializeToUtf8Bytes(type, JsonOptions);
type.Clear();
return bytes;
}
public static OrderedDictionary ReadType(TypeTree m_Types, ObjectReader reader)
{
reader.Reset();
var obj = new OrderedDictionary();
var m_Nodes = m_Types.m_Nodes;
for (int i = 1; i < m_Nodes.Count; i++)
var readed = 0L;
try
{
var m_Node = m_Nodes[i];
var varNameStr = m_Node.m_Name.Replace("image data", "image_data");
obj[varNameStr] = ReadValue(m_Nodes, reader, ref i);
for (int i = 1; i < m_Nodes.Count; i++)
{
var m_Node = m_Nodes[i];
var varNameStr = m_Node.m_Name;
obj[varNameStr] = ReadValue(m_Nodes, reader, ref i);
readed = reader.Position - reader.byteStart;
}
}
catch (Exception)
{
//Ignore
}
var readed = reader.Position - reader.byteStart;
if (readed != reader.byteSize)
{
Logger.Info($"Failed to read type, read {readed} bytes but expected {reader.byteSize} bytes");
@@ -234,7 +274,7 @@ namespace AssetStudio
case "bool":
value = reader.ReadBoolean();
break;
case "string":
case "string" when m_Nodes[i + 1].m_Type == "Array":
value = reader.ReadAlignedString();
var toSkip = GetNodes(m_Nodes, i);
i += toSkip.Count - 1;
@@ -262,10 +302,11 @@ namespace AssetStudio
case "TypelessData":
{
var size = reader.ReadInt32();
var offset = size > 0 ? reader.BaseStream.Position : 0;
var dic = new OrderedDictionary
{
{ "Offset", reader.BaseStream.Position },
{ "Size", size }
{"Offset", offset},
{"Size", size}
};
value = dic;
reader.BaseStream.Position += size;
@@ -274,6 +315,8 @@ namespace AssetStudio
}
default:
{
if (m_Node.m_Type == "string")
m_Node.m_Type = "CustomType";
if (i < m_Nodes.Count - 1 && m_Nodes[i + 1].m_Type == "Array") //Array
{
if ((m_Nodes[i + 1].m_MetaFlag & 0x4000) != 0)
@@ -281,13 +324,13 @@ namespace AssetStudio
var vector = GetNodes(m_Nodes, i);
i += vector.Count - 1;
var size = reader.ReadInt32();
var list = new List<object>(size);
var array = new object[size];
for (int j = 0; j < size; j++)
{
int tmp = 3;
list.Add(ReadValue(vector, reader, ref tmp));
array[j] = ReadValue(vector, reader, ref tmp);
}
value = list;
value = array;
break;
}
else //Class

View File

@@ -1,9 +1,20 @@
using System;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace AssetStudio
{
public static class BuildTypes
{
public static readonly string
Alpha = "a",
Beta = "b",
Final = "f",
Patch = "p",
Tuanjie = "t";
}
public class UnityVersion : IComparable
{
public int Major { get; }
@@ -14,6 +25,10 @@ namespace AssetStudio
public string FullVersion { get; }
public bool IsStripped => this == (0, 0, 0);
public bool IsAlpha => BuildType == BuildTypes.Alpha;
public bool IsBeta => BuildType == BuildTypes.Beta;
public bool IsPatch => BuildType == BuildTypes.Patch;
public bool IsTuanjie => BuildType == BuildTypes.Tuanjie && this >= (2022, 3, 2);
public UnityVersion(string version)
{
@@ -24,7 +39,7 @@ namespace AssetStudio
{
int[] ver = Regex.Matches(version, @"\d+").Cast<Match>().Select(x => int.Parse(x.Value)).ToArray();
(Major, Minor, Patch) = (ver[0], ver[1], ver[2]);
if (ver.Length == 4)
if (ver.Length >= 4)
Build = ver[3];
FullVersion = version;
}
@@ -40,6 +55,7 @@ namespace AssetStudio
}
}
[JsonConstructor]
public UnityVersion(int major = 0, int minor = 0, int patch = 0)
{
(Major, Minor, Patch) = (major, minor, patch);
@@ -47,11 +63,25 @@ namespace AssetStudio
if (!IsStripped)
{
Build = 1;
BuildType = "f";
BuildType = BuildTypes.Final;
FullVersion += $"{BuildType}{Build}";
}
}
public static bool TryParse(string versionStr, out UnityVersion version)
{
version = null;
try
{
version = new UnityVersion(versionStr);
return true;
}
catch (Exception)
{
return false;
}
}
#region UnityVer, UnityVer
public static bool operator ==(UnityVersion left, UnityVersion right)
{

View File

@@ -6,7 +6,7 @@ namespace AssetStudio
{
public class WebFile
{
public StreamFile[] fileList;
public List<StreamFile> fileList;
private class WebData
{
@@ -30,16 +30,15 @@ namespace AssetStudio
data.path = Encoding.UTF8.GetString(reader.ReadBytes(pathLength));
dataList.Add(data);
}
fileList = new StreamFile[dataList.Count];
for (int i = 0; i < dataList.Count; i++)
fileList = new List<StreamFile>(dataList.Count);
foreach (var data in dataList)
{
var data = dataList[i];
var file = new StreamFile();
file.path = data.path;
file.fileName = Path.GetFileName(data.path);
reader.BaseStream.Position = data.dataOffset;
file.stream = new MemoryStream(reader.ReadBytes(data.dataLength));
fileList[i] = file;
fileList.Add(file);
}
}
}

View File

@@ -2,11 +2,11 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net472;net6.0;net7.0;net8.0</TargetFrameworks>
<TargetFrameworks>net472;net8.0;net9.0</TargetFrameworks>
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle>
<AssemblyName>AssetStudioModCLI</AssemblyName>
<Version>0.18.0.0</Version>
<Copyright>Copyright © Perfare; Copyright © aelurum 2023-2024</Copyright>
<Version>0.19.0.0</Version>
<Copyright>Copyright © Perfare; Copyright © aelurum 2023-2025</Copyright>
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>embedded</DebugType>
</PropertyGroup>
@@ -30,6 +30,8 @@
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x86\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x64\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x86\ooz.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x64\ooz.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesPortableNet" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' AND '$(TargetFramework)' != 'net472' ">
@@ -37,10 +39,21 @@
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libAssetStudioFBXNative.so" DestinationFolder="$(TargetDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-x64\libAssetStudioFBXNative.dylib" DestinationFolder="$(TargetDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-arm64\libAssetStudioFBXNative.dylib" DestinationFolder="$(TargetDir)runtimes\osx-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x86\libfmod.so" DestinationFolder="$(TargetDir)runtimes\linux-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libfmod.so" DestinationFolder="$(TargetDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-arm64\libfmod.so" DestinationFolder="$(TargetDir)runtimes\linux-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-x64\libfmod.dylib" DestinationFolder="$(TargetDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-arm64\libfmod.dylib" DestinationFolder="$(TargetDir)runtimes\osx-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-arm64\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x86\libooz.so" DestinationFolder="$(TargetDir)runtimes\linux-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libooz.so" DestinationFolder="$(TargetDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-arm64\libooz.so" DestinationFolder="$(TargetDir)runtimes\linux-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-x64\libooz.dylib" DestinationFolder="$(TargetDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-arm64\libooz.dylib" DestinationFolder="$(TargetDir)runtimes\osx-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-arm64\libTexture2DDecoderNative.so" DestinationFolder="$(TargetDir)runtimes\linux-arm64\native" ContinueOnError="false" />
</Target>
<!-- Publishing an app as framework-dependent produces a cross-platform binary as a dll file, and a platform-specific executable that targets your current platform.
@@ -53,6 +66,8 @@
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\fmod.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\fmod.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\ooz.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\ooz.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesPortableNet" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' AND '$(TargetFramework)' != 'net472' ">
@@ -60,10 +75,21 @@
<Copy SourceFiles="$(TargetDir)runtimes\linux-x64\native\libAssetStudioFBXNative.so" DestinationFolder="$(PublishDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-x64\native\libAssetStudioFBXNative.dylib" DestinationFolder="$(PublishDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-arm64\native\libAssetStudioFBXNative.dylib" DestinationFolder="$(PublishDir)runtimes\osx-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-x86\native\libfmod.so" DestinationFolder="$(PublishDir)runtimes\linux-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-x64\native\libfmod.so" DestinationFolder="$(PublishDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-arm64\native\libfmod.so" DestinationFolder="$(PublishDir)runtimes\linux-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-x64\native\libfmod.dylib" DestinationFolder="$(PublishDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-arm64\native\libfmod.dylib" DestinationFolder="$(PublishDir)runtimes\osx-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-arm64\native\fmod.dll" DestinationFolder="$(PublishDir)runtimes\win-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-x86\native\libooz.so" DestinationFolder="$(PublishDir)runtimes\linux-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-x64\native\libooz.so" DestinationFolder="$(PublishDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-arm64\native\libooz.so" DestinationFolder="$(PublishDir)runtimes\linux-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-x64\native\libooz.dylib" DestinationFolder="$(PublishDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-arm64\native\libooz.dylib" DestinationFolder="$(PublishDir)runtimes\osx-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-arm64\native\libTexture2DDecoderNative.so" DestinationFolder="$(PublishDir)runtimes\linux-arm64\native" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesWin86" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x86' ">
@@ -71,6 +97,7 @@
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\Win32\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x86\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x86\ooz.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesWin64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x64' ">
@@ -78,6 +105,7 @@
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\x64\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x64\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x64\ooz.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesWin" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('win-x')) ">
@@ -85,30 +113,59 @@
<Copy SourceFiles="$(TargetDir)\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\fmod.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\ooz.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesWinArm64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-arm64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-arm64\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesWinArm64" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('win-arm64')) ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\fmod.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesLinux64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libAssetStudioFBXNative.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libfmod.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libooz.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesLinux64" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\libAssetStudioFBXNative.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\libfmod.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\libooz.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesLinuxArm64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'linux-arm64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-arm64\libTexture2DDecoderNative.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-arm64\libfmod.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-arm64\libooz.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesLinuxArm64" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == 'linux-arm64' ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\libTexture2DDecoderNative.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\libfmod.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\libooz.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesMac" AfterTargets="AfterBuild" Condition=" $(RuntimeIdentifier.Contains('osx-')) ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\$(RuntimeIdentifier)\libAssetStudioFBXNative.dylib" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\$(RuntimeIdentifier)\libfmod.dylib" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\$(RuntimeIdentifier)\libooz.dylib" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesMac" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('osx-')) ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\libAssetStudioFBXNative.dylib" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\libfmod.dylib" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\libooz.dylib" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
</Project>

View File

@@ -10,7 +10,51 @@ namespace AssetStudioCLI
{
internal static class Exporter
{
public static bool ExportVideoClip(AssetItem item, string exportPath)
private static readonly HashSet<string> ExportPathHashSet = new HashSet<string>(System.StringComparer.OrdinalIgnoreCase);
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath, string mode = "Export")
{
var fileName = FixFileName(item.Text);
var filenameFormat = CLIOptions.o_filenameFormat.Value;
var canOverwrite = CLIOptions.f_overwriteExisting.Value;
switch (filenameFormat)
{
case FilenameFormat.AssetName_PathID:
fileName = $"{fileName} @{item.m_PathID}";
break;
case FilenameFormat.PathID:
fileName = item.m_PathID.ToString();
break;
}
fullPath = Path.Combine(dir, fileName + extension);
if (ExportPathHashSet.Add(fullPath))
{
if (CanWrite(fullPath, dir, canOverwrite))
{
return true;
}
}
else if (filenameFormat == FilenameFormat.AssetName)
{
fullPath = Path.Combine(dir, fileName + item.UniqueID + extension);
if (CanWrite(fullPath, dir, canOverwrite))
{
return true;
}
}
Logger.Error($"{mode} error. File \"{fullPath.Color(ColorConsole.BrightRed)}\" already exist");
return false;
}
private static bool CanWrite(string fullPath, string dir, bool canOverwrite)
{
if (!canOverwrite && File.Exists(fullPath))
return false;
Directory.CreateDirectory(dir);
return true;
}
private static bool ExportVideoClip(AssetItem item, string exportPath)
{
var m_VideoClip = (VideoClip)item.Asset;
if (m_VideoClip.m_ExternalResources.m_Size > 0)
@@ -36,7 +80,7 @@ namespace AssetStudioCLI
return false;
}
public static bool ExportMovieTexture(AssetItem item, string exportPath)
private static bool ExportMovieTexture(AssetItem item, string exportPath)
{
var m_MovieTexture = (MovieTexture)item.Asset;
if (!TryExportFile(exportPath, item, ".ogv", out var exportFullPath))
@@ -47,7 +91,7 @@ namespace AssetStudioCLI
return true;
}
public static bool ExportShader(AssetItem item, string exportPath)
private static bool ExportShader(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".shader", out var exportFullPath))
return false;
@@ -58,24 +102,23 @@ namespace AssetStudioCLI
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportTextAsset(AssetItem item, string exportPath)
private static bool ExportTextAsset(AssetItem item, string exportPath)
{
var m_TextAsset = (TextAsset)item.Asset;
var extension = ".txt";
var assetExtension = Path.GetExtension(m_TextAsset.m_Name);
if (!CLIOptions.f_notRestoreExtensionName.Value)
{
if (!string.IsNullOrEmpty(assetExtension))
if (Path.HasExtension(m_TextAsset.m_Name))
{
extension = "";
}
else if (!string.IsNullOrEmpty(item.Container))
else
{
var ext = Path.GetExtension(item.Container);
if (!string.IsNullOrEmpty(item.Container))
var extFromContainer = Path.GetExtension(item.Container);
if (!string.IsNullOrEmpty(extFromContainer))
{
extension = ext;
extension = extFromContainer;
}
}
}
@@ -86,8 +129,8 @@ namespace AssetStudioCLI
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportMonoBehaviour(AssetItem item, string exportPath)
private static bool ExportMonoBehaviour(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".json", out var exportFullPath))
return false;
@@ -109,7 +152,7 @@ namespace AssetStudioCLI
return false;
}
public static bool ExportFont(AssetItem item, string exportPath)
private static bool ExportFont(AssetItem item, string exportPath)
{
var m_Font = (Font)item.Asset;
if (m_Font.m_FontData != null)
@@ -129,37 +172,150 @@ namespace AssetStudioCLI
return false;
}
public static void ExportGameObject(GameObject gameObject, string exportPath, List<AssetItem> animationList = null)
private static bool ExportMesh(AssetItem item, string exportPath)
{
var m_Mesh = (Mesh)item.Asset;
m_Mesh.ProcessData();
if (m_Mesh.m_VertexCount <= 0)
return false;
if (!TryExportFile(exportPath, item, ".obj", out var exportFullPath))
return false;
var sb = new StringBuilder();
sb.AppendLine("g " + m_Mesh.m_Name);
#region Vertices
if (m_Mesh.m_Vertices == null || m_Mesh.m_Vertices.Length == 0)
{
return false;
}
int c = 3;
if (m_Mesh.m_Vertices.Length == m_Mesh.m_VertexCount * 4)
{
c = 4;
}
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
{
sb.Append($"v {-m_Mesh.m_Vertices[v * c]} {m_Mesh.m_Vertices[v * c + 1]} {m_Mesh.m_Vertices[v * c + 2]}\r\n");
}
#endregion
#region UV
if (m_Mesh.m_UV0?.Length > 0)
{
c = 4;
if (m_Mesh.m_UV0.Length == m_Mesh.m_VertexCount * 2)
{
c = 2;
}
else if (m_Mesh.m_UV0.Length == m_Mesh.m_VertexCount * 3)
{
c = 3;
}
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
{
sb.AppendFormat("vt {0} {1}\r\n", m_Mesh.m_UV0[v * c], m_Mesh.m_UV0[v * c + 1]);
}
}
#endregion
#region Normals
if (m_Mesh.m_Normals?.Length > 0)
{
if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 3)
{
c = 3;
}
else if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 4)
{
c = 4;
}
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
{
sb.AppendFormat("vn {0} {1} {2}\r\n", -m_Mesh.m_Normals[v * c], m_Mesh.m_Normals[v * c + 1], m_Mesh.m_Normals[v * c + 2]);
}
}
#endregion
#region Face
int sum = 0;
for (var i = 0; i < m_Mesh.m_SubMeshes.Count; i++)
{
sb.AppendLine($"g {m_Mesh.m_Name}_{i}");
int indexCount = (int)m_Mesh.m_SubMeshes[i].indexCount;
var end = sum + indexCount / 3;
for (int f = sum; f < end; f++)
{
sb.AppendFormat("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\r\n", m_Mesh.m_Indices[f * 3 + 2] + 1, m_Mesh.m_Indices[f * 3 + 1] + 1, m_Mesh.m_Indices[f * 3] + 1);
}
sum = end;
}
#endregion
sb.Replace("NaN", "0");
File.WriteAllText(exportFullPath, sb.ToString());
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportAnimator(AssetItem item, string exportPath, List<AssetItem> animationList = null)
{
var exportFullPath = Path.Combine(exportPath, "FBX_Animator", item.Text, item.Text + ".fbx");
if (File.Exists(exportFullPath))
{
exportFullPath = Path.Combine(exportPath, item.Text + item.UniqueID, item.Text + ".fbx");
}
var m_Animator = (Animator)item.Asset;
var convert = animationList != null
? new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value, animationList.Select(x => (AnimationClip)x.Asset).ToArray())
: new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value);
exportPath = exportPath + FixFileName(gameObject.m_Name) + ".fbx";
ExportFbx(convert, exportPath);
? new ModelConverter(m_Animator, CLIOptions.o_imageFormat.Value, animationList.Select(x => (AnimationClip)x.Asset).ToList())
: new ModelConverter(m_Animator, CLIOptions.o_imageFormat.Value);
ExportFbx(convert, exportFullPath);
return true;
}
private static void ExportFbx(IImported convert, string exportPath)
{
var eulerFilter = true;
var filterPrecision = 0.25f;
var exportAllNodes = true;
var exportSkins = true;
var exportAnimations = true;
var exportBlendShape = true;
var castToBone = false;
var boneSize = CLIOptions.o_fbxBoneSize.Value;
var exportAllUvsAsDiffuseMaps = false;
var scaleFactor = CLIOptions.o_fbxScaleFactor.Value;
var fbxVersion = 3;
var fbxFormat = 0;
ModelExporter.ExportFbx(exportPath, convert, eulerFilter, filterPrecision,
exportAllNodes, exportSkins, exportAnimations, exportBlendShape, castToBone, boneSize, exportAllUvsAsDiffuseMaps, scaleFactor, fbxVersion, fbxFormat == 1);
var fbxSettings = new Fbx.Settings
{
BoneSize = CLIOptions.o_fbxBoneSize.Value,
ScaleFactor = CLIOptions.o_fbxScaleFactor.Value,
ExportAllUvsAsDiffuseMaps = CLIOptions.f_fbxUvsAsDiffuseMaps.Value,
ExportAnimations = CLIOptions.o_fbxAnimMode.Value != AnimationExportMode.Skip,
};
ModelExporter.ExportFbx(exportPath, convert, fbxSettings);
}
public static bool ExportRawFile(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath, mode: "ExportRaw"))
return false;
switch (item.Asset)
{
case Texture2D m_Texture2D:
if (!string.IsNullOrEmpty(m_Texture2D.m_StreamData?.path))
{
m_Texture2D.image_data.WriteData(exportFullPath.Replace(".dat", "_data.dat"));
}
break;
case AudioClip m_AudioClip:
if (!string.IsNullOrEmpty(m_AudioClip.m_Source))
{
m_AudioClip.m_AudioData.WriteData(exportFullPath.Replace(".dat", "_data.dat"));
}
break;
case VideoClip m_VideoClip:
if (!string.IsNullOrEmpty(m_VideoClip.m_ExternalResources.m_Source))
{
m_VideoClip.m_VideoData.WriteData(exportFullPath.Replace(".dat", "_data.dat"));
}
break;
}
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
@@ -189,135 +345,6 @@ namespace AssetStudioCLI
return false;
}
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath, string mode = "Export")
{
var fileName = FixFileName(item.Text);
var filenameFormat = CLIOptions.o_filenameFormat.Value;
switch (filenameFormat)
{
case FilenameFormat.AssetName_PathID:
fileName = $"{fileName} @{item.m_PathID}";
break;
case FilenameFormat.PathID:
fileName = item.m_PathID.ToString();
break;
}
fullPath = Path.Combine(dir, fileName + extension);
if (!File.Exists(fullPath))
{
Directory.CreateDirectory(dir);
return true;
}
if (filenameFormat == FilenameFormat.AssetName)
{
fullPath = Path.Combine(dir, fileName + item.UniqueID + extension);
if (!File.Exists(fullPath))
{
Directory.CreateDirectory(dir);
return true;
}
}
Logger.Error($"{mode} error. File \"{fullPath.Color(ColorConsole.BrightRed)}\" already exist");
return false;
}
private static bool ExportMesh(AssetItem item, string exportPath)
{
var m_Mesh = (Mesh)item.Asset;
if (m_Mesh.m_VertexCount <= 0)
return false;
if (!TryExportFile(exportPath, item, ".obj", out var exportFullPath))
return false;
var sb = new StringBuilder();
sb.AppendLine("g " + m_Mesh.m_Name);
#region Vertices
if (m_Mesh.m_Vertices == null || m_Mesh.m_Vertices.Length == 0)
{
return false;
}
int c = 3;
if (m_Mesh.m_Vertices.Length == m_Mesh.m_VertexCount * 4)
{
c = 4;
}
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
{
sb.Append($"v {-m_Mesh.m_Vertices[v * c]} {m_Mesh.m_Vertices[v * c + 1]} {m_Mesh.m_Vertices[v * c + 2]}\r\n");
}
#endregion
#region UV
if (m_Mesh.m_UV0?.Length > 0)
{
c = 4;
if (m_Mesh.m_UV0.Length == m_Mesh.m_VertexCount * 2)
{
c = 2;
}
else if (m_Mesh.m_UV0.Length == m_Mesh.m_VertexCount * 3)
{
c = 3;
}
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
{
sb.AppendFormat("vt {0} {1}\r\n", m_Mesh.m_UV0[v * c], m_Mesh.m_UV0[v * c + 1]);
}
}
#endregion
#region Normals
if (m_Mesh.m_Normals?.Length > 0)
{
if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 3)
{
c = 3;
}
else if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 4)
{
c = 4;
}
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
{
sb.AppendFormat("vn {0} {1} {2}\r\n", -m_Mesh.m_Normals[v * c], m_Mesh.m_Normals[v * c + 1], m_Mesh.m_Normals[v * c + 2]);
}
}
#endregion
#region Face
int sum = 0;
for (var i = 0; i < m_Mesh.m_SubMeshes.Length; i++)
{
sb.AppendLine($"g {m_Mesh.m_Name}_{i}");
int indexCount = (int)m_Mesh.m_SubMeshes[i].indexCount;
var end = sum + indexCount / 3;
for (int f = sum; f < end; f++)
{
sb.AppendFormat("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\r\n", m_Mesh.m_Indices[f * 3 + 2] + 1, m_Mesh.m_Indices[f * 3 + 1] + 1, m_Mesh.m_Indices[f * 3] + 1);
}
sum = end;
}
#endregion
sb.Replace("NaN", "0");
File.WriteAllText(exportFullPath, sb.ToString());
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportConvertFile(AssetItem item, string exportPath)
{
switch (item.Type)
@@ -346,11 +373,30 @@ namespace AssetStudioCLI
}
}
public static void ExportGameObject(GameObject gameObject, string exportPath, List<AssetItem> animationList = null)
{
var convert = animationList != null
? new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value, animationList.Select(x => (AnimationClip)x.Asset).ToList())
: new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value);
var modelName = FixFileName(gameObject.m_Name);
var exportFullPath = Path.Combine(exportPath, "FBX_GameObjects", modelName, modelName + ".fbx");
if (File.Exists(exportFullPath))
{
exportFullPath = Path.Combine(exportPath, $"{modelName}_{gameObject.GetHashCode():X}", modelName + ".fbx");
}
ExportFbx(convert, exportFullPath);
}
public static string FixFileName(string str)
{
return str.Length >= 260
? Path.GetRandomFileName()
: Path.GetInvalidFileNameChars().Aggregate(str, (current, c) => current.Replace(c, '_'));
}
public static void ClearHash()
{
ExportPathHashSet.Clear();
}
}
}

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