This commit is contained in:
Razmoth
2023-08-21 21:45:57 +04:00
parent 6da2387c8c
commit 0bd3fa6db2
48 changed files with 967 additions and 510 deletions

View File

@@ -2,9 +2,9 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>0.90.00</Version> <Version>0.90.10</Version>
<AssemblyVersion>0.90.00</AssemblyVersion> <AssemblyVersion>0.90.10</AssemblyVersion>
<FileVersion>0.90.00</FileVersion> <FileVersion>0.90.10</FileVersion>
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright> <Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>

View File

@@ -2,15 +2,15 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>0.90.00</Version> <Version>0.90.10</Version>
<AssemblyVersion>0.90.00</AssemblyVersion> <AssemblyVersion>0.90.10</AssemblyVersion>
<FileVersion>0.90.00</FileVersion> <FileVersion>0.90.10</FileVersion>
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright> <Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.5" /> <PackageReference Include="K4os.Compression.LZ4" Version="1.3.6" />
<PackageReference Include="MessagePack" Version="2.6.100-alpha" /> <PackageReference Include="MessagePack" Version="2.6.100-alpha" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="ZstdSharp.Port" Version="0.7.2" /> <PackageReference Include="ZstdSharp.Port" Version="0.7.2" />

View File

@@ -36,7 +36,9 @@ namespace AssetStudio
{ {
Directory.CreateDirectory(MapName); Directory.CreateDirectory(MapName);
var files = Directory.GetFiles(MapName, "*.bin", SearchOption.TopDirectoryOnly); var files = Directory.GetFiles(MapName, "*.bin", SearchOption.TopDirectoryOnly);
return files.Select(Path.GetFileNameWithoutExtension).ToArray(); var mapNames = files.Select(Path.GetFileNameWithoutExtension).ToArray();
Logger.Verbose($"Found {mapNames.Length} CABMaps under Maps folder");
return mapNames;
} }
public static void Clear() public static void Clear()
@@ -48,34 +50,20 @@ namespace AssetStudio
tokenSource.Dispose(); tokenSource.Dispose();
tokenSource = new CancellationTokenSource(); tokenSource = new CancellationTokenSource();
GC.WaitForPendingFinalizers(); Logger.Verbose("Cleared AssetsHelper successfully !!");
GC.Collect();
} }
public static void ClearOffsets() => Offsets.Clear(); public static void ClearOffsets()
public static void Remove(string path) => Offsets.Remove(path);
public static bool TryAdd(string name, out string path)
{ {
if (CABMap.TryGetValue(name, out var entry)) Offsets.Clear();
{ Logger.Verbose("Cleared cached offsets");
path = Path.Combine(BaseFolder, entry.Path);
if (!Offsets.ContainsKey(path))
{
Offsets.Add(path, new HashSet<long>());
}
Offsets[path].Add(entry.Offset);
return true;
}
path = string.Empty;
return false;
} }
public static bool TryGet(string path, out long[] offsets) public static bool TryGet(string path, out long[] offsets)
{ {
if (Offsets.TryGetValue(path, out var list)) if (Offsets.TryGetValue(path, out var list) && list.Count > 0)
{ {
Logger.Verbose($"Found {list.Count} offsets for path {path}");
offsets = list.ToArray(); offsets = list.ToArray();
return true; return true;
} }
@@ -83,6 +71,67 @@ namespace AssetStudio
return false; return false;
} }
public static void AddCABOffsets(string[] paths, List<string> cabs)
{
for (int i = 0; i < cabs.Count; i++)
{
var cab = cabs[i];
if (CABMap.TryGetValue(cab, out var entry))
{
var fullPath = Path.Combine(BaseFolder, entry.Path);
Logger.Verbose($"Found {cab} in {fullPath}");
if (!paths.Contains(fullPath))
{
Offsets.TryAdd(fullPath, new HashSet<long>());
Offsets[fullPath].Add(entry.Offset);
Logger.Verbose($"Added {fullPath} to Offsets, at offset {entry.Offset}");
}
foreach (var dep in entry.Dependencies)
{
if (!cabs.Contains(dep))
cabs.Add(dep);
}
}
}
}
public static bool FindCAB(string path, out List<string> cabs)
{
var relativePath = Path.GetRelativePath(BaseFolder, path);
cabs = CABMap.AsParallel().Where(x => x.Value.Path.Equals(relativePath, StringComparison.OrdinalIgnoreCase)).Select(x => x.Key).Distinct().ToList();
Logger.Verbose($"Found {cabs.Count} that belongs to {relativePath}");
return cabs.Count != 0;
}
public static string[] ProcessFiles(string[] files)
{
foreach (var file in files)
{
Offsets.TryAdd(file, new HashSet<long>());
Logger.Verbose($"Added {file} to Offsets dictionary");
if (FindCAB(file, out var cabs))
{
AddCABOffsets(files, cabs);
}
}
Logger.Verbose($"Finished resolving dependncies, the original {files.Length} files will be loaded entirely, and the {Offsets.Count - files.Length} dependicnes will be loaded from cached offsets only");
return Offsets.Keys.ToArray();
}
public static string[] ProcessDependencies(string[] files)
{
if (CABMap.Count == 0)
{
Logger.Warning("CABMap is not build, skip resolving dependencies...");
}
else
{
Logger.Info("Resolving Dependencies...");
files = ProcessFiles(files);
}
return files;
}
public static void BuildCABMap(string[] files, string mapName, string baseFolder, Game game) public static void BuildCABMap(string[] files, string mapName, string baseFolder, Game game)
{ {
Logger.Info("Building CABMap..."); Logger.Info("Building CABMap...");
@@ -220,6 +269,7 @@ namespace AssetStudio
CABMap.Add(cab, entry); CABMap.Add(cab, entry);
} }
} }
Logger.Verbose($"Initialized CABMap with {CABMap.Count} entries");
Logger.Info($"Loaded {mapName} !!"); Logger.Info($"Loaded {mapName} !!");
} }
catch (Exception e) catch (Exception e)
@@ -342,7 +392,7 @@ namespace AssetStudio
case ClassIDType.Texture2D: case ClassIDType.Texture2D:
case ClassIDType.VideoClip: case ClassIDType.VideoClip:
case ClassIDType.AudioClip: case ClassIDType.AudioClip:
case ClassIDType.AnimationClip: case ClassIDType.AnimationClip when AnimationClip.Parsable:
asset.Name = objectReader.ReadAlignedString(); asset.Name = objectReader.ReadAlignedString();
break; break;
default: default:

View File

@@ -56,6 +56,8 @@ namespace AssetStudio
var path = Path.GetDirectoryName(Path.GetFullPath(files[0])); var path = Path.GetDirectoryName(Path.GetFullPath(files[0]));
MergeSplitAssets(path); MergeSplitAssets(path);
var toReadFile = ProcessingSplitFiles(files.ToList()); var toReadFile = ProcessingSplitFiles(files.ToList());
if (ResolveDependencies)
toReadFile = AssetsHelper.ProcessDependencies(toReadFile);
Load(toReadFile); Load(toReadFile);
if (Silent) if (Silent)
@@ -89,6 +91,7 @@ namespace AssetStudio
{ {
foreach (var file in files) foreach (var file in files)
{ {
Logger.Verbose($"caching {file} path and name to filter out duplicates");
importFiles.Add(file); importFiles.Add(file);
importFilesHash.Add(Path.GetFileName(file)); importFilesHash.Add(Path.GetFileName(file));
} }
@@ -171,6 +174,7 @@ namespace AssetStudio
foreach (var sharedFile in assetsFile.m_Externals) foreach (var sharedFile in assetsFile.m_Externals)
{ {
Logger.Verbose($"{assetsFile.fileName} needs external file {sharedFile.fileName}, attempting to look it up...");
var sharedFileName = sharedFile.fileName; var sharedFileName = sharedFile.fileName;
if (!importFilesHash.Contains(sharedFileName)) if (!importFilesHash.Contains(sharedFileName))
@@ -183,6 +187,7 @@ namespace AssetStudio
var findFiles = Directory.GetFiles(Path.GetDirectoryName(reader.FullPath), sharedFileName, SearchOption.AllDirectories); var findFiles = Directory.GetFiles(Path.GetDirectoryName(reader.FullPath), sharedFileName, SearchOption.AllDirectories);
if (findFiles.Length > 0) if (findFiles.Length > 0)
{ {
Logger.Verbose($"Found {findFiles.Length} matching files, picking first file {findFiles[0]} !!");
sharedFilePath = findFiles[0]; sharedFilePath = findFiles[0];
} }
} }
@@ -193,6 +198,7 @@ namespace AssetStudio
} }
else else
{ {
Logger.Verbose("Nothing was found, caching into non existant files to avoid repeated searching !!");
noexistFiles.Add(sharedFilePath); noexistFiles.Add(sharedFilePath);
} }
} }
@@ -214,6 +220,7 @@ namespace AssetStudio
private void LoadAssetsFromMemory(FileReader reader, string originalPath, string unityVersion = null, long originalOffset = 0) private void LoadAssetsFromMemory(FileReader reader, string originalPath, string unityVersion = null, long originalOffset = 0)
{ {
Logger.Verbose($"Loading asset file {reader.FileName} with version {unityVersion} from {originalPath} at offset 0x{originalOffset:X8}");
if (!assetsFileListHash.Contains(reader.FileName)) if (!assetsFileListHash.Contains(reader.FileName))
{ {
try try
@@ -228,38 +235,6 @@ namespace AssetStudio
CheckStrippedVersion(assetsFile); CheckStrippedVersion(assetsFile);
assetsFileList.Add(assetsFile); assetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName); assetsFileListHash.Add(assetsFile.fileName);
if (ResolveDependencies)
{
foreach (var sharedFile in assetsFile.m_Externals)
{
var sharedFileName = sharedFile.fileName;
if (!importFilesHash.Contains(sharedFileName))
{
var sharedFilePath = Path.Combine(Path.GetDirectoryName(originalPath), sharedFileName);
if (!noexistFiles.Contains(sharedFilePath))
{
if (AssetsHelper.TryAdd(sharedFileName, out var path))
{
sharedFilePath = path;
}
if (File.Exists(sharedFilePath))
{
if (!importFiles.Contains(sharedFilePath))
{
importFiles.Add(sharedFilePath);
}
importFilesHash.Add(sharedFileName);
}
else
{
noexistFiles.Add(sharedFilePath);
}
}
}
}
}
} }
catch (Exception e) catch (Exception e)
{ {
@@ -290,6 +265,7 @@ namespace AssetStudio
} }
else else
{ {
Logger.Verbose("Caching resource stream");
resourceFileReaders[file.fileName] = subReader; //TODO resourceFileReaders[file.fileName] = subReader; //TODO
} }
} }
@@ -335,6 +311,7 @@ namespace AssetStudio
LoadWebFile(subReader); LoadWebFile(subReader);
break; break;
case FileType.ResourceFile: case FileType.ResourceFile:
Logger.Verbose("Caching resource stream");
resourceFileReaders[file.fileName] = subReader; //TODO resourceFileReaders[file.fileName] = subReader; //TODO
break; break;
} }
@@ -358,8 +335,7 @@ namespace AssetStudio
using (ZipArchive archive = new ZipArchive(reader.BaseStream, ZipArchiveMode.Read)) using (ZipArchive archive = new ZipArchive(reader.BaseStream, ZipArchiveMode.Read))
{ {
List<string> splitFiles = new List<string>(); List<string> splitFiles = new List<string>();
// register all files before parsing the assets so that the external references can be found Logger.Verbose("Register all files before parsing the assets so that the external references can be found and find split files");
// and find split files
foreach (ZipArchiveEntry entry in archive.Entries) foreach (ZipArchiveEntry entry in archive.Entries)
{ {
if (entry.Name.Contains(".split")) if (entry.Name.Contains(".split"))
@@ -378,7 +354,7 @@ namespace AssetStudio
} }
} }
// merge split files and load the result Logger.Verbose("Merge split files and load the result");
foreach (string basePath in splitFiles) foreach (string basePath in splitFiles)
{ {
try try
@@ -406,15 +382,14 @@ namespace AssetStudio
} }
} }
// load all entries Logger.Verbose("Load all entries");
Logger.Verbose($"Found {archive.Entries.Count} entries");
foreach (ZipArchiveEntry entry in archive.Entries) foreach (ZipArchiveEntry entry in archive.Entries)
{ {
try try
{ {
string dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), reader.FileName, entry.FullName); string dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), reader.FileName, entry.FullName);
// create a new stream Logger.Verbose("Create a new stream to store the deflated stream in and keep the data for later extraction");
// - to store the deflated stream in
// - to keep the data for later extraction
Stream streamReader = new MemoryStream(); Stream streamReader = new MemoryStream();
using (Stream entryStream = entry.Open()) using (Stream entryStream = entry.Open())
{ {
@@ -429,6 +404,7 @@ namespace AssetStudio
entryReader.Position = 0; entryReader.Position = 0;
if (!resourceFileReaders.ContainsKey(entry.Name)) if (!resourceFileReaders.ContainsKey(entry.Name))
{ {
Logger.Verbose("Caching resource file");
resourceFileReaders.Add(entry.Name, entryReader); resourceFileReaders.Add(entry.Name, entryReader);
} }
} }
@@ -459,27 +435,14 @@ namespace AssetStudio
{ {
foreach (var offset in offsets) foreach (var offset in offsets)
{ {
var name = offset.ToString("X8"); LoadBlockSubFile(reader.FullPath, stream, offset);
Logger.Info($"Loading Block {name}");
stream.Offset = offset;
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name);
var subReader = new FileReader(dummyPath, stream, true);
LoadBundleFile(subReader, reader.FullPath, offset, false);
} }
AssetsHelper.Remove(reader.FullPath);
} }
else else
{ {
do do
{ {
var name = stream.AbsolutePosition.ToString("X8"); LoadBlockSubFile(reader.FullPath, stream, stream.AbsolutePosition);
Logger.Info($"Loading Block {name}");
stream.Offset = stream.AbsolutePosition;
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name);
var subReader = new FileReader(dummyPath, stream, true);
LoadBundleFile(subReader, reader.FullPath, stream.AbsolutePosition, false);
} while (stream.Remaining > 0); } while (stream.Remaining > 0);
} }
} }
@@ -492,20 +455,27 @@ namespace AssetStudio
reader.Dispose(); reader.Dispose();
} }
} }
private void LoadBlockSubFile(string path, OffsetStream stream, long offset)
{
var name = offset.ToString("X8");
Logger.Info($"Loading Block {name}");
stream.Offset = offset;
var dummyPath = Path.Combine(Path.GetDirectoryName(path), name);
var subReader = new FileReader(dummyPath, stream, true);
LoadBundleFile(subReader, path, offset, false);
}
private void LoadBlkFile(FileReader reader) private void LoadBlkFile(FileReader reader)
{ {
Logger.Info("Loading " + reader.FullPath); Logger.Info("Loading " + reader.FullPath);
try try
{ {
using var stream = BlkUtils.Decrypt(reader, (Blk)Game); using var stream = BlkUtils.Decrypt(reader, (Blk)Game);
if (AssetsHelper.TryGet(reader.FullPath, out var offsets)) foreach (var offset in stream.GetOffsets(reader.FullPath))
{
foreach (var offset in offsets)
{ {
var name = offset.ToString("X8"); var name = offset.ToString("X8");
Logger.Info($"Loading Block {name}"); Logger.Info($"Loading Block {name}");
stream.Offset = offset;
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name); var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name);
var subReader = new FileReader(dummyPath, stream, true); var subReader = new FileReader(dummyPath, stream, true);
switch (subReader.FileType) switch (subReader.FileType)
@@ -518,30 +488,6 @@ namespace AssetStudio
break; break;
} }
} }
AssetsHelper.Remove(reader.FullPath);
}
else
{
do
{
var name = stream.AbsolutePosition.ToString("X8");
Logger.Info($"Loading Block {name}");
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name);
var subReader = new FileReader(dummyPath, stream, true);
switch (subReader.FileType)
{
case FileType.BundleFile:
LoadBundleFile(subReader, reader.FullPath, stream.AbsolutePosition, false);
break;
case FileType.Mhy0File:
LoadMhy0File(subReader, reader.FullPath, stream.AbsolutePosition, false);
break;
}
stream.Offset = stream.AbsolutePosition;
} while (stream.Remaining > 0);
}
} }
catch (InvalidCastException) catch (InvalidCastException)
{ {
@@ -565,6 +511,7 @@ namespace AssetStudio
try try
{ {
var mhy0File = new Mhy0File(reader, reader.FullPath, (Mhy0)Game); var mhy0File = new Mhy0File(reader, reader.FullPath, (Mhy0)Game);
Logger.Verbose($"mhy0 total size: {mhy0File.TotalSize:X8}");
foreach (var file in mhy0File.fileList) foreach (var file in mhy0File.fileList)
{ {
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName); var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
@@ -575,6 +522,7 @@ namespace AssetStudio
} }
else else
{ {
Logger.Verbose("Caching resource stream");
resourceFileReaders[file.fileName] = cabReader; //TODO resourceFileReaders[file.fileName] = cabReader; //TODO
} }
} }
@@ -612,6 +560,8 @@ namespace AssetStudio
public void Clear() public void Clear()
{ {
Logger.Verbose("Cleaning up...");
foreach (var assetsFile in assetsFileList) foreach (var assetsFile in assetsFileList)
{ {
assetsFile.Objects.Clear(); assetsFile.Objects.Clear();
@@ -653,103 +603,40 @@ namespace AssetStudio
var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objectInfo, Game); var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objectInfo, Game);
try try
{ {
Object obj; Object obj = objectReader.type switch
switch (objectReader.type)
{ {
case ClassIDType.Animation: ClassIDType.Animation => new Animation(objectReader),
obj = new Animation(objectReader); ClassIDType.AnimationClip when AnimationClip.Parsable => new AnimationClip(objectReader),
break; ClassIDType.Animator => new Animator(objectReader),
case ClassIDType.AnimationClip: ClassIDType.AnimatorController => new AnimatorController(objectReader),
obj = new AnimationClip(objectReader); ClassIDType.AnimatorOverrideController => new AnimatorOverrideController(objectReader),
break; ClassIDType.AssetBundle => new AssetBundle(objectReader),
case ClassIDType.Animator: ClassIDType.AudioClip => new AudioClip(objectReader),
obj = new Animator(objectReader); ClassIDType.Avatar => new Avatar(objectReader),
break; ClassIDType.Font => new Font(objectReader),
case ClassIDType.AnimatorController: ClassIDType.GameObject => new GameObject(objectReader),
obj = new AnimatorController(objectReader); ClassIDType.IndexObject => new IndexObject(objectReader),
break; ClassIDType.Material => new Material(objectReader),
case ClassIDType.AnimatorOverrideController: ClassIDType.Mesh => new Mesh(objectReader),
obj = new AnimatorOverrideController(objectReader); ClassIDType.MeshFilter => new MeshFilter(objectReader),
break; ClassIDType.MeshRenderer when Renderer.Parsable => new MeshRenderer(objectReader),
case ClassIDType.AssetBundle: ClassIDType.MiHoYoBinData => new MiHoYoBinData(objectReader),
obj = new AssetBundle(objectReader); ClassIDType.MonoBehaviour => new MonoBehaviour(objectReader),
break; ClassIDType.MonoScript => new MonoScript(objectReader),
case ClassIDType.AudioClip: ClassIDType.MovieTexture => new MovieTexture(objectReader),
obj = new AudioClip(objectReader); ClassIDType.PlayerSettings => new PlayerSettings(objectReader),
break; ClassIDType.RectTransform => new RectTransform(objectReader),
case ClassIDType.Avatar: ClassIDType.Shader when Shader.Parsable => new Shader(objectReader),
obj = new Avatar(objectReader); ClassIDType.SkinnedMeshRenderer when Renderer.Parsable => new SkinnedMeshRenderer(objectReader),
break; ClassIDType.Sprite => new Sprite(objectReader),
case ClassIDType.Font: ClassIDType.SpriteAtlas => new SpriteAtlas(objectReader),
obj = new Font(objectReader); ClassIDType.TextAsset => new TextAsset(objectReader),
break; ClassIDType.Texture2D => new Texture2D(objectReader),
case ClassIDType.GameObject: ClassIDType.Transform => new Transform(objectReader),
obj = new GameObject(objectReader); ClassIDType.VideoClip => new VideoClip(objectReader),
break; ClassIDType.ResourceManager => new ResourceManager(objectReader),
case ClassIDType.IndexObject: _ => new Object(objectReader),
obj = new IndexObject(objectReader); };
break;
case ClassIDType.Material:
obj = new Material(objectReader);
break;
case ClassIDType.Mesh:
obj = new Mesh(objectReader);
break;
case ClassIDType.MeshFilter:
obj = new MeshFilter(objectReader);
break;
case ClassIDType.MeshRenderer when Renderer.Parsable:
obj = new MeshRenderer(objectReader);
break;
case ClassIDType.MiHoYoBinData:
obj = new MiHoYoBinData(objectReader);
break;
case ClassIDType.MonoBehaviour:
obj = new MonoBehaviour(objectReader);
break;
case ClassIDType.MonoScript:
obj = new MonoScript(objectReader);
break;
case ClassIDType.MovieTexture:
obj = new MovieTexture(objectReader);
break;
case ClassIDType.PlayerSettings:
obj = new PlayerSettings(objectReader);
break;
case ClassIDType.RectTransform:
obj = new RectTransform(objectReader);
break;
case ClassIDType.Shader when Shader.Parsable:
obj = new Shader(objectReader);
break;
case ClassIDType.SkinnedMeshRenderer when Renderer.Parsable:
obj = new SkinnedMeshRenderer(objectReader);
break;
case ClassIDType.Sprite:
obj = new Sprite(objectReader);
break;
case ClassIDType.SpriteAtlas:
obj = new SpriteAtlas(objectReader);
break;
case ClassIDType.TextAsset:
obj = new TextAsset(objectReader);
break;
case ClassIDType.Texture2D:
obj = new Texture2D(objectReader);
break;
case ClassIDType.Transform:
obj = new Transform(objectReader);
break;
case ClassIDType.VideoClip:
obj = new VideoClip(objectReader);
break;
case ClassIDType.ResourceManager:
obj = new ResourceManager(objectReader);
break;
default:
obj = new Object(objectReader);
break;
}
assetsFile.AddObject(obj); assetsFile.AddObject(obj);
} }
catch (Exception e) catch (Exception e)
@@ -784,6 +671,7 @@ namespace AssetStudio
} }
if (obj is GameObject m_GameObject) if (obj is GameObject m_GameObject)
{ {
Logger.Verbose($"GameObject with {m_GameObject.m_PathID} in file {m_GameObject.assetsFile.fileName} has {m_GameObject.m_Components.Length} components, Attempting to fetch them...");
foreach (var pptr in m_GameObject.m_Components) foreach (var pptr in m_GameObject.m_Components)
{ {
if (pptr.TryGet(out var m_Component)) if (pptr.TryGet(out var m_Component))
@@ -791,21 +679,27 @@ namespace AssetStudio
switch (m_Component) switch (m_Component)
{ {
case Transform m_Transform: case Transform m_Transform:
Logger.Verbose($"Fetched Transform component with {m_Transform.m_PathID} in file {m_Transform.assetsFile.fileName}, assigning to GameObject components...");
m_GameObject.m_Transform = m_Transform; m_GameObject.m_Transform = m_Transform;
break; break;
case MeshRenderer m_MeshRenderer: case MeshRenderer m_MeshRenderer:
Logger.Verbose($"Fetched MeshRenderer component with {m_MeshRenderer.m_PathID} in file {m_MeshRenderer.assetsFile.fileName}, assigning to GameObject components...");
m_GameObject.m_MeshRenderer = m_MeshRenderer; m_GameObject.m_MeshRenderer = m_MeshRenderer;
break; break;
case MeshFilter m_MeshFilter: case MeshFilter m_MeshFilter:
Logger.Verbose($"Fetched MeshFilter component with {m_MeshFilter.m_PathID} in file {m_MeshFilter.assetsFile.fileName}, assigning to GameObject components...");
m_GameObject.m_MeshFilter = m_MeshFilter; m_GameObject.m_MeshFilter = m_MeshFilter;
break; break;
case SkinnedMeshRenderer m_SkinnedMeshRenderer: case SkinnedMeshRenderer m_SkinnedMeshRenderer:
Logger.Verbose($"Fetched SkinnedMeshRenderer component with {m_SkinnedMeshRenderer.m_PathID} in file {m_SkinnedMeshRenderer.assetsFile.fileName}, assigning to GameObject components...");
m_GameObject.m_SkinnedMeshRenderer = m_SkinnedMeshRenderer; m_GameObject.m_SkinnedMeshRenderer = m_SkinnedMeshRenderer;
break; break;
case Animator m_Animator: case Animator m_Animator:
Logger.Verbose($"Fetched Animator component with {m_Animator.m_PathID} in file {m_Animator.assetsFile.fileName}, assigning to GameObject components...");
m_GameObject.m_Animator = m_Animator; m_GameObject.m_Animator = m_Animator;
break; break;
case Animation m_Animation: case Animation m_Animation:
Logger.Verbose($"Fetched Animation component with {m_Animation.m_PathID} in file {m_Animation.assetsFile.fileName}, assigning to GameObject components...");
m_GameObject.m_Animation = m_Animation; m_GameObject.m_Animation = m_Animation;
break; break;
} }
@@ -816,12 +710,14 @@ namespace AssetStudio
{ {
if (m_SpriteAtlas.m_RenderDataMap.Count > 0) if (m_SpriteAtlas.m_RenderDataMap.Count > 0)
{ {
Logger.Verbose($"SpriteAtlas with {m_SpriteAtlas.m_PathID} in file {m_SpriteAtlas.assetsFile.fileName} has {m_SpriteAtlas.m_PackedSprites.Length} packed sprites, Attempting to fetch them...");
foreach (var m_PackedSprite in m_SpriteAtlas.m_PackedSprites) foreach (var m_PackedSprite in m_SpriteAtlas.m_PackedSprites)
{ {
if (m_PackedSprite.TryGet(out var m_Sprite)) if (m_PackedSprite.TryGet(out var m_Sprite))
{ {
if (m_Sprite.m_SpriteAtlas.IsNull) if (m_Sprite.m_SpriteAtlas.IsNull)
{ {
Logger.Verbose($"Fetched Sprite with {m_Sprite.m_PathID} in file {m_Sprite.assetsFile.fileName}, assigning to parent SpriteAtlas...");
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas); m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
} }
else else
@@ -829,6 +725,7 @@ namespace AssetStudio
m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlaOld); m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlaOld);
if (m_SpriteAtlaOld.m_IsVariant) if (m_SpriteAtlaOld.m_IsVariant)
{ {
Logger.Verbose($"Fetched Sprite with {m_Sprite.m_PathID} in file {m_Sprite.assetsFile.fileName} has a variant of the origianl SpriteAtlas, disposing of the variant and assinging to the parent SpriteAtlas...");
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas); m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
} }
} }

View File

@@ -5,6 +5,7 @@ using System.Data;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Text;
namespace AssetStudio namespace AssetStudio
{ {
@@ -49,6 +50,20 @@ namespace AssetStudio
public uint compressedBlocksInfoSize; public uint compressedBlocksInfoSize;
public uint uncompressedBlocksInfoSize; public uint uncompressedBlocksInfoSize;
public ArchiveFlags flags; public ArchiveFlags flags;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"signature: {signature} | ");
sb.Append($"version: {version} | ");
sb.Append($"unityVersion: {unityVersion} | ");
sb.Append($"unityRevision: {unityRevision} | ");
sb.Append($"size: 0x{size:X8} | ");
sb.Append($"compressedBlocksInfoSize: 0x{compressedBlocksInfoSize:X8} | ");
sb.Append($"uncompressedBlocksInfoSize: 0x{uncompressedBlocksInfoSize:X8} | ");
sb.Append($"flags: 0x{(int)flags:X8}");
return sb.ToString();
}
} }
public class StorageBlock public class StorageBlock
@@ -56,6 +71,15 @@ namespace AssetStudio
public uint compressedSize; public uint compressedSize;
public uint uncompressedSize; public uint uncompressedSize;
public StorageBlockFlags flags; public StorageBlockFlags flags;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"compressedSize: 0x{compressedSize:X8} | ");
sb.Append($"uncompressedSize: 0x{uncompressedSize:X8} | ");
sb.Append($"flags: 0x{(int)flags:X8}");
return sb.ToString();
}
} }
public class Node public class Node
@@ -64,6 +88,16 @@ namespace AssetStudio
public long size; public long size;
public uint flags; public uint flags;
public string path; public string path;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"offset: 0x{offset:X8} | ");
sb.Append($"size: 0x{size:X8} | ");
sb.Append($"flags: {flags} | ");
sb.Append($"path: {path}");
return sb.ToString();
}
} }
private Game Game; private Game Game;
@@ -121,6 +155,7 @@ namespace AssetStudio
{ {
Header header = new Header(); Header header = new Header();
header.signature = reader.ReadStringToNull(20); header.signature = reader.ReadStringToNull(20);
Logger.Verbose($"Parsed signature {header.signature}");
switch (header.signature) switch (header.signature)
{ {
case "UnityFS": case "UnityFS":
@@ -129,6 +164,7 @@ namespace AssetStudio
var version = reader.ReadUInt32(); var version = reader.ReadUInt32();
if (version > 11) if (version > 11)
{ {
Logger.Verbose($"Encrypted bundle header with key {version}");
XORShift128.InitSeed(version); XORShift128.InitSeed(version);
header.version = 6; header.version = 6;
header.unityVersion = "5.x.x"; header.unityVersion = "5.x.x";
@@ -205,6 +241,7 @@ namespace AssetStudio
{ {
Stream blocksStream; Stream blocksStream;
var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.uncompressedSize); var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.uncompressedSize);
Logger.Verbose($"Total size of decompressed blocks: {uncompressedSizeSum}");
if (uncompressedSizeSum >= int.MaxValue) if (uncompressedSizeSum >= int.MaxValue)
{ {
/*var memoryMappedFile = MemoryMappedFile.CreateNew(null, uncompressedSizeSum); /*var memoryMappedFile = MemoryMappedFile.CreateNew(null, uncompressedSizeSum);
@@ -220,26 +257,25 @@ namespace AssetStudio
private void ReadBlocksAndDirectory(FileReader reader, Stream blocksStream) private void ReadBlocksAndDirectory(FileReader reader, Stream blocksStream)
{ {
Logger.Verbose($"Writing block and directory to blocks stream...");
var isCompressed = m_Header.signature == "UnityWeb"; var isCompressed = m_Header.signature == "UnityWeb";
foreach (var blockInfo in m_BlocksInfo) foreach (var blockInfo in m_BlocksInfo)
{ {
var uncompressedBytes = reader.ReadBytes((int)blockInfo.compressedSize); var uncompressedBytes = reader.ReadBytes((int)blockInfo.compressedSize);
if (isCompressed) if (isCompressed)
{ {
using (var memoryStream = new MemoryStream(uncompressedBytes)) using var memoryStream = new MemoryStream(uncompressedBytes);
{ using var decompressStream = SevenZipHelper.StreamDecompress(memoryStream);
using (var decompressStream = SevenZipHelper.StreamDecompress(memoryStream))
{
uncompressedBytes = decompressStream.ToArray(); uncompressedBytes = decompressStream.ToArray();
} }
}
}
blocksStream.Write(uncompressedBytes, 0, uncompressedBytes.Length); blocksStream.Write(uncompressedBytes, 0, uncompressedBytes.Length);
} }
blocksStream.Position = 0; blocksStream.Position = 0;
var blocksReader = new EndianBinaryReader(blocksStream); var blocksReader = new EndianBinaryReader(blocksStream);
var nodesCount = blocksReader.ReadInt32(); var nodesCount = blocksReader.ReadInt32();
m_DirectoryInfo = new Node[nodesCount]; m_DirectoryInfo = new Node[nodesCount];
Logger.Verbose($"Directory count: {nodesCount}");
for (int i = 0; i < nodesCount; i++) for (int i = 0; i < nodesCount; i++)
{ {
m_DirectoryInfo[i] = new Node m_DirectoryInfo[i] = new Node
@@ -253,6 +289,8 @@ namespace AssetStudio
public void ReadFiles(Stream blocksStream, string path) public void ReadFiles(Stream blocksStream, string path)
{ {
Logger.Verbose($"Writing files from blocks stream...");
fileList = new StreamFile[m_DirectoryInfo.Length]; fileList = new StreamFile[m_DirectoryInfo.Length];
for (int i = 0; i < m_DirectoryInfo.Length; i++) for (int i = 0; i < m_DirectoryInfo.Length; i++)
{ {
@@ -286,6 +324,7 @@ namespace AssetStudio
m_Header.uncompressedBlocksInfoSize ^= XORShift128.NextDecryptUInt(); m_Header.uncompressedBlocksInfoSize ^= XORShift128.NextDecryptUInt();
m_Header.compressedBlocksInfoSize ^= XORShift128.NextDecryptUInt(); m_Header.compressedBlocksInfoSize ^= XORShift128.NextDecryptUInt();
XORShift128.Init = false; XORShift128.Init = false;
Logger.Verbose($"Bundle header decrypted");
} }
private void ReadHeader(FileReader reader) private void ReadHeader(FileReader reader)
@@ -317,10 +356,13 @@ namespace AssetStudio
m_Header.compressedBlocksInfoSize -= 0xCA; m_Header.compressedBlocksInfoSize -= 0xCA;
m_Header.uncompressedBlocksInfoSize -= 0xCA; m_Header.uncompressedBlocksInfoSize -= 0xCA;
} }
Logger.Verbose($"Bundle header Info: {m_Header}");
} }
private void ReadUnityCN(EndianBinaryReader reader) private void ReadUnityCN(FileReader reader)
{ {
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with UnityCN encryption");
ArchiveFlags mask; ArchiveFlags mask;
var version = ParseVersion(); var version = ParseVersion();
@@ -339,8 +381,11 @@ namespace AssetStudio
HasBlockInfoNeedPaddingAtStart = true; HasBlockInfoNeedPaddingAtStart = true;
} }
Logger.Verbose($"Mask set to 0x{mask:X8}");
if ((m_Header.flags & mask) != 0) if ((m_Header.flags & mask) != 0)
{ {
Logger.Verbose($"Encryption flag exist, file is encrypted, attempting to decrypt");
UnityCN = new UnityCN(reader); UnityCN = new UnityCN(reader);
} }
} }
@@ -367,6 +412,7 @@ namespace AssetStudio
var blocksInfoBytesSpan = blocksInfoBytes.AsSpan(); var blocksInfoBytesSpan = blocksInfoBytes.AsSpan();
var uncompressedSize = m_Header.uncompressedBlocksInfoSize; var uncompressedSize = m_Header.uncompressedBlocksInfoSize;
var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask); var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask);
Logger.Verbose($"BlockInfo compression type: {compressionType}");
switch (compressionType) //kArchiveCompressionTypeMask switch (compressionType) //kArchiveCompressionTypeMask
{ {
case CompressionType.None: //None case CompressionType.None: //None
@@ -399,6 +445,7 @@ namespace AssetStudio
case CompressionType.Lz4Mr0k: //Lz4Mr0k case CompressionType.Lz4Mr0k: //Lz4Mr0k
if (Mr0kUtils.IsMr0k(blocksInfoBytesSpan)) if (Mr0kUtils.IsMr0k(blocksInfoBytesSpan))
{ {
Logger.Verbose($"Header encrypted with mr0k, decrypting...");
blocksInfoBytesSpan = Mr0kUtils.Decrypt(blocksInfoBytesSpan, (Mr0k)Game).ToArray(); blocksInfoBytesSpan = Mr0kUtils.Decrypt(blocksInfoBytesSpan, (Mr0k)Game).ToArray();
} }
goto case CompressionType.Lz4HC; goto case CompressionType.Lz4HC;
@@ -413,6 +460,7 @@ namespace AssetStudio
} }
var blocksInfoCount = blocksInfoReader.ReadInt32(); var blocksInfoCount = blocksInfoReader.ReadInt32();
m_BlocksInfo = new StorageBlock[blocksInfoCount]; m_BlocksInfo = new StorageBlock[blocksInfoCount];
Logger.Verbose($"Blocks count: {blocksInfoCount}");
for (int i = 0; i < blocksInfoCount; i++) for (int i = 0; i < blocksInfoCount; i++)
{ {
m_BlocksInfo[i] = new StorageBlock m_BlocksInfo[i] = new StorageBlock
@@ -421,10 +469,13 @@ namespace AssetStudio
compressedSize = blocksInfoReader.ReadUInt32(), compressedSize = blocksInfoReader.ReadUInt32(),
flags = (StorageBlockFlags)blocksInfoReader.ReadUInt16() flags = (StorageBlockFlags)blocksInfoReader.ReadUInt16()
}; };
Logger.Verbose($"Block {i} Info: {m_BlocksInfo[i]}");
} }
var nodesCount = blocksInfoReader.ReadInt32(); var nodesCount = blocksInfoReader.ReadInt32();
m_DirectoryInfo = new Node[nodesCount]; m_DirectoryInfo = new Node[nodesCount];
Logger.Verbose($"Directory count: {nodesCount}");
for (int i = 0; i < nodesCount; i++) for (int i = 0; i < nodesCount; i++)
{ {
m_DirectoryInfo[i] = new Node m_DirectoryInfo[i] = new Node
@@ -434,6 +485,8 @@ namespace AssetStudio
flags = blocksInfoReader.ReadUInt32(), flags = blocksInfoReader.ReadUInt32(),
path = blocksInfoReader.ReadStringToNull(), path = blocksInfoReader.ReadStringToNull(),
}; };
Logger.Verbose($"Directory {i} Info: {m_DirectoryInfo[i]}");
} }
} }
if (HasBlockInfoNeedPaddingAtStart && (m_Header.flags & ArchiveFlags.BlockInfoNeedPaddingAtStart) != 0) if (HasBlockInfoNeedPaddingAtStart && (m_Header.flags & ArchiveFlags.BlockInfoNeedPaddingAtStart) != 0)
@@ -444,10 +497,14 @@ namespace AssetStudio
private void ReadBlocks(FileReader reader, Stream blocksStream) private void ReadBlocks(FileReader reader, Stream blocksStream)
{ {
Logger.Verbose($"Writing block to blocks stream...");
for (int i = 0; i < m_BlocksInfo.Length; i++) for (int i = 0; i < m_BlocksInfo.Length; i++)
{ {
Logger.Verbose($"Reading block {i}...");
var blockInfo = m_BlocksInfo[i]; var blockInfo = m_BlocksInfo[i];
var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask); var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask);
Logger.Verbose($"Block compression type {compressionType}");
switch (compressionType) //kStorageBlockCompressionTypeMask switch (compressionType) //kStorageBlockCompressionTypeMask
{ {
case CompressionType.None: //None case CompressionType.None: //None
@@ -470,10 +527,12 @@ namespace AssetStudio
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize); var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
if (compressionType == CompressionType.Lz4Mr0k && Mr0kUtils.IsMr0k(compressedBytes)) if (compressionType == CompressionType.Lz4Mr0k && Mr0kUtils.IsMr0k(compressedBytes))
{ {
Logger.Verbose($"Block encrypted with mr0k, decrypting...");
compressedBytesSpan = Mr0kUtils.Decrypt(compressedBytesSpan, (Mr0k)Game); compressedBytesSpan = Mr0kUtils.Decrypt(compressedBytesSpan, (Mr0k)Game);
} }
if (Game.Type.IsUnityCN() && ((int)blockInfo.flags & 0x100) != 0) if (Game.Type.IsUnityCN() && ((int)blockInfo.flags & 0x100) != 0)
{ {
Logger.Verbose($"Decrypting block with UnityCN...");
UnityCN.DecryptBlock(compressedBytes, compressedSize, i); UnityCN.DecryptBlock(compressedBytes, compressedSize, i);
} }
if (Game.Type.IsNetEase() && i == 0) if (Game.Type.IsNetEase() && i == 0)

View File

@@ -965,6 +965,8 @@ namespace AssetStudio
public sealed class AnimationClip : NamedObject public sealed class AnimationClip : NamedObject
{ {
public static bool Parsable;
public AnimationType m_AnimationType; public AnimationType m_AnimationType;
public bool m_Legacy; public bool m_Legacy;
public bool m_Compressed; public bool m_Compressed;

View File

@@ -37,6 +37,8 @@ namespace AssetStudio
serializedType = reader.serializedType; serializedType = reader.serializedType;
byteSize = reader.byteSize; byteSize = reader.byteSize;
Logger.Verbose($"Attempting to read object {type} with {m_PathID} in file {assetsFile.fileName}, starting from offset 0x{reader.byteStart:X8} with expected size of 0x{byteSize:X8} !!");
if (platform == BuildTarget.NoTarget) if (platform == BuildTarget.NoTarget)
{ {
var m_ObjectHideFlags = reader.ReadUInt32(); var m_ObjectHideFlags = reader.ReadUInt32();
@@ -81,6 +83,7 @@ namespace AssetStudio
public byte[] GetRawData() public byte[] GetRawData()
{ {
Logger.Verbose($"Dumping raw bytes of the object with {m_PathID} in file {assetsFile.fileName}...");
reader.Reset(); reader.Reset();
return reader.ReadBytes((int)byteSize); return reader.ReadBytes((int)byteSize);
} }

View File

@@ -105,7 +105,7 @@ namespace AssetStudio
var m_ReadAllowed = reader.ReadBoolean(); var m_ReadAllowed = reader.ReadBoolean();
} }
} }
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2) || reader.Game.Type.IsGI()) //2018.2 and up if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
{ {
var m_StreamingMipmaps = reader.ReadBoolean(); var m_StreamingMipmaps = reader.ReadBoolean();
} }

View File

@@ -1,5 +1,7 @@
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic;
using System.Text;
namespace AssetStudio namespace AssetStudio
{ {
@@ -8,16 +10,20 @@ namespace AssetStudio
private const int DataOffset = 0x2A; private const int DataOffset = 0x2A;
private const int KeySize = 0x1000; private const int KeySize = 0x1000;
private const int SeedBlockSize = 0x800; private const int SeedBlockSize = 0x800;
private const int BufferSize = 0x10000;
public static XORStream Decrypt(FileReader reader, Blk blk) public static XORStream Decrypt(FileReader reader, Blk blk)
{ {
reader.Endian = EndianType.LittleEndian; reader.Endian = EndianType.LittleEndian;
var signature = reader.ReadStringToNull(); var signature = reader.ReadStringToNull();
Logger.Verbose($"Signature: {signature}");
var count = reader.ReadInt32(); var count = reader.ReadInt32();
Logger.Verbose($"Key size: {count}");
var key = reader.ReadBytes(count); var key = reader.ReadBytes(count);
reader.Position += count; reader.Position += count;
var seedSize = Math.Min(reader.ReadInt16(), blk.SBox.IsNullOrEmpty() ? SeedBlockSize : SeedBlockSize * 2); var seedSize = Math.Min(reader.ReadInt16(), blk.SBox.IsNullOrEmpty() ? SeedBlockSize : SeedBlockSize * 2);
Logger.Verbose($"Seed size: 0x{seedSize:X8}");
if (!blk.SBox.IsNullOrEmpty() && blk.Type.IsGI()) if (!blk.SBox.IsNullOrEmpty() && blk.Type.IsGI())
{ {
@@ -47,6 +53,8 @@ namespace AssetStudio
var keyHigh = BinaryPrimitives.ReadUInt64LittleEndian(key.AsSpan(8, 8)); var keyHigh = BinaryPrimitives.ReadUInt64LittleEndian(key.AsSpan(8, 8));
var seed = keyLow ^ keyHigh ^ keySeed ^ blk.InitSeed; var seed = keyLow ^ keyHigh ^ keySeed ^ blk.InitSeed;
Logger.Verbose($"Seed: 0x{seed:X8}");
var mt64 = new MT19937_64(seed); var mt64 = new MT19937_64(seed);
var xorpad = new byte[KeySize]; var xorpad = new byte[KeySize];
for (int i = 0; i < KeySize; i += 8) for (int i = 0; i < KeySize; i += 8)
@@ -56,5 +64,48 @@ namespace AssetStudio
return new XORStream(reader.BaseStream, DataOffset, xorpad); return new XORStream(reader.BaseStream, DataOffset, xorpad);
} }
public static IEnumerable<long> GetOffsets(this XORStream stream, string path)
{
if (AssetsHelper.TryGet(path, out var offsets))
{
foreach(var offset in offsets)
{
stream.Offset = offset;
yield return offset;
}
}
else
{
using var reader = new FileReader(path, stream, true);
var signature = reader.FileType switch
{
FileType.BundleFile => "UnityFS\x00",
FileType.Mhy0File => "mhy0",
_ => throw new InvalidOperationException()
};
Logger.Verbose($"Prased signature: {signature}");
var signatureBytes = Encoding.UTF8.GetBytes(signature);
var buffer = BigArrayPool<byte>.Shared.Rent(BufferSize);
while (stream.Remaining > 0)
{
var index = 0;
var absOffset = stream.AbsolutePosition;
var read = stream.Read(buffer);
while (index < read)
{
index = buffer.AsSpan(0, read).Search(signatureBytes, index);
if (index == -1) break;
var offset = absOffset + index;
stream.Offset = offset;
yield return offset;
index++;
}
}
BigArrayPool<byte>.Shared.Return(buffer);
}
}
} }
} }

View File

@@ -21,6 +21,7 @@ namespace AssetStudio
var encryptedBlockSize = Math.Min(0x10 * ((data.Length - 0x94) >> 7), BlockSize); var encryptedBlockSize = Math.Min(0x10 * ((data.Length - 0x94) >> 7), BlockSize);
Logger.Verbose($"Encrypted block size: {encryptedBlockSize}");
if (!mr0k.InitVector.IsNullOrEmpty()) if (!mr0k.InitVector.IsNullOrEmpty())
{ {
for (int i = 0; i < mr0k.InitVector.Length; i++) for (int i = 0; i < mr0k.InitVector.Length; i++)
@@ -47,6 +48,8 @@ namespace AssetStudio
var seed2 = BinaryPrimitives.ReadUInt64LittleEndian(key3); var seed2 = BinaryPrimitives.ReadUInt64LittleEndian(key3);
var seed = seed2 ^ seed1 ^ (seed1 + (uint)data.Length - 20); var seed = seed2 ^ seed1 ^ (seed1 + (uint)data.Length - 20);
Logger.Verbose($"Seed: 0x{seed:X8}");
var encryptedBlock = data.Slice(0x94, encryptedBlockSize); var encryptedBlock = data.Slice(0x94, encryptedBlockSize);
var seedSpan = BitConverter.GetBytes(seed); var seedSpan = BitConverter.GetBytes(seed);
for (var i = 0; i < encryptedBlockSize; i++) for (var i = 0; i < encryptedBlockSize; i++)

View File

@@ -11,6 +11,8 @@ namespace AssetStudio
private static readonly byte[] Signature = new byte[] { 0xEE, 0xDD }; private static readonly byte[] Signature = new byte[] { 0xEE, 0xDD };
public static void Decrypt(Span<byte> bytes) public static void Decrypt(Span<byte> bytes)
{ {
Logger.Verbose($"Attempting to decrypt block with NetEase encryption...");
var (encryptedOffset, encryptedSize) = ReadHeader(bytes); var (encryptedOffset, encryptedSize) = ReadHeader(bytes);
var encrypted = bytes.Slice(encryptedOffset, encryptedSize); var encrypted = bytes.Slice(encryptedOffset, encryptedSize);
var encryptedInts = MemoryMarshal.Cast<byte, int>(encrypted); var encryptedInts = MemoryMarshal.Cast<byte, int>(encrypted);
@@ -77,7 +79,7 @@ namespace AssetStudio
} }
private static (int, int) ReadHeader(Span<byte> bytes) private static (int, int) ReadHeader(Span<byte> bytes)
{ {
var index = bytes.Search(Signature, 0); var index = bytes.Search(Signature);
if (index == -1 || index >= 0x40) if (index == -1 || index >= 0x40)
{ {
throw new Exception("Header not found !!"); throw new Exception("Header not found !!");
@@ -127,6 +129,7 @@ namespace AssetStudio
throw new Exception("Unsupported version"); throw new Exception("Unsupported version");
} }
var versionString = version.ToString("X4"); var versionString = version.ToString("X4");
Logger.Verbose($"Bundle version: {versionString}");
Encoding.UTF8.GetBytes(versionString, bytes); Encoding.UTF8.GetBytes(versionString, bytes);
} }

View File

@@ -12,6 +12,7 @@ namespace AssetStudio
public static void Decrypt(Span<byte> data, string path) public static void Decrypt(Span<byte> data, string path)
{ {
Logger.Verbose($"Attempting to decrypt block with OPFP encryption...");
if (IsEncryptionBundle(path, out var key, out var version)) if (IsEncryptionBundle(path, out var key, out var version))
{ {
switch (version) switch (version)
@@ -41,30 +42,39 @@ namespace AssetStudio
{ {
if (V1_Prefixes.Any(prefix => relativePath.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) if (V1_Prefixes.Any(prefix => relativePath.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
{ {
Logger.Verbose("Path matches with V1 prefixes, generatring key...");
key = (byte)Path.GetFileName(relativePath).Length; key = (byte)Path.GetFileName(relativePath).Length;
version = 1; version = 1;
Logger.Verbose($"version: {version}, key: {key}");
return true; return true;
} }
else if (V0_Prefixes.Any(prefix => relativePath.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) else if (V0_Prefixes.Any(prefix => relativePath.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
{ {
Logger.Verbose("Path matches with V2 prefixes, generatring key...");
key = (byte)relativePath.Length; key = (byte)relativePath.Length;
version = 0; version = 0;
Logger.Verbose($"version: {version}, key: {key}");
return true; return true;
} }
} }
Logger.Verbose($"Unknown encryption type");
key = 0x00; key = 0x00;
version = 0; version = 0;
return false; return false;
} }
private static bool IsFixedPath(string path, out string fixedPath) private static bool IsFixedPath(string path, out string fixedPath)
{ {
Logger.Verbose($"Fixing path before checking...");
var dirs = path.Split(Path.DirectorySeparatorChar); var dirs = path.Split(Path.DirectorySeparatorChar);
if (dirs.Contains(BaseFolder)) if (dirs.Contains(BaseFolder))
{ {
var idx = Array.IndexOf(dirs, BaseFolder); var idx = Array.IndexOf(dirs, BaseFolder);
Logger.Verbose($"Seperator found at index {idx}");
fixedPath = string.Join(Path.DirectorySeparatorChar, dirs[(idx+1)..]).Replace("\\", "/"); fixedPath = string.Join(Path.DirectorySeparatorChar, dirs[(idx+1)..]).Replace("\\", "/");
return true; return true;
} }
Logger.Verbose($"Unknown path");
fixedPath = string.Empty; fixedPath = string.Empty;
return false; return false;
} }

View File

@@ -28,8 +28,11 @@ namespace AssetStudio
DecryptKey(signatureKey, signatureBytes); DecryptKey(signatureKey, signatureBytes);
var str = Encoding.UTF8.GetString(signatureBytes); var str = Encoding.UTF8.GetString(signatureBytes);
Logger.Verbose($"Decrypted signature is {str}");
if (str != Signature) if (str != Signature)
throw new Exception("Invalid Signature !!"); {
throw new Exception($"Invalid Signature, Expected {Signature} but found {str} instead");
}
DecryptKey(infoKey, infoBytes); DecryptKey(infoKey, infoBytes);
@@ -41,19 +44,20 @@ namespace AssetStudio
var idx = (i % 4 * 4) + (i / 4); var idx = (i % 4 * 4) + (i / 4);
Sub[idx] = subBytes[i]; Sub[idx] = subBytes[i];
} }
} }
public static bool SetKey(Entry entry) public static bool SetKey(Entry entry)
{ {
Logger.Verbose($"Initializing decryptor with key {entry.Key}");
try try
{ {
using (var aes = Aes.Create()) using var aes = Aes.Create();
{
aes.Mode = CipherMode.ECB; aes.Mode = CipherMode.ECB;
aes.Key = Convert.FromHexString(entry.Key); aes.Key = Convert.FromHexString(entry.Key);
Encryptor = aes.CreateEncryptor(); Encryptor = aes.CreateEncryptor();
} Logger.Verbose($"Decryptor initialized !!");
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -19,7 +19,7 @@ namespace AssetStudio
return buffer; return buffer;
} }
public static int Search(this byte[] src, string value, int offset = 0) => Search(src.AsSpan(), Encoding.UTF8.GetBytes(value), offset); public static int Search(this byte[] src, string value, int offset = 0) => Search(src.AsSpan(), Encoding.UTF8.GetBytes(value), offset);
public static int Search(this Span<byte> src, byte[] pattern, int offset) public static int Search(this Span<byte> src, byte[] pattern, int offset = 0)
{ {
int maxFirstCharSlot = src.Length - pattern.Length + 1; int maxFirstCharSlot = src.Length - pattern.Length + 1;
for (int i = offset; i < maxFirstCharSlot; i++) for (int i = offset; i < maxFirstCharSlot; i++)

View File

@@ -13,5 +13,15 @@ namespace AssetStudio
//custom //custom
public string fileName; public string fileName;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"Guid: {guid} | ");
sb.Append($"type: {type} | ");
sb.Append($"pathName: {pathName} | ");
sb.Append($"fileName: {fileName}");
return sb.ToString();
}
} }
} }

View File

@@ -27,12 +27,14 @@ namespace AssetStudio
FullPath = Path.GetFullPath(path); FullPath = Path.GetFullPath(path);
FileName = Path.GetFileName(path); FileName = Path.GetFileName(path);
FileType = CheckFileType(); FileType = CheckFileType();
Logger.Verbose($"File {path} type is {FileType}");
} }
private FileType CheckFileType() private FileType CheckFileType()
{ {
var signature = this.ReadStringToNull(20); var signature = this.ReadStringToNull(20);
Position = 0; Position = 0;
Logger.Verbose($"Parsed signature is {signature}");
switch (signature) switch (signature)
{ {
case "ENCR": case "ENCR":
@@ -47,46 +49,59 @@ namespace AssetStudio
return FileType.BlkFile; return FileType.BlkFile;
default: default:
{ {
Logger.Verbose("signature does not match any of the supported string signatures, attempting to check bytes signatures");
byte[] magic = ReadBytes(2); byte[] magic = ReadBytes(2);
Position = 0; Position = 0;
Logger.Verbose($"Parsed signature is {Convert.ToHexString(magic)}");
if (gzipMagic.SequenceEqual(magic)) if (gzipMagic.SequenceEqual(magic))
{ {
return FileType.GZipFile; return FileType.GZipFile;
} }
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(gzipMagic)}");
Position = 0x20; Position = 0x20;
magic = ReadBytes(6); magic = ReadBytes(6);
Position = 0; Position = 0;
Logger.Verbose($"Parsed signature is {Convert.ToHexString(magic)}");
if (brotliMagic.SequenceEqual(magic)) if (brotliMagic.SequenceEqual(magic))
{ {
return FileType.BrotliFile; return FileType.BrotliFile;
} }
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(brotliMagic)}");
if (IsSerializedFile()) if (IsSerializedFile())
{ {
return FileType.AssetsFile; return FileType.AssetsFile;
} }
magic = ReadBytes(4); magic = ReadBytes(4);
Position = 0; Position = 0;
Logger.Verbose($"Parsed signature is {Convert.ToHexString(magic)}");
if (zipMagic.SequenceEqual(magic) || zipSpannedMagic.SequenceEqual(magic)) if (zipMagic.SequenceEqual(magic) || zipSpannedMagic.SequenceEqual(magic))
{ {
return FileType.ZipFile; return FileType.ZipFile;
} }
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(zipMagic)} or {Convert.ToHexString(zipSpannedMagic)}");
if (mhy0Magic.SequenceEqual(magic)) if (mhy0Magic.SequenceEqual(magic))
{ {
return FileType.Mhy0File; return FileType.Mhy0File;
} }
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(mhy0Magic)}");
magic = ReadBytes(7); magic = ReadBytes(7);
Position = 0; Position = 0;
Logger.Verbose($"Parsed signature is {Convert.ToHexString(magic)}");
if (narakaMagic.SequenceEqual(magic)) if (narakaMagic.SequenceEqual(magic))
{ {
return FileType.BundleFile; return FileType.BundleFile;
} }
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(narakaMagic)}");
magic = ReadBytes(9); magic = ReadBytes(9);
Position = 0; Position = 0;
Logger.Verbose($"Parsed signature is {Convert.ToHexString(magic)}");
if (gunfireMagic.SequenceEqual(magic)) if (gunfireMagic.SequenceEqual(magic))
{ {
Position = 0x32; Position = 0x32;
return FileType.BundleFile; return FileType.BundleFile;
} }
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(gunfireMagic)}");
Logger.Verbose($"Parsed signature does not match any of the supported signatures, assuming resource file");
return FileType.ResourceFile; return FileType.ResourceFile;
} }
} }
@@ -94,9 +109,12 @@ namespace AssetStudio
private bool IsSerializedFile() private bool IsSerializedFile()
{ {
Logger.Verbose($"Attempting to check if the file is serialized file...");
var fileSize = BaseStream.Length; var fileSize = BaseStream.Length;
if (fileSize < 20) if (fileSize < 20)
{ {
Logger.Verbose($"File size 0x{fileSize:X8} is too small, minimal acceptable size is 0x14, aborting...");
return false; return false;
} }
var m_MetadataSize = ReadUInt32(); var m_MetadataSize = ReadUInt32();
@@ -109,6 +127,7 @@ namespace AssetStudio
{ {
if (fileSize < 48) if (fileSize < 48)
{ {
Logger.Verbose($"File size 0x{fileSize:X8} for version {m_Version} is too small, minimal acceptable size is 0x30, aborting...");
Position = 0; Position = 0;
return false; return false;
} }
@@ -119,12 +138,15 @@ namespace AssetStudio
Position = 0; Position = 0;
if (m_FileSize != fileSize) if (m_FileSize != fileSize)
{ {
Logger.Verbose($"Parsed file size 0x{m_FileSize:X8} does not match stream size {fileSize}, file might be corrupted, aborting...");
return false; return false;
} }
if (m_DataOffset > fileSize) if (m_DataOffset > fileSize)
{ {
Logger.Verbose($"Parsed data offset 0x{m_DataOffset:X8} is outside the stream of the size {fileSize}, file might be corrupted, aborting...");
return false; return false;
} }
Logger.Verbose($"Valid serialized file !!");
return true; return true;
} }
} }
@@ -133,8 +155,10 @@ namespace AssetStudio
{ {
public static FileReader PreProcessing(this FileReader reader, Game game) public static FileReader PreProcessing(this FileReader reader, Game game)
{ {
Logger.Verbose($"Applying preprocessing to file {reader.FileName}");
if (reader.FileType == FileType.ResourceFile || !game.Type.IsNormal()) if (reader.FileType == FileType.ResourceFile || !game.Type.IsNormal())
{ {
Logger.Verbose("File is encrypted !!");
switch (game.Type) switch (game.Type)
{ {
case GameType.GI_Pack: case GameType.GI_Pack:
@@ -147,16 +171,13 @@ namespace AssetStudio
reader = DecryptEnsembleStar(reader); reader = DecryptEnsembleStar(reader);
break; break;
case GameType.OPFP: case GameType.OPFP:
reader = ParseOPFP(reader);
break;
case GameType.FakeHeader: case GameType.FakeHeader:
case GameType.ShiningNikki:
reader = ParseFakeHeader(reader); reader = ParseFakeHeader(reader);
break; break;
case GameType.FantasyOfWind: case GameType.FantasyOfWind:
reader = DecryptFantasyOfWind(reader); reader = DecryptFantasyOfWind(reader);
break; break;
case GameType.ShiningNikki:
reader = ParseShiningNikki(reader);
break; break;
case GameType.HelixWaltz2: case GameType.HelixWaltz2:
reader = ParseHelixWaltz2(reader); reader = ParseHelixWaltz2(reader);
@@ -174,6 +195,7 @@ namespace AssetStudio
} }
if (reader.FileType == FileType.BundleFile && game.Type.IsBlockFile()) if (reader.FileType == FileType.BundleFile && game.Type.IsBlockFile())
{ {
Logger.Verbose("File might have multiple bundles !!");
try try
{ {
var signature = reader.ReadStringToNull(); var signature = reader.ReadStringToNull();
@@ -183,6 +205,8 @@ namespace AssetStudio
var size = reader.ReadInt64(); var size = reader.ReadInt64();
if (!(signature == "UnityFS" && size == reader.BaseStream.Length)) if (!(signature == "UnityFS" && size == reader.BaseStream.Length))
{ {
Logger.Verbose($"Found signature UnityFS, expected bundle size is 0x{size:X8}, found 0x{reader.BaseStream.Length} instead !!");
Logger.Verbose("Loading as block file !!");
reader.FileType = FileType.BlockFile; reader.FileType = FileType.BlockFile;
} }
} }
@@ -190,6 +214,7 @@ namespace AssetStudio
reader.Position = 0; reader.Position = 0;
} }
Logger.Verbose("No preprocessing is needed");
return reader; return reader;
} }
} }

View File

@@ -17,7 +17,7 @@ namespace AssetStudio
public class ImportedFrame public class ImportedFrame
{ {
public string Name { get; set; } public string Name { get; set; }
public Vector3 LocalRotation { get; set; } public Quaternion LocalRotation { get; set; }
public Vector3 LocalPosition { get; set; } public Vector3 LocalPosition { get; set; }
public Vector3 LocalScale { get; set; } public Vector3 LocalScale { get; set; }
public ImportedFrame Parent { get; set; } public ImportedFrame Parent { get; set; }
@@ -248,7 +248,7 @@ namespace AssetStudio
{ {
public string Path { get; set; } public string Path { get; set; }
public List<ImportedKeyframe<Vector3>> Scalings = new List<ImportedKeyframe<Vector3>>(); public List<ImportedKeyframe<Vector3>> Scalings = new List<ImportedKeyframe<Vector3>>();
public List<ImportedKeyframe<Vector3>> Rotations = new List<ImportedKeyframe<Vector3>>(); public List<ImportedKeyframe<Quaternion>> Rotations = new List<ImportedKeyframe<Quaternion>>();
public List<ImportedKeyframe<Vector3>> Translations = new List<ImportedKeyframe<Vector3>>(); public List<ImportedKeyframe<Vector3>> Translations = new List<ImportedKeyframe<Vector3>>();
public ImportedBlendShape BlendShape; public ImportedBlendShape BlendShape;
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -34,4 +35,39 @@ namespace AssetStudio
Console.WriteLine("[{0}] {1}", loggerEvent, message); Console.WriteLine("[{0}] {1}", loggerEvent, message);
} }
} }
public sealed class FileLogger : ILogger
{
private const string LogFileName = "log.txt";
private const string PrevLogFileName = "log_prev.txt";
private readonly object LockWriter = new object();
public StreamWriter Writer;
public FileLogger()
{
var logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogFileName);
var prevLogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, PrevLogFileName);
if (File.Exists(logPath))
{
File.Move(logPath, prevLogPath, true);
}
Writer = new StreamWriter(logPath, true) { AutoFlush = true };
}
~FileLogger()
{
Writer?.Dispose();
}
public void Log(LoggerEvent loggerEvent, string message, bool silent = false)
{
if (silent)
return;
lock (LockWriter)
{
Writer.WriteLine($"[{DateTime.Now}][{loggerEvent}] {message}");
}
}
}
} }

View File

@@ -18,7 +18,9 @@ namespace AssetStudio
{ {
public static void MergeSplitAssets(string path, bool allDirectories = false) public static void MergeSplitAssets(string path, bool allDirectories = false)
{ {
Logger.Verbose($"Processing split assets (.splitX) prior to loading files...");
var splitFiles = Directory.GetFiles(path, "*.split0", allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); var splitFiles = Directory.GetFiles(path, "*.split0", allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
Logger.Verbose($"Found {splitFiles.Length} split files, attempting to merge...");
foreach (var splitFile in splitFiles) foreach (var splitFile in splitFiles)
{ {
var destFile = Path.GetFileNameWithoutExtension(splitFile); var destFile = Path.GetFileNameWithoutExtension(splitFile);
@@ -27,6 +29,7 @@ namespace AssetStudio
if (!File.Exists(destFull)) if (!File.Exists(destFull))
{ {
var splitParts = Directory.GetFiles(destPath, destFile + ".split*"); var splitParts = Directory.GetFiles(destPath, destFile + ".split*");
Logger.Verbose($"Creating {destFull} where split files will be combined");
using (var destStream = File.Create(destFull)) using (var destStream = File.Create(destFull))
{ {
for (int i = 0; i < splitParts.Length; i++) for (int i = 0; i < splitParts.Length; i++)
@@ -35,6 +38,7 @@ namespace AssetStudio
using (var sourceStream = File.OpenRead(splitPart)) using (var sourceStream = File.OpenRead(splitPart))
{ {
sourceStream.CopyTo(destStream); sourceStream.CopyTo(destStream);
Logger.Verbose($"{splitPart} has been combined into {destFull}");
} }
} }
} }
@@ -44,6 +48,7 @@ namespace AssetStudio
public static string[] ProcessingSplitFiles(List<string> selectFile) public static string[] ProcessingSplitFiles(List<string> selectFile)
{ {
Logger.Verbose("Filter out paths that has .split and has the same name");
var splitFiles = selectFile.Where(x => x.Contains(".split")) var splitFiles = selectFile.Where(x => x.Contains(".split"))
.Select(x => Path.Combine(Path.GetDirectoryName(x), Path.GetFileNameWithoutExtension(x))) .Select(x => Path.Combine(Path.GetDirectoryName(x), Path.GetFileNameWithoutExtension(x)))
.Distinct() .Distinct()
@@ -61,6 +66,7 @@ namespace AssetStudio
public static FileReader DecompressGZip(FileReader reader) public static FileReader DecompressGZip(FileReader reader)
{ {
Logger.Verbose($"Decompressing GZip file {reader.FileName} into memory");
using (reader) using (reader)
{ {
var stream = new MemoryStream(); var stream = new MemoryStream();
@@ -75,6 +81,7 @@ namespace AssetStudio
public static FileReader DecompressBrotli(FileReader reader) public static FileReader DecompressBrotli(FileReader reader)
{ {
Logger.Verbose($"Decompressing Brotli file {reader.FileName} into memory");
using (reader) using (reader)
{ {
var stream = new MemoryStream(); var stream = new MemoryStream();
@@ -89,24 +96,31 @@ namespace AssetStudio
public static FileReader DecryptPack(FileReader reader, Game game) public static FileReader DecryptPack(FileReader reader, Game game)
{ {
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Pack encryption");
const int PackSize = 0x880; const int PackSize = 0x880;
const string PackSignature = "pack"; const string PackSignature = "pack";
const string UnityFSSignature = "UnityFS"; const string UnityFSSignature = "UnityFS";
var data = reader.ReadBytes((int)reader.Length); var data = reader.ReadBytes((int)reader.Length);
var idx = data.Search(PackSignature); var packIdx = data.Search(PackSignature);
if (idx == -1) if (packIdx == -1)
{ {
Logger.Verbose($"Signature {PackSignature} was not found, aborting...");
reader.Position = 0; reader.Position = 0;
return reader; return reader;
} }
idx = data.Search("mr0k", idx); Logger.Verbose($"Found signature {PackSignature} at offset 0x{packIdx:X8}");
if (idx == -1) var mr0kIdx = data.Search("mr0k", packIdx);
if (mr0kIdx == -1)
{ {
Logger.Verbose("Signature mr0k was not found, aborting...");
reader.Position = 0; reader.Position = 0;
return reader; return reader;
} }
Logger.Verbose($"Found signature mr0k signature at offset 0x{mr0kIdx:X8}");
Logger.Verbose("Attempting to process pack chunks...");
var ms = new MemoryStream(); var ms = new MemoryStream();
try try
{ {
@@ -121,9 +135,12 @@ namespace AssetStudio
var signature = reader.ReadStringToNull(4); var signature = reader.ReadStringToNull(4);
if (signature == PackSignature) if (signature == PackSignature)
{ {
Logger.Verbose($"Found {PackSignature} chunk at position {reader.Position - PackSignature.Length}");
var isMr0k = reader.ReadBoolean(); var isMr0k = reader.ReadBoolean();
Logger.Verbose("Chunk is mr0k encrypted");
var blockSize = BinaryPrimitives.ReadInt32LittleEndian(reader.ReadBytes(4)); var blockSize = BinaryPrimitives.ReadInt32LittleEndian(reader.ReadBytes(4));
Logger.Verbose($"Chunk size is 0x{blockSize:X8}");
Span<byte> buffer = new byte[blockSize]; Span<byte> buffer = new byte[blockSize];
reader.Read(buffer); reader.Read(buffer);
if (isMr0k) if (isMr0k)
@@ -134,6 +151,7 @@ namespace AssetStudio
if (bundleSize == 0) if (bundleSize == 0)
{ {
Logger.Verbose("This is header chunk !! attempting to read the bundle size");
using var blockReader = new EndianBinaryReader(new MemoryStream(buffer.ToArray())); using var blockReader = new EndianBinaryReader(new MemoryStream(buffer.ToArray()));
var header = new Header() var header = new Header()
{ {
@@ -144,17 +162,21 @@ namespace AssetStudio
size = blockReader.ReadInt64() size = blockReader.ReadInt64()
}; };
bundleSize = header.size; bundleSize = header.size;
Logger.Verbose($"Bundle size is 0x{bundleSize:X8}");
} }
readSize += buffer.Length; readSize += buffer.Length;
if (readSize % (PackSize - 0x80) == 0) if (readSize % (PackSize - 0x80) == 0)
{ {
reader.Position += PackSize - 9 - blockSize; var padding = PackSize - 9 - blockSize;
reader.Position += padding;
Logger.Verbose($"Skip 0x{padding:X8} padding");
} }
if (readSize == bundleSize) if (readSize == bundleSize)
{ {
Logger.Verbose($"Bundle has been read entirely !!");
readSize = 0; readSize = 0;
bundleSize = 0; bundleSize = 0;
} }
@@ -166,6 +188,7 @@ namespace AssetStudio
signature = reader.ReadStringToNull(); signature = reader.ReadStringToNull();
if (signature == UnityFSSignature) if (signature == UnityFSSignature)
{ {
Logger.Verbose($"Found {UnityFSSignature} chunk at position {reader.Position - (UnityFSSignature.Length + 1)}");
var header = new Header() var header = new Header()
{ {
signature = reader.ReadStringToNull(), signature = reader.ReadStringToNull(),
@@ -175,6 +198,7 @@ namespace AssetStudio
size = reader.ReadInt64() size = reader.ReadInt64()
}; };
Logger.Verbose($"Bundle size is 0x{header.size:X8}");
reader.Position = pos; reader.Position = pos;
reader.BaseStream.CopyTo(ms, header.size); reader.BaseStream.CopyTo(ms, header.size);
continue; continue;
@@ -196,15 +220,19 @@ namespace AssetStudio
reader.Dispose(); reader.Dispose();
} }
Logger.Verbose("Decrypted pack file successfully !!");
ms.Position = 0; ms.Position = 0;
return new FileReader(reader.FullPath, ms); return new FileReader(reader.FullPath, ms);
} }
public static FileReader DecryptMark(FileReader reader) public static FileReader DecryptMark(FileReader reader)
{ {
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Mark encryption");
var signature = reader.ReadStringToNull(4); var signature = reader.ReadStringToNull(4);
if (signature != "mark") if (signature != "mark")
{ {
Logger.Verbose($"Expected signature mark, found {signature} instead, aborting...");
reader.Position = 0; reader.Position = 0;
return reader; return reader;
} }
@@ -240,6 +268,7 @@ namespace AssetStudio
} }
} }
Logger.Verbose("Decrypted mark file successfully !!");
reader.Dispose(); reader.Dispose();
dataStream.Position = 0; dataStream.Position = 0;
return new FileReader(reader.FullPath, dataStream); return new FileReader(reader.FullPath, dataStream);
@@ -247,8 +276,10 @@ namespace AssetStudio
public static FileReader DecryptEnsembleStar(FileReader reader) public static FileReader DecryptEnsembleStar(FileReader reader)
{ {
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Ensemble Star encryption");
if (Path.GetExtension(reader.FileName) != ".z") if (Path.GetExtension(reader.FileName) != ".z")
{ {
Logger.Verbose($"Expected file extension .z, found {Path.GetExtension(reader.FileName)} instead, aborting...");
return reader; return reader;
} }
using (reader) using (reader)
@@ -269,46 +300,42 @@ namespace AssetStudio
data[i] = (byte)(EnsembleStarKey1[k1] ^ ((size ^ EnsembleStarKey3[k3] ^ data[i] ^ EnsembleStarKey2[k2]) + remaining)); data[i] = (byte)(EnsembleStarKey1[k1] ^ ((size ^ EnsembleStarKey3[k3] ^ data[i] ^ EnsembleStarKey2[k2]) + remaining));
} }
Logger.Verbose("Decrypted Ensemble Star file successfully !!");
return new FileReader(reader.FullPath, new MemoryStream(data)); return new FileReader(reader.FullPath, new MemoryStream(data));
} }
} }
public static FileReader ParseOPFP(FileReader reader)
{
var stream = reader.BaseStream;
var data = reader.ReadBytes(0x1000);
var idx = data.Search("UnityFS");
if (idx != -1)
{
stream = new OffsetStream(stream, idx);
}
return new FileReader(reader.FullPath, stream);
}
public static FileReader ParseFakeHeader(FileReader reader) public static FileReader ParseFakeHeader(FileReader reader)
{ {
Logger.Verbose($"Attempting to parse file {reader.FileName} with fake header");
var stream = reader.BaseStream; var stream = reader.BaseStream;
var data = reader.ReadBytes(0x1000); var data = reader.ReadBytes(0x1000);
var idx = data.Search("UnityFS"); var idx = data.Search("UnityFS");
if (idx != -1) if (idx != -1)
{ {
Logger.Verbose($"Found fake header at offset 0x{idx:X8}");
var idx2 = data[(idx + 1)..].Search("UnityFS"); var idx2 = data[(idx + 1)..].Search("UnityFS");
if (idx2 != -1) if (idx2 != -1)
{ {
Logger.Verbose($"Found real header at offset 0x{idx + idx2 + 1:X8}");
stream = new OffsetStream(stream, idx + idx2 + 1); stream = new OffsetStream(stream, idx + idx2 + 1);
} }
else else
{ {
Logger.Verbose("Real header was not found, assuming fake header is the real one");
stream = new OffsetStream(stream, idx); stream = new OffsetStream(stream, idx);
} }
} }
Logger.Verbose("Parsed fake header file successfully !!");
return new FileReader(reader.FullPath, stream); return new FileReader(reader.FullPath, stream);
} }
public static FileReader DecryptFantasyOfWind(FileReader reader) public static FileReader DecryptFantasyOfWind(FileReader reader)
{ {
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Fantasy of Wind encryption");
byte[] encryptKeyName = Encoding.UTF8.GetBytes("28856"); byte[] encryptKeyName = Encoding.UTF8.GetBytes("28856");
const int MinLength = 0xC8; const int MinLength = 0xC8;
const int KeyLength = 8; const int KeyLength = 8;
@@ -319,6 +346,7 @@ namespace AssetStudio
var signature = reader.ReadStringToNull(HeadLength); var signature = reader.ReadStringToNull(HeadLength);
if (string.Compare(signature, "K9999") > 0 || reader.Length <= MinLength) if (string.Compare(signature, "K9999") > 0 || reader.Length <= MinLength)
{ {
Logger.Verbose($"Signature version {signature} is higher than K9999 or stream length {reader.Length} is less than minimum length {MinLength}, aborting...");
reader.Position = 0; reader.Position = 0;
return reader; return reader;
} }
@@ -360,23 +388,13 @@ namespace AssetStudio
reader.BaseStream.CopyTo(ms); reader.BaseStream.CopyTo(ms);
ms.Position = 0; ms.Position = 0;
Logger.Verbose("Decrypted Fantasy of Wind file successfully !!");
return new FileReader(reader.FullPath, ms); return new FileReader(reader.FullPath, ms);
} }
public static FileReader ParseShiningNikki(FileReader reader)
{
var data = reader.ReadBytes(0x1000);
var idx = data.Search("UnityFS");
if (idx == -1)
{
reader.Position = 0;
return reader;
}
var stream = new OffsetStream(reader.BaseStream, idx);
return new FileReader(reader.FullPath, stream);
}
public static FileReader ParseHelixWaltz2(FileReader reader) public static FileReader ParseHelixWaltz2(FileReader reader)
{ {
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Helix Waltz 2 encryption");
var originalHeader = new byte[] { 0x55, 0x6E, 0x69, 0x74, 0x79, 0x46, 0x53, 0x00, 0x00, 0x00, 0x00, 0x07, 0x35, 0x2E, 0x78, 0x2E }; var originalHeader = new byte[] { 0x55, 0x6E, 0x69, 0x74, 0x79, 0x46, 0x53, 0x00, 0x00, 0x00, 0x00, 0x07, 0x35, 0x2E, 0x78, 0x2E };
var signature = reader.ReadStringToNull(); var signature = reader.ReadStringToNull();
@@ -384,6 +402,7 @@ namespace AssetStudio
if (signature != "SzxFS") if (signature != "SzxFS")
{ {
Logger.Verbose($"Expected signature SzxFS, found {signature} instead, aborting...");
reader.Position = 0; reader.Position = 0;
return reader; return reader;
} }
@@ -415,6 +434,7 @@ namespace AssetStudio
data[i] = key[idx]; data[i] = key[idx];
} }
Logger.Verbose("Decrypted Helix Waltz 2 file successfully !!");
MemoryStream ms = new(); MemoryStream ms = new();
ms.Write(originalHeader); ms.Write(originalHeader);
ms.Write(data); ms.Write(data);
@@ -424,6 +444,8 @@ namespace AssetStudio
} }
public static FileReader DecryptAnchorPanic(FileReader reader) public static FileReader DecryptAnchorPanic(FileReader reader)
{ {
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Anchor Panic encryption");
const int BlockSize = 0x800; const int BlockSize = 0x800;
var data = reader.ReadBytes(0x1000); var data = reader.ReadBytes(0x1000);
@@ -432,25 +454,31 @@ namespace AssetStudio
var idx = data.Search("UnityFS"); var idx = data.Search("UnityFS");
if (idx != -1) if (idx != -1)
{ {
Logger.Verbose("Found UnityFS signature, file might not be encrypted");
return ParseFakeHeader(reader); return ParseFakeHeader(reader);
} }
var key = GetKey(Path.GetFileNameWithoutExtension(reader.FileName)); var key = GetKey(Path.GetFileNameWithoutExtension(reader.FileName));
Logger.Verbose($"Calculated key is {key}");
var chunkIndex = 0; var chunkIndex = 0;
MemoryStream ms = new(); MemoryStream ms = new();
while (reader.Remaining > 0) while (reader.Remaining > 0)
{ {
var chunkSize = Math.Min((int)reader.Remaining, BlockSize); var chunkSize = Math.Min((int)reader.Remaining, BlockSize);
Logger.Verbose($"Chunk {chunkIndex} size is {chunkSize}");
var chunk = reader.ReadBytes(chunkSize); var chunk = reader.ReadBytes(chunkSize);
if (IsEncrypt((int)reader.Length, chunkIndex++)) if (IsEncrypt((int)reader.Length, chunkIndex++))
{
Logger.Verbose($"Chunk {chunkIndex} is encrypted, decrypting...");
RC4(chunk, key); RC4(chunk, key);
}
ms.Write(chunk); ms.Write(chunk);
} }
Logger.Verbose("Decrypted Anchor Panic file successfully !!");
ms.Position = 0; ms.Position = 0;
return new FileReader(reader.FullPath, ms); return new FileReader(reader.FullPath, ms);
bool IsEncrypt(int fileSize, int chunkIndex) bool IsEncrypt(int fileSize, int chunkIndex)
@@ -533,11 +561,14 @@ namespace AssetStudio
public static FileReader DecryptDreamscapeAlbireo(FileReader reader) public static FileReader DecryptDreamscapeAlbireo(FileReader reader)
{ {
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Dreamscape Albireo encryption");
var key = new byte[] { 0x1E, 0x1E, 0x01, 0x01, 0xFC }; var key = new byte[] { 0x1E, 0x1E, 0x01, 0x01, 0xFC };
var signature = reader.ReadStringToNull(4); var signature = reader.ReadStringToNull(4);
if (signature != "MJJ") if (signature != "MJJ")
{ {
Logger.Verbose($"Expected signature MJJ, found {signature} instead, aborting...");
reader.Position = 0; reader.Position = 0;
return reader; return reader;
} }
@@ -559,6 +590,8 @@ namespace AssetStudio
var sizeLow = (u5 >> 24 | (u2 << 8)) ^ u1; var sizeLow = (u5 >> 24 | (u2 << 8)) ^ u1;
var size = (long)(sizeHigh << 32 | sizeLow); var size = (long)(sizeHigh << 32 | sizeLow);
Logger.Verbose($"Decrypted File info: Flag 0x{flag:X8} | Compressed blockInfo size 0x{compressedBlocksInfoSize:X8} | Decompressed blockInfo size 0x{uncompressedBlocksInfoSize:X8} | Bundle size 0x{size:X8}");
var blocksInfo = reader.ReadBytes((int)compressedBlocksInfoSize); var blocksInfo = reader.ReadBytes((int)compressedBlocksInfoSize);
for(int i = 0; i < blocksInfo.Length; i++) for(int i = 0; i < blocksInfo.Length; i++)
{ {
@@ -587,6 +620,7 @@ namespace AssetStudio
reader.BaseStream.CopyTo(ms); reader.BaseStream.CopyTo(ms);
ms.Position = 0; ms.Position = 0;
Logger.Verbose("Decrypted Dreamscape Albireo file successfully !!");
return new FileReader(reader.FullPath, ms); return new FileReader(reader.FullPath, ms);
static uint Scrample(uint value) => (value >> 5) & 0xFFE000 | (value >> 29) | (value << 14) & 0xFF000000 | (8 * value) & 0x1FF8; static uint Scrample(uint value) => (value >> 5) & 0xFFE000 | (value >> 29) | (value << 14) & 0xFF000000 | (8 * value) & 0x1FF8;
@@ -594,6 +628,8 @@ namespace AssetStudio
public static FileReader DecryptImaginaryFest(FileReader reader) public static FileReader DecryptImaginaryFest(FileReader reader)
{ {
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Imaginary Fest encryption");
const string dataRoot = "data"; const string dataRoot = "data";
var key = new byte[] { 0xBD, 0x65, 0xF2, 0x4F, 0xBE, 0xD1, 0x36, 0xD4, 0x95, 0xFE, 0x64, 0x94, 0xCB, 0xD3, 0x7E, 0x91, 0x57, 0xB7, 0x94, 0xB7, 0x9F, 0x70, 0xB2, 0xA9, 0xA0, 0xD5, 0x4E, 0x36, 0xC6, 0x40, 0xE0, 0x2F, 0x4E, 0x6E, 0x76, 0x6D, 0xCD, 0xAE, 0xEA, 0x05, 0x13, 0x6B, 0xA7, 0x84, 0xFF, 0xED, 0x90, 0x91, 0x15, 0x7E, 0xF1, 0xF8, 0xA5, 0x9C, 0xB6, 0xDE, 0xF9, 0x56, 0x57, 0x18, 0xBF, 0x94, 0x63, 0x6F, 0x1B, 0xE2, 0x92, 0xD2, 0x7E, 0x25, 0x95, 0x23, 0x24, 0xCB, 0x93, 0xD3, 0x36, 0xD9, 0x18, 0x11, 0xF5, 0x50, 0x18, 0xE4, 0x22, 0x28, 0xD8, 0xE2, 0x1A, 0x57, 0x1E, 0x04, 0x88, 0xA5, 0x84, 0xC0, 0x6C, 0x3B, 0x46, 0x62, 0xCE, 0x85, 0x10, 0x2E, 0xA0, 0xDC, 0xD3, 0x09, 0xB2, 0xB6, 0xA4, 0x8D, 0xAF, 0x74, 0x36, 0xF7, 0x9A, 0x3F, 0x98, 0xDA, 0x62, 0x57, 0x71, 0x75, 0x92, 0x05, 0xA3, 0xB2, 0x7C, 0xCA, 0xFB, 0x1E, 0xBE, 0xC9, 0x24, 0xC1, 0xD2, 0xB9, 0xDE, 0xE4, 0x7E, 0xF3, 0x0F, 0xB4, 0xFB, 0xA2, 0xC1, 0xC2, 0x14, 0x5C, 0x78, 0x13, 0x74, 0x41, 0x8D, 0x79, 0xF4, 0x3C, 0x49, 0x92, 0x98, 0xF2, 0xCD, 0x8C, 0x09, 0xA6, 0x40, 0x34, 0x51, 0x1C, 0x11, 0x2B, 0xE0, 0x6B, 0x42, 0x9C, 0x86, 0x41, 0x06, 0xF6, 0xD2, 0x87, 0xF1, 0x10, 0x26, 0x89, 0xC2, 0x7B, 0x2A, 0x5D, 0x1C, 0xDA, 0x92, 0xC8, 0x93, 0x59, 0xF9, 0x60, 0xD0, 0xB5, 0x1E, 0xD5, 0x75, 0x56, 0xA0, 0x05, 0x83, 0x90, 0xAC, 0x72, 0xC8, 0x10, 0x09, 0xED, 0x1A, 0x46, 0xD9, 0x39, 0x6B, 0x9E, 0x19, 0x5E, 0x51, 0x44, 0x09, 0x0D, 0x74, 0xAB, 0xA8, 0xF9, 0x32, 0x43, 0xBC, 0xD2, 0xED, 0x7B, 0x6C, 0x75, 0x32, 0x24, 0x14, 0x43, 0x5D, 0x98, 0xB2, 0xFC, 0xFB, 0xF5, 0x9A, 0x19, 0x03, 0xB0, 0xB7, 0xAC, 0xAE, 0x8B }; var key = new byte[] { 0xBD, 0x65, 0xF2, 0x4F, 0xBE, 0xD1, 0x36, 0xD4, 0x95, 0xFE, 0x64, 0x94, 0xCB, 0xD3, 0x7E, 0x91, 0x57, 0xB7, 0x94, 0xB7, 0x9F, 0x70, 0xB2, 0xA9, 0xA0, 0xD5, 0x4E, 0x36, 0xC6, 0x40, 0xE0, 0x2F, 0x4E, 0x6E, 0x76, 0x6D, 0xCD, 0xAE, 0xEA, 0x05, 0x13, 0x6B, 0xA7, 0x84, 0xFF, 0xED, 0x90, 0x91, 0x15, 0x7E, 0xF1, 0xF8, 0xA5, 0x9C, 0xB6, 0xDE, 0xF9, 0x56, 0x57, 0x18, 0xBF, 0x94, 0x63, 0x6F, 0x1B, 0xE2, 0x92, 0xD2, 0x7E, 0x25, 0x95, 0x23, 0x24, 0xCB, 0x93, 0xD3, 0x36, 0xD9, 0x18, 0x11, 0xF5, 0x50, 0x18, 0xE4, 0x22, 0x28, 0xD8, 0xE2, 0x1A, 0x57, 0x1E, 0x04, 0x88, 0xA5, 0x84, 0xC0, 0x6C, 0x3B, 0x46, 0x62, 0xCE, 0x85, 0x10, 0x2E, 0xA0, 0xDC, 0xD3, 0x09, 0xB2, 0xB6, 0xA4, 0x8D, 0xAF, 0x74, 0x36, 0xF7, 0x9A, 0x3F, 0x98, 0xDA, 0x62, 0x57, 0x71, 0x75, 0x92, 0x05, 0xA3, 0xB2, 0x7C, 0xCA, 0xFB, 0x1E, 0xBE, 0xC9, 0x24, 0xC1, 0xD2, 0xB9, 0xDE, 0xE4, 0x7E, 0xF3, 0x0F, 0xB4, 0xFB, 0xA2, 0xC1, 0xC2, 0x14, 0x5C, 0x78, 0x13, 0x74, 0x41, 0x8D, 0x79, 0xF4, 0x3C, 0x49, 0x92, 0x98, 0xF2, 0xCD, 0x8C, 0x09, 0xA6, 0x40, 0x34, 0x51, 0x1C, 0x11, 0x2B, 0xE0, 0x6B, 0x42, 0x9C, 0x86, 0x41, 0x06, 0xF6, 0xD2, 0x87, 0xF1, 0x10, 0x26, 0x89, 0xC2, 0x7B, 0x2A, 0x5D, 0x1C, 0xDA, 0x92, 0xC8, 0x93, 0x59, 0xF9, 0x60, 0xD0, 0xB5, 0x1E, 0xD5, 0x75, 0x56, 0xA0, 0x05, 0x83, 0x90, 0xAC, 0x72, 0xC8, 0x10, 0x09, 0xED, 0x1A, 0x46, 0xD9, 0x39, 0x6B, 0x9E, 0x19, 0x5E, 0x51, 0x44, 0x09, 0x0D, 0x74, 0xAB, 0xA8, 0xF9, 0x32, 0x43, 0xBC, 0xD2, 0xED, 0x7B, 0x6C, 0x75, 0x32, 0x24, 0x14, 0x43, 0x5D, 0x98, 0xB2, 0xFC, 0xFB, 0xF5, 0x9A, 0x19, 0x03, 0xB0, 0xB7, 0xAC, 0xAE, 0x8B };
@@ -601,12 +637,14 @@ namespace AssetStudio
var signature = Encoding.UTF8.GetString(signatureBytes[..7]); var signature = Encoding.UTF8.GetString(signatureBytes[..7]);
if (signature == "UnityFS") if (signature == "UnityFS")
{ {
Logger.Verbose("Found UnityFS signature, file might not be encrypted");
reader.Position = 0; reader.Position = 0;
return reader; return reader;
} }
if (signatureBytes[7] != 0) if (signatureBytes[7] != 0)
{ {
Logger.Verbose($"File might be encrypted with a byte xorkey 0x{signatureBytes[7]:X8}, attemping to decrypting...");
var xorKey = signatureBytes[7]; var xorKey = signatureBytes[7];
for (int i = 0; i < signatureBytes.Length; i++) for (int i = 0; i < signatureBytes.Length; i++)
{ {
@@ -615,12 +653,14 @@ namespace AssetStudio
signature = Encoding.UTF8.GetString(signatureBytes[..7]); signature = Encoding.UTF8.GetString(signatureBytes[..7]);
if (signature == "UnityFS") if (signature == "UnityFS")
{ {
Logger.Verbose("Found UnityFS signature, key is valid, decrypting the rest of the stream");
var remaining = reader.ReadBytes((int)reader.Remaining); var remaining = reader.ReadBytes((int)reader.Remaining);
for (int i = 0; i < remaining.Length; i++) for (int i = 0; i < remaining.Length; i++)
{ {
remaining[i] ^= xorKey; remaining[i] ^= xorKey;
} }
Logger.Verbose("Decrypted Imaginary Fest file successfully !!");
var stream = new MemoryStream(); var stream = new MemoryStream();
stream.Write(signatureBytes); stream.Write(signatureBytes);
stream.Write(remaining); stream.Write(remaining);
@@ -635,25 +675,33 @@ namespace AssetStudio
var startIdx = Array.FindIndex(paths, x => x == dataRoot); var startIdx = Array.FindIndex(paths, x => x == dataRoot);
if (startIdx != -1 && startIdx != paths.Length - 1) if (startIdx != -1 && startIdx != paths.Length - 1)
{ {
Logger.Verbose("File is in the data folder !!");
var path = string.Join(Path.AltDirectorySeparatorChar, paths[(startIdx+1)..]); var path = string.Join(Path.AltDirectorySeparatorChar, paths[(startIdx+1)..]);
var offset = GetLoadAssetBundleOffset(path); var offset = GetLoadAssetBundleOffset(path);
if (offset > 0 && offset < reader.Length) if (offset > 0 && offset < reader.Length)
{ {
Logger.Verbose($"Calculated offset is 0x{offset:X8}, attempting to read signature...");
reader.Position = offset; reader.Position = offset;
signature = reader.ReadStringToNull(7); signature = reader.ReadStringToNull(7);
if (signature == "UnityFS") if (signature == "UnityFS")
{ {
Logger.Verbose($"Found UnityFS signature, file starts at 0x{offset:X8} !!");
Logger.Verbose("Parsed Imaginary Fest file successfully !!");
reader.Position = offset; reader.Position = offset;
return new FileReader(reader.FullPath, new MemoryStream(reader.ReadBytes((int)reader.Remaining))); return new FileReader(reader.FullPath, new MemoryStream(reader.ReadBytes((int)reader.Remaining)));
} }
} }
Logger.Verbose($"Invalid offset, attempting to generate key...");
reader.Position = 0; reader.Position = 0;
var data = reader.ReadBytes((int)reader.Remaining); var data = reader.ReadBytes((int)reader.Remaining);
var key_value = GetHashCode(path); var key_value = GetHashCode(path);
Logger.Verbose($"Generated key is 0x{key_value:X8}, decrypting...");
Decrypt(data, key_value); Decrypt(data, key_value);
Logger.Verbose("Decrypted Imaginary Fest file successfully !!");
return new FileReader(reader.FullPath, new MemoryStream(data)); return new FileReader(reader.FullPath, new MemoryStream(data));
} }
Logger.Verbose("File doesn't match any of the encryption types");
reader.Position = 0; reader.Position = 0;
return reader; return reader;

View File

@@ -9,5 +9,13 @@ namespace AssetStudio
{ {
public int localSerializedFileIndex; public int localSerializedFileIndex;
public long localIdentifierInFile; public long localIdentifierInFile;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"localSerializedFileIndex: {localSerializedFileIndex} | ");
sb.Append($"localIdentifierInFile: {localIdentifierInFile}");
return sb.ToString();
}
} }
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -7,21 +8,79 @@ namespace AssetStudio
{ {
public static class Logger public static class Logger
{ {
public static ILogger Default = new DummyLogger(); private static bool _fileLogging = true;
public static bool Silent = false;
public static void Verbose(string message) => Default.Log(LoggerEvent.Verbose, message, Silent); public static ILogger Default = new DummyLogger();
public static void Debug(string message) => Default.Log(LoggerEvent.Debug, message, Silent); public static ILogger File;
public static void Info(string message) => Default.Log(LoggerEvent.Info, message, Silent); public static bool Silent = false;
public static void Warning(string message) => Default.Log(LoggerEvent.Warning, message, Silent); public static bool LogVerbose = false;
public static void Error(string message) => Default.Log(LoggerEvent.Error, message, Silent);
public static bool FileLogging
{
get => _fileLogging;
set
{
_fileLogging = value;
if (_fileLogging)
{
File = new FileLogger();
}
else
{
((FileLogger)File)?.Writer?.Dispose();
File = null;
}
}
}
public static void Verbose(string message)
{
if (LogVerbose)
{
try
{
var callerMethod = new StackTrace().GetFrame(1).GetMethod();
var callerMethodClass = callerMethod.ReflectedType.Name;
if (!string.IsNullOrEmpty(callerMethodClass))
{
message = $"[{callerMethodClass}] {message}";
}
}
catch (Exception) { }
if (FileLogging) File.Log(LoggerEvent.Verbose, message);
Default.Log(LoggerEvent.Verbose, message, Silent);
}
}
public static void Debug(string message)
{
if (FileLogging) File.Log(LoggerEvent.Debug, message);
Default.Log(LoggerEvent.Debug, message, Silent);
}
public static void Info(string message)
{
if (FileLogging) File.Log(LoggerEvent.Info, message);
Default.Log(LoggerEvent.Info, message, Silent);
}
public static void Warning(string message)
{
if (FileLogging) File.Log(LoggerEvent.Warning, message);
Default.Log(LoggerEvent.Warning, message, Silent);
}
public static void Error(string message)
{
if (FileLogging) File.Log(LoggerEvent.Error, message);
Default.Log(LoggerEvent.Error, message, Silent);
}
public static void Error(string message, Exception e) public static void Error(string message, Exception e)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine(message); sb.AppendLine(message);
sb.AppendLine(e.ToString()); sb.AppendLine(e.ToString());
Default.Log(LoggerEvent.Error, sb.ToString(), Silent);
message = sb.ToString();
if (FileLogging) File.Log(LoggerEvent.Error, message);
Default.Log(LoggerEvent.Error, message, Silent);
} }
} }
} }

View File

@@ -63,6 +63,7 @@ namespace AssetStudio
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z) && W.Equals(other.W); return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z) && W.Equals(other.W);
} }
public static Quaternion Zero => new Quaternion(0, 0, 0, 1);
public static float Dot(Quaternion a, Quaternion b) public static float Dot(Quaternion a, Quaternion b)
{ {
return a.X * b.X + a.Y * b.Y + a.Z * b.Z + a.W * b.W; return a.X * b.X + a.Y * b.Y + a.Z * b.Z + a.W * b.W;

View File

@@ -15,6 +15,8 @@ namespace AssetStudio
public long Offset; public long Offset;
public Mhy0 mhy0; public Mhy0 mhy0;
public long TotalSize => 8 + m_Header.compressedBlocksInfoSize + m_BlocksInfo.Sum(x => x.compressedSize);
public Mhy0File(FileReader reader, string path, Mhy0 mhy0) public Mhy0File(FileReader reader, string path, Mhy0 mhy0)
{ {
this.mhy0 = mhy0; this.mhy0 = mhy0;
@@ -22,6 +24,10 @@ namespace AssetStudio
reader.Endian = EndianType.LittleEndian; reader.Endian = EndianType.LittleEndian;
var signature = reader.ReadStringToNull(4); var signature = reader.ReadStringToNull(4);
Logger.Verbose($"Parsed signature {signature}");
if (signature != "mhy0")
throw new Exception("not a mhy0");
m_Header = new BundleFile.Header m_Header = new BundleFile.Header
{ {
version = 6, version = 6,
@@ -30,6 +36,7 @@ namespace AssetStudio
compressedBlocksInfoSize = reader.ReadUInt32(), compressedBlocksInfoSize = reader.ReadUInt32(),
flags = (ArchiveFlags)0x43 flags = (ArchiveFlags)0x43
}; };
Logger.Verbose($"Header: {m_Header}");
ReadBlocksInfoAndDirectory(reader); ReadBlocksInfoAndDirectory(reader);
using var blocksStream = CreateBlocksStream(path); using var blocksStream = CreateBlocksStream(path);
ReadBlocks(reader, blocksStream); ReadBlocks(reader, blocksStream);
@@ -41,9 +48,11 @@ namespace AssetStudio
var blocksInfo = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize); var blocksInfo = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
DescrambleHeader(blocksInfo); DescrambleHeader(blocksInfo);
Logger.Verbose($"Descrambled blocksInfo signature {Convert.ToHexString(blocksInfo, 0 , 4)}");
using var blocksInfoStream = new MemoryStream(blocksInfo, 0x20, (int)m_Header.compressedBlocksInfoSize - 0x20); using var blocksInfoStream = new MemoryStream(blocksInfo, 0x20, (int)m_Header.compressedBlocksInfoSize - 0x20);
using var blocksInfoReader = new EndianBinaryReader(blocksInfoStream); using var blocksInfoReader = new EndianBinaryReader(blocksInfoStream);
m_Header.uncompressedBlocksInfoSize = blocksInfoReader.ReadMhy0UInt(); m_Header.uncompressedBlocksInfoSize = blocksInfoReader.ReadMhy0UInt();
Logger.Verbose($"uncompressed blocksInfo size: 0x{m_Header.uncompressedBlocksInfoSize:X8}");
var compressedBlocksInfo = blocksInfoReader.ReadBytes((int)blocksInfoReader.Remaining); var compressedBlocksInfo = blocksInfoReader.ReadBytes((int)blocksInfoReader.Remaining);
var uncompressedBlocksInfo = new byte[(int)m_Header.uncompressedBlocksInfoSize]; var uncompressedBlocksInfo = new byte[(int)m_Header.uncompressedBlocksInfoSize];
var numWrite = LZ4Codec.Decode(compressedBlocksInfo, uncompressedBlocksInfo); var numWrite = LZ4Codec.Decode(compressedBlocksInfo, uncompressedBlocksInfo);
@@ -52,10 +61,12 @@ namespace AssetStudio
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {m_Header.uncompressedBlocksInfoSize} bytes"); throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {m_Header.uncompressedBlocksInfoSize} bytes");
} }
Logger.Verbose($"Writing block and directory to blocks stream...");
using var blocksInfoUncompressedStream = new MemoryStream(uncompressedBlocksInfo); using var blocksInfoUncompressedStream = new MemoryStream(uncompressedBlocksInfo);
using var blocksInfoUncompressedReader = new EndianBinaryReader(blocksInfoUncompressedStream); using var blocksInfoUncompressedReader = new EndianBinaryReader(blocksInfoUncompressedStream);
var nodesCount = blocksInfoUncompressedReader.ReadMhy0Int(); var nodesCount = blocksInfoUncompressedReader.ReadMhy0Int();
m_DirectoryInfo = new BundleFile.Node[nodesCount]; m_DirectoryInfo = new BundleFile.Node[nodesCount];
Logger.Verbose($"Directory count: {nodesCount}");
for (int i = 0; i < nodesCount; i++) for (int i = 0; i < nodesCount; i++)
{ {
m_DirectoryInfo[i] = new BundleFile.Node m_DirectoryInfo[i] = new BundleFile.Node
@@ -65,10 +76,13 @@ namespace AssetStudio
offset = blocksInfoUncompressedReader.ReadMhy0Int(), offset = blocksInfoUncompressedReader.ReadMhy0Int(),
size = blocksInfoUncompressedReader.ReadMhy0UInt() size = blocksInfoUncompressedReader.ReadMhy0UInt()
}; };
Logger.Verbose($"Directory {i} Info: {m_DirectoryInfo[i]}");
} }
var blocksInfoCount = blocksInfoUncompressedReader.ReadMhy0Int(); var blocksInfoCount = blocksInfoUncompressedReader.ReadMhy0Int();
m_BlocksInfo = new BundleFile.StorageBlock[blocksInfoCount]; m_BlocksInfo = new BundleFile.StorageBlock[blocksInfoCount];
Logger.Verbose($"Blocks count: {blocksInfoCount}");
for (int i = 0; i < blocksInfoCount; i++) for (int i = 0; i < blocksInfoCount; i++)
{ {
m_BlocksInfo[i] = new BundleFile.StorageBlock m_BlocksInfo[i] = new BundleFile.StorageBlock
@@ -77,6 +91,8 @@ namespace AssetStudio
uncompressedSize = blocksInfoUncompressedReader.ReadMhy0UInt(), uncompressedSize = blocksInfoUncompressedReader.ReadMhy0UInt(),
flags = (StorageBlockFlags)0x43 flags = (StorageBlockFlags)0x43
}; };
Logger.Verbose($"Block {i} Info: {m_BlocksInfo[i]}");
} }
} }
@@ -84,6 +100,7 @@ namespace AssetStudio
{ {
Stream blocksStream; Stream blocksStream;
var uncompressedSizeSum = (int)m_BlocksInfo.Sum(x => x.uncompressedSize); var uncompressedSizeSum = (int)m_BlocksInfo.Sum(x => x.uncompressedSize);
Logger.Verbose($"Total size of decompressed blocks: 0x{uncompressedSizeSum:X8}");
if (uncompressedSizeSum >= int.MaxValue) if (uncompressedSizeSum >= int.MaxValue)
blocksStream = new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose); blocksStream = new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose);
else else
@@ -110,6 +127,7 @@ namespace AssetStudio
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize); var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
DescrambleEntry(compressedBytesSpan); DescrambleEntry(compressedBytesSpan);
Logger.Verbose($"Descrambled block signature {Convert.ToHexString(compressedBytes, 0, 4)}");
var numWrite = LZ4Codec.Decode(compressedBytesSpan[0xC..compressedSize], uncompressedBytesSpan); var numWrite = LZ4Codec.Decode(compressedBytesSpan[0xC..compressedSize], uncompressedBytesSpan);
if (numWrite != uncompressedSize) if (numWrite != uncompressedSize)
{ {
@@ -124,6 +142,8 @@ namespace AssetStudio
private void ReadFiles(Stream blocksStream, string path) private void ReadFiles(Stream blocksStream, string path)
{ {
Logger.Verbose($"Writing files from blocks stream...");
fileList = new StreamFile[m_DirectoryInfo.Length]; fileList = new StreamFile[m_DirectoryInfo.Length];
for (int i = 0; i < m_DirectoryInfo.Length; i++) for (int i = 0; i < m_DirectoryInfo.Length; i++)
{ {

View File

@@ -1,4 +1,6 @@
namespace AssetStudio using System.Text;
namespace AssetStudio
{ {
public class ObjectInfo public class ObjectInfo
{ {
@@ -11,5 +13,18 @@
public long m_PathID; public long m_PathID;
public SerializedType serializedType; public SerializedType serializedType;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"byteStart: 0x{byteStart:X8} | ");
sb.Append($"byteSize: 0x{byteSize:X8} | ");
sb.Append($"typeID: {typeID} | ");
sb.Append($"classID: {classID} | ");
sb.Append($"isDestroyed: {isDestroyed} | ");
sb.Append($"stripped: {stripped} | ");
sb.Append($"PathID: {m_PathID}");
return sb.ToString();
}
} }
} }

View File

@@ -39,10 +39,13 @@ namespace AssetStudio
serializedType = objectInfo.serializedType; serializedType = objectInfo.serializedType;
platform = assetsFile.m_TargetPlatform; platform = assetsFile.m_TargetPlatform;
m_Version = assetsFile.header.m_Version; m_Version = assetsFile.header.m_Version;
Logger.Verbose($"Initialized reader for {type} object with {m_PathID} in file {assetsFile.fileName} !!");
} }
public void Reset() public void Reset()
{ {
Logger.Verbose($"Resetting reader position to object offset 0x{byteStart:X8}...");
Position = byteStart; Position = byteStart;
} }
} }

View File

@@ -60,14 +60,7 @@ namespace AssetStudio
_baseStream.Seek(target, SeekOrigin.Begin); _baseStream.Seek(target, SeekOrigin.Begin);
return Position; return Position;
} }
public override int Read(byte[] buffer, int offset, int count) public override int Read(byte[] buffer, int offset, int count) => _baseStream.Read(buffer, offset, count);
{
if (offset > _baseStream.Length || Position + count > _baseStream.Length)
{
throw new IOException("Unable to read beyond stream bound");
}
return _baseStream.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException(); public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
public override void SetLength(long value) => throw new NotImplementedException(); public override void SetLength(long value) => throw new NotImplementedException();
public override void Flush() => throw new NotImplementedException(); public override void Flush() => throw new NotImplementedException();

View File

@@ -67,16 +67,21 @@ namespace AssetStudio
header.m_FileSize = reader.ReadInt64(); header.m_FileSize = reader.ReadInt64();
header.m_DataOffset = reader.ReadInt64(); header.m_DataOffset = reader.ReadInt64();
reader.ReadInt64(); // unknown reader.ReadInt64(); // unknown
} }
Logger.Verbose($"File {fileName} Info: {header}");
// ReadMetadata // ReadMetadata
if (m_FileEndianess == 0) if (m_FileEndianess == 0)
{ {
reader.Endian = EndianType.LittleEndian; reader.Endian = EndianType.LittleEndian;
Logger.Verbose($"Endianness {reader.Endian}");
} }
if (header.m_Version >= SerializedFileFormatVersion.Unknown_7) if (header.m_Version >= SerializedFileFormatVersion.Unknown_7)
{ {
unityVersion = reader.ReadStringToNull(); unityVersion = reader.ReadStringToNull();
Logger.Verbose($"Unity version {unityVersion}");
SetVersion(unityVersion); SetVersion(unityVersion);
} }
if (header.m_Version >= SerializedFileFormatVersion.Unknown_8) if (header.m_Version >= SerializedFileFormatVersion.Unknown_8)
@@ -84,12 +89,15 @@ namespace AssetStudio
m_TargetPlatform = (BuildTarget)reader.ReadInt32(); m_TargetPlatform = (BuildTarget)reader.ReadInt32();
if (!Enum.IsDefined(typeof(BuildTarget), m_TargetPlatform)) if (!Enum.IsDefined(typeof(BuildTarget), m_TargetPlatform))
{ {
Logger.Verbose($"Parsed target format {m_TargetPlatform} doesn't match any of supported formats, defaulting to {BuildTarget.UnknownPlatform}");
m_TargetPlatform = BuildTarget.UnknownPlatform; m_TargetPlatform = BuildTarget.UnknownPlatform;
} }
else if (game.Type.IsMhyGroup()) else if (game.Type.IsMhyGroup())
{ {
Logger.Verbose($"Selected game {game.Name} is a mhy game, forcing target format {BuildTarget.StandaloneWindows64}");
m_TargetPlatform = BuildTarget.StandaloneWindows64; m_TargetPlatform = BuildTarget.StandaloneWindows64;
} }
Logger.Verbose($"Target format {m_TargetPlatform}");
} }
if (header.m_Version >= SerializedFileFormatVersion.HasTypeTreeHashes) if (header.m_Version >= SerializedFileFormatVersion.HasTypeTreeHashes)
{ {
@@ -99,6 +107,7 @@ namespace AssetStudio
// Read Types // Read Types
int typeCount = reader.ReadInt32(); int typeCount = reader.ReadInt32();
m_Types = new List<SerializedType>(typeCount); m_Types = new List<SerializedType>(typeCount);
Logger.Verbose($"Found {typeCount} serialized types");
for (int i = 0; i < typeCount; i++) for (int i = 0; i < typeCount; i++)
{ {
m_Types.Add(ReadSerializedType(false)); m_Types.Add(ReadSerializedType(false));
@@ -114,6 +123,7 @@ namespace AssetStudio
m_Objects = new List<ObjectInfo>(objectCount); m_Objects = new List<ObjectInfo>(objectCount);
Objects = new List<Object>(objectCount); Objects = new List<Object>(objectCount);
ObjectsDic = new Dictionary<long, Object>(objectCount); ObjectsDic = new Dictionary<long, Object>(objectCount);
Logger.Verbose($"Found {objectCount} objects");
for (int i = 0; i < objectCount; i++) for (int i = 0; i < objectCount; i++)
{ {
var objectInfo = new ObjectInfo(); var objectInfo = new ObjectInfo();
@@ -164,12 +174,14 @@ namespace AssetStudio
{ {
objectInfo.stripped = reader.ReadByte(); objectInfo.stripped = reader.ReadByte();
} }
Logger.Verbose($"Object Info: {objectInfo}");
m_Objects.Add(objectInfo); m_Objects.Add(objectInfo);
} }
if (header.m_Version >= SerializedFileFormatVersion.HasScriptTypeIndex) if (header.m_Version >= SerializedFileFormatVersion.HasScriptTypeIndex)
{ {
int scriptCount = reader.ReadInt32(); int scriptCount = reader.ReadInt32();
Logger.Verbose($"Found {scriptCount} scripts");
m_ScriptTypes = new List<LocalSerializedObjectIdentifier>(scriptCount); m_ScriptTypes = new List<LocalSerializedObjectIdentifier>(scriptCount);
for (int i = 0; i < scriptCount; i++) for (int i = 0; i < scriptCount; i++)
{ {
@@ -184,12 +196,14 @@ namespace AssetStudio
reader.AlignStream(); reader.AlignStream();
m_ScriptType.localIdentifierInFile = reader.ReadInt64(); m_ScriptType.localIdentifierInFile = reader.ReadInt64();
} }
Logger.Verbose($"Script Info: {m_ScriptType}");
m_ScriptTypes.Add(m_ScriptType); m_ScriptTypes.Add(m_ScriptType);
} }
} }
int externalsCount = reader.ReadInt32(); int externalsCount = reader.ReadInt32();
m_Externals = new List<FileIdentifier>(externalsCount); m_Externals = new List<FileIdentifier>(externalsCount);
Logger.Verbose($"Found {externalsCount} externals");
for (int i = 0; i < externalsCount; i++) for (int i = 0; i < externalsCount; i++)
{ {
var m_External = new FileIdentifier(); var m_External = new FileIdentifier();
@@ -204,6 +218,7 @@ namespace AssetStudio
} }
m_External.pathName = reader.ReadStringToNull(); m_External.pathName = reader.ReadStringToNull();
m_External.fileName = Path.GetFileName(m_External.pathName); m_External.fileName = Path.GetFileName(m_External.pathName);
Logger.Verbose($"External Info: {m_External}");
m_Externals.Add(m_External); m_Externals.Add(m_External);
} }
@@ -211,6 +226,7 @@ namespace AssetStudio
{ {
int refTypesCount = reader.ReadInt32(); int refTypesCount = reader.ReadInt32();
m_RefTypes = new List<SerializedType>(refTypesCount); m_RefTypes = new List<SerializedType>(refTypesCount);
Logger.Verbose($"Found {refTypesCount} reference types");
for (int i = 0; i < refTypesCount; i++) for (int i = 0; i < refTypesCount; i++)
{ {
m_RefTypes.Add(ReadSerializedType(true)); m_RefTypes.Add(ReadSerializedType(true));
@@ -239,12 +255,14 @@ namespace AssetStudio
private SerializedType ReadSerializedType(bool isRefType) private SerializedType ReadSerializedType(bool isRefType)
{ {
Logger.Verbose($"Attempting to parse serialized" + (isRefType ? " reference" : " ") + "type");
var type = new SerializedType(); var type = new SerializedType();
type.classID = reader.ReadInt32(); type.classID = reader.ReadInt32();
if (game.Type.IsGIGroup() && BitConverter.ToBoolean(header.m_Reserved)) if (game.Type.IsGIGroup() && BitConverter.ToBoolean(header.m_Reserved))
{ {
Logger.Verbose($"Encoded class ID {type.classID}, decoding...");
type.classID = DecodeClassID(type.classID); type.classID = DecodeClassID(type.classID);
} }
@@ -273,6 +291,7 @@ namespace AssetStudio
if (m_EnableTypeTree) if (m_EnableTypeTree)
{ {
Logger.Verbose($"File has type tree enabled !!");
type.m_Type = new TypeTree(); type.m_Type = new TypeTree();
type.m_Type.m_Nodes = new List<TypeTreeNode>(); type.m_Type.m_Nodes = new List<TypeTreeNode>();
if (header.m_Version >= SerializedFileFormatVersion.Unknown_12 || header.m_Version == SerializedFileFormatVersion.Unknown_10) if (header.m_Version >= SerializedFileFormatVersion.Unknown_12 || header.m_Version == SerializedFileFormatVersion.Unknown_10)
@@ -298,11 +317,13 @@ namespace AssetStudio
} }
} }
Logger.Verbose($"Serialized type info: {type}");
return type; return type;
} }
private void ReadTypeTree(TypeTree m_Type, int level = 0) private void ReadTypeTree(TypeTree m_Type, int level = 0)
{ {
Logger.Verbose($"Attempting to parse type tree...");
var typeTreeNode = new TypeTreeNode(); var typeTreeNode = new TypeTreeNode();
m_Type.m_Nodes.Add(typeTreeNode); m_Type.m_Nodes.Add(typeTreeNode);
typeTreeNode.m_Level = level; typeTreeNode.m_Level = level;
@@ -329,12 +350,16 @@ namespace AssetStudio
{ {
ReadTypeTree(m_Type, level + 1); ReadTypeTree(m_Type, level + 1);
} }
Logger.Verbose($"Type Tree Info: {m_Type}");
} }
private void TypeTreeBlobRead(TypeTree m_Type) private void TypeTreeBlobRead(TypeTree m_Type)
{ {
Logger.Verbose($"Attempting to parse blob type tree...");
int numberOfNodes = reader.ReadInt32(); int numberOfNodes = reader.ReadInt32();
int stringBufferSize = reader.ReadInt32(); int stringBufferSize = reader.ReadInt32();
Logger.Verbose($"Found {numberOfNodes} nodes and {stringBufferSize} strings");
for (int i = 0; i < numberOfNodes; i++) for (int i = 0; i < numberOfNodes; i++)
{ {
var typeTreeNode = new TypeTreeNode(); var typeTreeNode = new TypeTreeNode();
@@ -364,6 +389,8 @@ namespace AssetStudio
} }
} }
Logger.Verbose($"Type Tree Info: {m_Type}");
string ReadString(EndianBinaryReader stringBufferReader, uint value) string ReadString(EndianBinaryReader stringBufferReader, uint value)
{ {
var isOffset = (value & 0x80000000) == 0; var isOffset = (value & 0x80000000) == 0;
@@ -383,6 +410,7 @@ namespace AssetStudio
public void AddObject(Object obj) public void AddObject(Object obj)
{ {
Logger.Verbose($"Caching object with {obj.m_PathID} in file {fileName}...");
Objects.Add(obj); Objects.Add(obj);
ObjectsDic.Add(obj.m_PathID, obj); ObjectsDic.Add(obj.m_PathID, obj);
} }

View File

@@ -13,5 +13,16 @@ namespace AssetStudio
public long m_DataOffset; public long m_DataOffset;
public byte m_Endianess; public byte m_Endianess;
public byte[] m_Reserved; public byte[] m_Reserved;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"MetadataSize: 0x{m_MetadataSize:X8} | ");
sb.Append($"FileSize: 0x{m_FileSize:X8} | ");
sb.Append($"Version: {m_Version} | ");
sb.Append($"DataOffset: 0x{m_DataOffset:X8} | ");
sb.Append($"Endianness: {(EndianType)m_Endianess}");
return sb.ToString();
}
} }
} }

View File

@@ -19,5 +19,16 @@ namespace AssetStudio
public string m_AsmName; public string m_AsmName;
public bool Match(string hash) => Convert.ToHexString(m_OldTypeHash) == hash; public bool Match(string hash) => Convert.ToHexString(m_OldTypeHash) == hash;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"classID: {classID} | ");
sb.Append($"IsStrippedType: {m_IsStrippedType} | ");
sb.Append($"ScriptTypeIndex: {m_ScriptTypeIndex} | ");
sb.Append($"KlassName: {m_KlassName} | ");
sb.Append($"NameSpace: {m_NameSpace} | ");
sb.Append($"AsmName: {m_AsmName}");
return sb.ToString();
}
} }
} }

View File

@@ -186,6 +186,7 @@ namespace AssetStudio
{ {
var m_Node = m_Nodes[i]; var m_Node = m_Nodes[i];
var varTypeStr = m_Node.m_Type; var varTypeStr = m_Node.m_Type;
Logger.Verbose($"Reading {m_Node.m_Name} of type {varTypeStr}");
object value; object value;
var align = (m_Node.m_MetaFlag & 0x4000) != 0; var align = (m_Node.m_MetaFlag & 0x4000) != 0;
switch (varTypeStr) switch (varTypeStr)

View File

@@ -28,5 +28,19 @@ namespace AssetStudio
m_Level = level; m_Level = level;
m_MetaFlag = align ? 0x4000 : 0; m_MetaFlag = align ? 0x4000 : 0;
} }
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"Type: {m_Type} | ");
sb.Append($"Name: {m_Name} | ");
sb.Append($"ByteSize: 0x{m_ByteSize:X8} | ");
sb.Append($"Index: {m_Index} | ");
sb.Append($"TypeFlags: {m_TypeFlags} | ");
sb.Append($"Version: {m_Version} | ");
sb.Append($"TypeStrOffset: 0x{m_TypeStrOffset:X8} | ");
sb.Append($"NameStrOffset: 0x{m_NameStrOffset:X8}");
return sb.ToString();
}
} }
} }

View File

@@ -13,6 +13,15 @@ namespace AssetStudio
public int dataOffset; public int dataOffset;
public int dataLength; public int dataLength;
public string path; public string path;
public override string ToString()
{
var sb = new StringBuilder();
sb.Append($"dataOffset: 0x{dataOffset:X8} | ");
sb.Append($"dataOffset: 0x{dataLength:X8} | ");
sb.Append($"path: {path}");
return sb.ToString();
}
} }
public WebFile(EndianBinaryReader reader) public WebFile(EndianBinaryReader reader)
@@ -21,15 +30,19 @@ namespace AssetStudio
var signature = reader.ReadStringToNull(); var signature = reader.ReadStringToNull();
var headLength = reader.ReadInt32(); var headLength = reader.ReadInt32();
var dataList = new List<WebData>(); var dataList = new List<WebData>();
Logger.Verbose($"Header size: 0x{headLength:X8}");
while (reader.BaseStream.Position < headLength) while (reader.BaseStream.Position < headLength)
{ {
var data = new WebData(); var data = new WebData();
data.dataOffset = reader.ReadInt32(); data.dataOffset = reader.ReadInt32();
data.dataLength = reader.ReadInt32(); data.dataLength = reader.ReadInt32();
var pathLength = reader.ReadInt32(); var pathLength = reader.ReadInt32();
Logger.Verbose($"Path length: {pathLength}");
data.path = Encoding.UTF8.GetString(reader.ReadBytes(pathLength)); data.path = Encoding.UTF8.GetString(reader.ReadBytes(pathLength));
Logger.Verbose($"Web data Info: {data}");
dataList.Add(data); dataList.Add(data);
} }
Logger.Verbose("Writing files to streams...");
fileList = new StreamFile[dataList.Count]; fileList = new StreamFile[dataList.Count];
for (int i = 0; i < dataList.Count; i++) for (int i = 0; i < dataList.Count; i++)
{ {

View File

@@ -3,9 +3,9 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFrameworks>net6.0-windows;net7.0-windows</TargetFrameworks> <TargetFrameworks>net6.0-windows;net7.0-windows</TargetFrameworks>
<ApplicationIcon>Resources\as.ico</ApplicationIcon> <ApplicationIcon>Resources\as.ico</ApplicationIcon>
<Version>0.90.00</Version> <Version>0.90.10</Version>
<AssemblyVersion>0.90.00</AssemblyVersion> <AssemblyVersion>0.90.10</AssemblyVersion>
<FileVersion>0.90.00</FileVersion> <FileVersion>0.90.10</FileVersion>
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright> <Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
<BaseOutputPath>..\AssetStudioGUI\bin</BaseOutputPath> <BaseOutputPath>..\AssetStudioGUI\bin</BaseOutputPath>
</PropertyGroup> </PropertyGroup>

View File

@@ -3,9 +3,9 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>0.90.00</Version> <Version>0.90.10</Version>
<AssemblyVersion>0.90.00</AssemblyVersion> <AssemblyVersion>0.90.10</AssemblyVersion>
<FileVersion>0.90.00</FileVersion> <FileVersion>0.90.10</FileVersion>
<Copyright>Copyright © Perfare 2018-2022; Copyright © hozuki 2020</Copyright> <Copyright>Copyright © Perfare 2018-2022; Copyright © hozuki 2020</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>

View File

@@ -27,9 +27,10 @@ namespace AssetStudio.FbxInterop
[DllImport(FbxDll.DllName)] [DllImport(FbxDll.DllName)]
private static extern IntPtr AsFbxGetSceneRootNode(IntPtr context); private static extern IntPtr AsFbxGetSceneRootNode(IntPtr context);
private static IntPtr AsFbxExportSingleFrame(IntPtr context, IntPtr parentNode, string framePath, string frameName, in Vector3 localPosition, in Vector3 localRotation, in Vector3 localScale) private static IntPtr AsFbxExportSingleFrame(IntPtr context, IntPtr parentNode, string framePath, string frameName, in Vector3 localPosition, in Quaternion localRotation, in Vector3 localScale)
{ {
return AsFbxExportSingleFrame(context, parentNode, framePath, frameName, localPosition.X, localPosition.Y, localPosition.Z, localRotation.X, localRotation.Y, localRotation.Z, localScale.X, localScale.Y, localScale.Z); var localRotationEuler = Fbx.QuaternionToEuler(localRotation);
return AsFbxExportSingleFrame(context, parentNode, framePath, frameName, localPosition.X, localPosition.Y, localPosition.Z, localRotationEuler.X, localRotationEuler.Y, localRotationEuler.Z, localScale.X, localScale.Y, localScale.Z);
} }
[DllImport(FbxDll.DllName)] [DllImport(FbxDll.DllName)]

View File

@@ -16,6 +16,7 @@ namespace AssetStudio.FbxInterop
public FbxExporterContext() public FbxExporterContext()
{ {
Fbx.QuaternionToEuler(Quaternion.Zero); // workaround to init dll
_pContext = AsFbxCreateContext(); _pContext = AsFbxCreateContext();
_frameToNode = new Dictionary<ImportedFrame, IntPtr>(); _frameToNode = new Dictionary<ImportedFrame, IntPtr>();
_createdMaterials = new List<KeyValuePair<string, IntPtr>>(); _createdMaterials = new List<KeyValuePair<string, IntPtr>>();
@@ -554,7 +555,7 @@ namespace AssetStudio.FbxInterop
foreach (var rotation in track.Rotations) foreach (var rotation in track.Rotations)
{ {
var value = rotation.value; var value = Fbx.QuaternionToEuler(rotation.value);
AsFbxAnimAddRotationKey(pAnimContext, rotation.time, value.X, value.Y, value.Z); AsFbxAnimAddRotationKey(pAnimContext, rotation.time, value.X, value.Y, value.Z);
} }

View File

@@ -115,6 +115,15 @@
<setting name="selectedCABMapName" serializeAs="String"> <setting name="selectedCABMapName" serializeAs="String">
<value /> <value />
</setting> </setting>
<setting name="disableAnimationClip" serializeAs="String">
<value>False</value>
</setting>
<setting name="enableFileLogging" serializeAs="String">
<value>True</value>
</setting>
<setting name="enableVerbose" serializeAs="String">
<value>False</value>
</setting>
</AssetStudioGUI.Properties.Settings> </AssetStudioGUI.Properties.Settings>
</userSettings> </userSettings>
</configuration> </configuration>

View File

@@ -5,9 +5,9 @@
<TargetFrameworks>net6.0-windows;net7.0-windows</TargetFrameworks> <TargetFrameworks>net6.0-windows;net7.0-windows</TargetFrameworks>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>Resources\as.ico</ApplicationIcon> <ApplicationIcon>Resources\as.ico</ApplicationIcon>
<Version>0.90.00</Version> <Version>0.90.10</Version>
<AssemblyVersion>0.90.00</AssemblyVersion> <AssemblyVersion>0.90.10</AssemblyVersion>
<FileVersion>0.90.00</FileVersion> <FileVersion>0.90.10</FileVersion>
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright> <Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
@@ -69,7 +69,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OpenTK" Version="5.0.0-pre.8" /> <PackageReference Include="OpenTK" Version="4.8.0" />
<Reference Include="OpenTK.WinForms"> <Reference Include="OpenTK.WinForms">
<HintPath>Libraries\OpenTK.WinForms.dll</HintPath> <HintPath>Libraries\OpenTK.WinForms.dll</HintPath>
</Reference> </Reference>

View File

@@ -55,6 +55,8 @@ namespace AssetStudioGUI
toolStripSeparator12 = new System.Windows.Forms.ToolStripSeparator(); toolStripSeparator12 = new System.Windows.Forms.ToolStripSeparator();
toolStripMenuItem14 = new System.Windows.Forms.ToolStripMenuItem(); toolStripMenuItem14 = new System.Windows.Forms.ToolStripMenuItem();
specifyUnityVersion = new System.Windows.Forms.ToolStripTextBox(); specifyUnityVersion = new System.Windows.Forms.ToolStripTextBox();
specifyUnityCNKey = new System.Windows.Forms.ToolStripMenuItem();
toolStripSeparator13 = new System.Windows.Forms.ToolStripSeparator();
toolStripMenuItem18 = new System.Windows.Forms.ToolStripMenuItem(); toolStripMenuItem18 = new System.Windows.Forms.ToolStripMenuItem();
specifyGame = new System.Windows.Forms.ToolStripComboBox(); specifyGame = new System.Windows.Forms.ToolStripComboBox();
toolStripMenuItem19 = new System.Windows.Forms.ToolStripMenuItem(); toolStripMenuItem19 = new System.Windows.Forms.ToolStripMenuItem();
@@ -101,6 +103,8 @@ namespace AssetStudioGUI
exportClassStructuresMenuItem = new System.Windows.Forms.ToolStripMenuItem(); exportClassStructuresMenuItem = new System.Windows.Forms.ToolStripMenuItem();
enableConsole = new System.Windows.Forms.ToolStripMenuItem(); enableConsole = new System.Windows.Forms.ToolStripMenuItem();
clearConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); clearConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
enableFileLogging = new System.Windows.Forms.ToolStripMenuItem();
enableVerbose = new System.Windows.Forms.ToolStripMenuItem();
miscToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); miscToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
MapNameComboBox = new System.Windows.Forms.ToolStripComboBox(); MapNameComboBox = new System.Windows.Forms.ToolStripComboBox();
buildMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); buildMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -167,8 +171,6 @@ namespace AssetStudioGUI
exportAnimatorwithselectedAnimationClipMenuItem = new System.Windows.Forms.ToolStripMenuItem(); exportAnimatorwithselectedAnimationClipMenuItem = new System.Windows.Forms.ToolStripMenuItem();
goToSceneHierarchyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); goToSceneHierarchyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
showOriginalFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); showOriginalFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
specifyUnityCNKey = new System.Windows.Forms.ToolStripMenuItem();
toolStripSeparator13 = new System.Windows.Forms.ToolStripSeparator();
menuStrip1.SuspendLayout(); menuStrip1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit(); ((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit();
splitContainer1.Panel1.SuspendLayout(); splitContainer1.Panel1.SuspendLayout();
@@ -360,6 +362,18 @@ namespace AssetStudioGUI
specifyUnityVersion.Name = "specifyUnityVersion"; specifyUnityVersion.Name = "specifyUnityVersion";
specifyUnityVersion.Size = new System.Drawing.Size(100, 23); specifyUnityVersion.Size = new System.Drawing.Size(100, 23);
// //
// specifyUnityCNKey
//
specifyUnityCNKey.Name = "specifyUnityCNKey";
specifyUnityCNKey.Size = new System.Drawing.Size(225, 22);
specifyUnityCNKey.Text = "Specify UnityCN Key";
specifyUnityCNKey.Click += specifyUnityCNKey_Click;
//
// toolStripSeparator13
//
toolStripSeparator13.Name = "toolStripSeparator13";
toolStripSeparator13.Size = new System.Drawing.Size(222, 6);
//
// toolStripMenuItem18 // toolStripMenuItem18
// //
toolStripMenuItem18.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { specifyGame }); toolStripMenuItem18.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { specifyGame });
@@ -642,7 +656,7 @@ namespace AssetStudioGUI
// //
// debugMenuItem // debugMenuItem
// //
debugMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { toolStripMenuItem15, exportClassStructuresMenuItem, enableConsole, clearConsoleToolStripMenuItem }); debugMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { toolStripMenuItem15, exportClassStructuresMenuItem, enableConsole, clearConsoleToolStripMenuItem, enableFileLogging, enableVerbose });
debugMenuItem.Name = "debugMenuItem"; debugMenuItem.Name = "debugMenuItem";
debugMenuItem.Size = new System.Drawing.Size(54, 20); debugMenuItem.Size = new System.Drawing.Size(54, 20);
debugMenuItem.Text = "Debug"; debugMenuItem.Text = "Debug";
@@ -681,6 +695,24 @@ namespace AssetStudioGUI
clearConsoleToolStripMenuItem.Text = "Clear Console"; clearConsoleToolStripMenuItem.Text = "Clear Console";
clearConsoleToolStripMenuItem.Click += clearConsoleToolStripMenuItem_Click; clearConsoleToolStripMenuItem.Click += clearConsoleToolStripMenuItem_Click;
// //
// enableFileLogging
//
enableFileLogging.Checked = true;
enableFileLogging.CheckOnClick = true;
enableFileLogging.CheckState = System.Windows.Forms.CheckState.Checked;
enableFileLogging.Name = "enableFileLogging";
enableFileLogging.Size = new System.Drawing.Size(191, 22);
enableFileLogging.Text = "Enable File Logging";
enableFileLogging.CheckedChanged += enableFileLogging_CheckedChanged;
//
// enableVerbose
//
enableVerbose.CheckOnClick = true;
enableVerbose.Name = "enableVerbose";
enableVerbose.Size = new System.Drawing.Size(191, 22);
enableVerbose.Text = "Enable Verbose";
enableVerbose.CheckedChanged += enableVerbose_Click;
//
// miscToolStripMenuItem // miscToolStripMenuItem
// //
miscToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { MapNameComboBox, buildMapToolStripMenuItem, buildBothToolStripMenuItem, clearMapToolStripMenuItem, toolStripSeparator7, assetMapNameTextBox, buildAssetMapToolStripMenuItem, assetMapTypeComboBox, toolStripSeparator8, loadAIToolStripMenuItem, assetBrowserToolStripMenuItem }); miscToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { MapNameComboBox, buildMapToolStripMenuItem, buildBothToolStripMenuItem, clearMapToolStripMenuItem, toolStripSeparator7, assetMapNameTextBox, buildAssetMapToolStripMenuItem, assetMapTypeComboBox, toolStripSeparator8, loadAIToolStripMenuItem, assetBrowserToolStripMenuItem });
@@ -843,16 +875,14 @@ namespace AssetStudioGUI
// treeSearch // treeSearch
// //
treeSearch.Dock = System.Windows.Forms.DockStyle.Top; treeSearch.Dock = System.Windows.Forms.DockStyle.Top;
treeSearch.ForeColor = System.Drawing.SystemColors.GrayText; treeSearch.ForeColor = System.Drawing.SystemColors.WindowText;
treeSearch.Location = new System.Drawing.Point(0, 0); treeSearch.Location = new System.Drawing.Point(0, 0);
treeSearch.Name = "treeSearch"; treeSearch.Name = "treeSearch";
treeSearch.Size = new System.Drawing.Size(472, 23); treeSearch.Size = new System.Drawing.Size(472, 23);
treeSearch.TabIndex = 0; treeSearch.TabIndex = 0;
treeSearch.Text = " Search "; treeSearch.PlaceholderText = "Search (with Ctrl to check result, with Shift for all)";
treeSearch.TextChanged += treeSearch_TextChanged; treeSearch.TextChanged += treeSearch_TextChanged;
treeSearch.Enter += treeSearch_Enter;
treeSearch.KeyDown += treeSearch_KeyDown; treeSearch.KeyDown += treeSearch_KeyDown;
treeSearch.Leave += treeSearch_Leave;
// //
// tabPage2 // tabPage2
// //
@@ -910,15 +940,13 @@ namespace AssetStudioGUI
// listSearch // listSearch
// //
listSearch.Dock = System.Windows.Forms.DockStyle.Top; listSearch.Dock = System.Windows.Forms.DockStyle.Top;
listSearch.ForeColor = System.Drawing.SystemColors.GrayText; listSearch.ForeColor = System.Drawing.SystemColors.WindowText;
listSearch.Location = new System.Drawing.Point(0, 0); listSearch.Location = new System.Drawing.Point(0, 0);
listSearch.Name = "listSearch"; listSearch.Name = "listSearch";
listSearch.Size = new System.Drawing.Size(472, 23); listSearch.Size = new System.Drawing.Size(472, 23);
listSearch.TabIndex = 0; listSearch.TabIndex = 0;
listSearch.Text = " Filter "; listSearch.PlaceholderText = "Search";
listSearch.TextChanged += ListSearchTextChanged; listSearch.KeyPress += listSearch_KeyPress;
listSearch.Enter += listSearch_Enter;
listSearch.Leave += listSearch_Leave;
// //
// tabPage3 // tabPage3
// //
@@ -1166,7 +1194,7 @@ namespace AssetStudioGUI
// glControl // glControl
// //
glControl.API = OpenTK.Windowing.Common.ContextAPI.OpenGL; glControl.API = OpenTK.Windowing.Common.ContextAPI.OpenGL;
glControl.APIVersion = new Version(4, 6, 0, 0); glControl.APIVersion = new Version(4, 5, 0, 0);
glControl.BackColor = System.Drawing.SystemColors.ControlDarkDark; glControl.BackColor = System.Drawing.SystemColors.ControlDarkDark;
glControl.Dock = System.Windows.Forms.DockStyle.Fill; glControl.Dock = System.Windows.Forms.DockStyle.Fill;
glControl.Flags = OpenTK.Windowing.Common.ContextFlags.Default; glControl.Flags = OpenTK.Windowing.Common.ContextFlags.Default;
@@ -1308,18 +1336,6 @@ namespace AssetStudioGUI
showOriginalFileToolStripMenuItem.Visible = false; showOriginalFileToolStripMenuItem.Visible = false;
showOriginalFileToolStripMenuItem.Click += showOriginalFileToolStripMenuItem_Click; showOriginalFileToolStripMenuItem.Click += showOriginalFileToolStripMenuItem_Click;
// //
// specifyUnityCNKey
//
specifyUnityCNKey.Name = "specifyUnityCNKey";
specifyUnityCNKey.Size = new System.Drawing.Size(225, 22);
specifyUnityCNKey.Text = "Specify UnityCN Key";
specifyUnityCNKey.Click += specifyUnityCNKey_Click;
//
// toolStripSeparator13
//
toolStripSeparator13.Name = "toolStripSeparator13";
toolStripSeparator13.Size = new System.Drawing.Size(222, 6);
//
// AssetStudioGUIForm // AssetStudioGUIForm
// //
AllowDrop = true; AllowDrop = true;
@@ -1506,6 +1522,8 @@ namespace AssetStudioGUI
private System.Windows.Forms.ToolStripSeparator toolStripSeparator12; private System.Windows.Forms.ToolStripSeparator toolStripSeparator12;
private System.Windows.Forms.ToolStripMenuItem specifyUnityCNKey; private System.Windows.Forms.ToolStripMenuItem specifyUnityCNKey;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator13; private System.Windows.Forms.ToolStripSeparator toolStripSeparator13;
private System.Windows.Forms.ToolStripMenuItem enableFileLogging;
private System.Windows.Forms.ToolStripMenuItem enableVerbose;
} }
} }

View File

@@ -23,6 +23,7 @@ using Matrix4 = OpenTK.Mathematics.Matrix4;
using OpenTK.Graphics; using OpenTK.Graphics;
using OpenTK.Mathematics; using OpenTK.Mathematics;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using System.Text.RegularExpressions;
namespace AssetStudioGUI namespace AssetStudioGUI
{ {
@@ -50,14 +51,14 @@ namespace AssetStudioGUI
private bool glControlLoaded; private bool glControlLoaded;
private int mdx, mdy; private int mdx, mdy;
private bool lmdown, rmdown; private bool lmdown, rmdown;
private ProgramHandle pgmID, pgmColorID, pgmBlackID; private int pgmID, pgmColorID, pgmBlackID;
private int attributeVertexPosition; private int attributeVertexPosition;
private int attributeNormalDirection; private int attributeNormalDirection;
private int attributeVertexColor; private int attributeVertexColor;
private int uniformModelMatrix; private int uniformModelMatrix;
private int uniformViewMatrix; private int uniformViewMatrix;
private int uniformProjMatrix; private int uniformProjMatrix;
private VertexArrayHandle vao; private int vao;
private Vector3[] vertexData; private Vector3[] vertexData;
private Vector3[] normalData; private Vector3[] normalData;
private Vector3[] normal2Data; private Vector3[] normal2Data;
@@ -75,10 +76,6 @@ namespace AssetStudioGUI
private int sortColumn = -1; private int sortColumn = -1;
private bool reverseSort; private bool reverseSort;
//asset list filter
private System.Timers.Timer delayTimer;
private bool enableFiltering;
//tree search //tree search
private int nextGObject; private int nextGObject;
private List<TreeNode> treeSrcResults = new List<TreeNode>(); private List<TreeNode> treeSrcResults = new List<TreeNode>();
@@ -93,8 +90,6 @@ namespace AssetStudioGUI
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
InitializeComponent(); InitializeComponent();
Text = $"Studio v{Application.ProductVersion}"; Text = $"Studio v{Application.ProductVersion}";
delayTimer = new System.Timers.Timer(800);
delayTimer.Elapsed += new ElapsedEventHandler(delayTimer_Elapsed);
InitializeExportOptions(); InitializeExportOptions();
InitializeProgressBar(); InitializeProgressBar();
InitializeLogger(); InitializeLogger();
@@ -105,6 +100,8 @@ namespace AssetStudioGUI
private void InitializeExportOptions() private void InitializeExportOptions()
{ {
enableConsole.Checked = Properties.Settings.Default.enableConsole; enableConsole.Checked = Properties.Settings.Default.enableConsole;
enableFileLogging.Checked = Properties.Settings.Default.enableFileLogging;
enableVerbose.Checked = Properties.Settings.Default.enableVerbose;
displayAll.Checked = Properties.Settings.Default.displayAll; displayAll.Checked = Properties.Settings.Default.displayAll;
displayInfo.Checked = Properties.Settings.Default.displayInfo; displayInfo.Checked = Properties.Settings.Default.displayInfo;
enablePreview.Checked = Properties.Settings.Default.enablePreview; enablePreview.Checked = Properties.Settings.Default.enablePreview;
@@ -119,10 +116,13 @@ namespace AssetStudioGUI
AssetsHelper.Minimal = Properties.Settings.Default.minimalAssetMap; AssetsHelper.Minimal = Properties.Settings.Default.minimalAssetMap;
Renderer.Parsable = !Properties.Settings.Default.disableRenderer; Renderer.Parsable = !Properties.Settings.Default.disableRenderer;
Shader.Parsable = !Properties.Settings.Default.disableShader; Shader.Parsable = !Properties.Settings.Default.disableShader;
AnimationClip.Parsable = !Properties.Settings.Default.disableAnimationClip;
} }
private void InitializeLogger() private void InitializeLogger()
{ {
Logger.LogVerbose = enableVerbose.Checked;
Logger.FileLogging = enableFileLogging.Checked;
logger = new GUILogger(StatusStripUpdate); logger = new GUILogger(StatusStripUpdate);
ConsoleHelper.AllocConsole(); ConsoleHelper.AllocConsole();
ConsoleHelper.SetConsoleTitle("Debug Console"); ConsoleHelper.SetConsoleTitle("Debug Console");
@@ -559,24 +559,6 @@ namespace AssetStudioGUI
} }
} }
private void treeSearch_Enter(object sender, EventArgs e)
{
if (treeSearch.Text == " Search ")
{
treeSearch.Text = "";
treeSearch.ForeColor = SystemColors.WindowText;
}
}
private void treeSearch_Leave(object sender, EventArgs e)
{
if (treeSearch.Text == "")
{
treeSearch.Text = " Search ";
treeSearch.ForeColor = SystemColors.GrayText;
}
}
private void treeSearch_TextChanged(object sender, EventArgs e) private void treeSearch_TextChanged(object sender, EventArgs e)
{ {
treeSrcResults.Clear(); treeSrcResults.Clear();
@@ -585,38 +567,52 @@ namespace AssetStudioGUI
private void treeSearch_KeyDown(object sender, KeyEventArgs e) private void treeSearch_KeyDown(object sender, KeyEventArgs e)
{ {
if (e.KeyCode == Keys.Enter) if (e.KeyCode == Keys.Enter && !string.IsNullOrEmpty(treeSearch.Text))
{ {
if (treeSrcResults.Count == 0) if (treeSrcResults.Count == 0)
{ {
var regex = new Regex(treeSearch.Text, RegexOptions.IgnoreCase);
foreach (TreeNode node in sceneTreeView.Nodes) foreach (TreeNode node in sceneTreeView.Nodes)
{ {
TreeNodeSearch(node); TreeNodeSearch(regex, node);
} }
} }
if (treeSrcResults.Count > 0) if (treeSrcResults.Count > 0)
{
if (e.Shift)
{
foreach(var node in treeSrcResults)
{
node.EnsureVisible();
node.Checked = e.Control;
}
sceneTreeView.SelectedNode = treeSrcResults[0];
}
else
{ {
if (nextGObject >= treeSrcResults.Count) if (nextGObject >= treeSrcResults.Count)
{ {
nextGObject = 0; nextGObject = 0;
} }
treeSrcResults[nextGObject].EnsureVisible(); treeSrcResults[nextGObject].EnsureVisible();
treeSrcResults[nextGObject].Checked = e.Control;
sceneTreeView.SelectedNode = treeSrcResults[nextGObject]; sceneTreeView.SelectedNode = treeSrcResults[nextGObject];
nextGObject++; nextGObject++;
} }
} }
} }
}
private void TreeNodeSearch(TreeNode treeNode) private void TreeNodeSearch(Regex regex, TreeNode treeNode)
{ {
if (treeNode.Text.IndexOf(treeSearch.Text, StringComparison.OrdinalIgnoreCase) >= 0) if (regex.IsMatch(treeNode.Text))
{ {
treeSrcResults.Add(treeNode); treeSrcResults.Add(treeNode);
} }
foreach (TreeNode node in treeNode.Nodes) foreach (TreeNode node in treeNode.Nodes)
{ {
TreeNodeSearch(node); TreeNodeSearch(regex, node);
} }
} }
@@ -628,47 +624,13 @@ namespace AssetStudioGUI
} }
} }
private void listSearch_Enter(object sender, EventArgs e) private void listSearch_KeyPress(object sender, KeyPressEventArgs e)
{ {
if (listSearch.Text == " Filter ") if (e.KeyChar == (char)Keys.Enter)
{ {
listSearch.Text = "";
listSearch.ForeColor = SystemColors.WindowText;
enableFiltering = true;
}
}
private void listSearch_Leave(object sender, EventArgs e)
{
if (listSearch.Text == "")
{
enableFiltering = false;
listSearch.Text = " Filter ";
listSearch.ForeColor = SystemColors.GrayText;
}
}
private void ListSearchTextChanged(object sender, EventArgs e)
{
if (enableFiltering)
{
if (delayTimer.Enabled)
{
delayTimer.Stop();
delayTimer.Start();
}
else
{
delayTimer.Start();
}
}
}
private void delayTimer_Elapsed(object sender, ElapsedEventArgs e)
{
delayTimer.Stop();
Invoke(new Action(FilterAssetList)); Invoke(new Action(FilterAssetList));
} }
}
private void assetListView_ColumnClick(object sender, ColumnClickEventArgs e) private void assetListView_ColumnClick(object sender, ColumnClickEventArgs e)
{ {
@@ -1429,8 +1391,7 @@ namespace AssetStudioGUI
lastSelectedItem = null; lastSelectedItem = null;
sortColumn = -1; sortColumn = -1;
reverseSort = false; reverseSort = false;
enableFiltering = false; listSearch.Text = string.Empty;
listSearch.Text = " Filter ";
var count = filterTypeToolStripMenuItem.DropDownItems.Count; var count = filterTypeToolStripMenuItem.DropDownItems.Count;
for (var i = 1; i < count; i++) for (var i = 1; i < count; i++)
@@ -1788,12 +1749,13 @@ namespace AssetStudioGUI
} }
} }
} }
if (listSearch.Text != " Filter ") if (!string.IsNullOrEmpty(listSearch.Text))
{ {
var regex = new Regex(listSearch.Text, RegexOptions.IgnoreCase);
visibleAssets = visibleAssets.FindAll( visibleAssets = visibleAssets.FindAll(
x => x.Text.IndexOf(listSearch.Text, StringComparison.OrdinalIgnoreCase) >= 0 || x => regex.IsMatch(x.Text) ||
x.SubItems[1].Text.IndexOf(listSearch.Text, StringComparison.OrdinalIgnoreCase) >= 0 || regex.IsMatch(x.SubItems[1].Text) ||
x.SubItems[3].Text.IndexOf(listSearch.Text, StringComparison.OrdinalIgnoreCase) >= 0); regex.IsMatch(x.SubItems[3].Text));
} }
assetListView.VirtualListSize = visibleAssets.Count; assetListView.VirtualListSize = visibleAssets.Count;
assetListView.EndUpdate(); assetListView.EndUpdate();
@@ -2238,6 +2200,22 @@ namespace AssetStudioGUI
} }
} }
private void enableFileLogging_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.enableFileLogging = enableFileLogging.Checked;
Properties.Settings.Default.Save();
Logger.FileLogging = enableFileLogging.Checked;
}
private void enableVerbose_Click(object sender, EventArgs e)
{
Properties.Settings.Default.enableVerbose = enableVerbose.Checked;
Properties.Settings.Default.Save();
Logger.LogVerbose = enableVerbose.Checked;
}
private void abortStripMenuItem_Click(object sender, EventArgs e) private void abortStripMenuItem_Click(object sender, EventArgs e)
{ {
Logger.Info("Aborting...."); Logger.Info("Aborting....");
@@ -2607,10 +2585,10 @@ namespace AssetStudioGUI
private void InitOpenTK() private void InitOpenTK()
{ {
ChangeGLSize(glControl.Size); ChangeGLSize(glControl.Size);
GL.ClearColor(Color4.Cadetblue); GL.ClearColor(System.Drawing.Color.CadetBlue);
pgmID = GL.CreateProgram(); pgmID = GL.CreateProgram();
LoadShader("vs", ShaderType.VertexShader, pgmID, out ShaderHandle vsID); LoadShader("vs", ShaderType.VertexShader, pgmID, out int vsID);
LoadShader("fs", ShaderType.FragmentShader, pgmID, out ShaderHandle fsID); LoadShader("fs", ShaderType.FragmentShader, pgmID, out int fsID);
GL.LinkProgram(pgmID); GL.LinkProgram(pgmID);
pgmColorID = GL.CreateProgram(); pgmColorID = GL.CreateProgram();
@@ -2631,7 +2609,7 @@ namespace AssetStudioGUI
uniformProjMatrix = GL.GetUniformLocation(pgmID, "projMatrix"); uniformProjMatrix = GL.GetUniformLocation(pgmID, "projMatrix");
} }
private static void LoadShader(string filename, ShaderType type, ProgramHandle program, out ShaderHandle address) private static void LoadShader(string filename, ShaderType type, int program, out int address)
{ {
address = GL.CreateShader(type); address = GL.CreateShader(type);
var str = (string)Properties.Resources.ResourceManager.GetObject(filename); var str = (string)Properties.Resources.ResourceManager.GetObject(filename);
@@ -2641,47 +2619,50 @@ namespace AssetStudioGUI
GL.DeleteShader(address); GL.DeleteShader(address);
} }
private static void CreateVBO(out BufferHandle vboAddress, Vector3[] data, int address) private static void CreateVBO(out int vboAddress, Vector3[] data, int address)
{ {
GL.CreateBuffer(out vboAddress); GL.GenBuffers(1, out vboAddress);
GL.BindBuffer(BufferTargetARB.ArrayBuffer, vboAddress); GL.BindBuffer(BufferTarget.ArrayBuffer, vboAddress);
GL.BufferData(BufferTargetARB.ArrayBuffer, GL.BufferData(BufferTarget.ArrayBuffer,
(IntPtr)(data.Length * Vector3.SizeInBytes),
data, data,
BufferUsageARB.StaticDraw); BufferUsageHint.StaticDraw);
GL.VertexAttribPointer((uint)address, 3, VertexAttribPointerType.Float, false, 0, 0); GL.VertexAttribPointer(address, 3, VertexAttribPointerType.Float, false, 0, 0);
GL.EnableVertexAttribArray((uint)address); GL.EnableVertexAttribArray(address);
} }
private static void CreateVBO(out BufferHandle vboAddress, Vector4[] data, int address) private static void CreateVBO(out int vboAddress, Vector4[] data, int address)
{ {
GL.CreateBuffer(out vboAddress); GL.GenBuffers(1, out vboAddress);
GL.BindBuffer(BufferTargetARB.ArrayBuffer, vboAddress); GL.BindBuffer(BufferTarget.ArrayBuffer, vboAddress);
GL.BufferData(BufferTargetARB.ArrayBuffer, GL.BufferData(BufferTarget.ArrayBuffer,
(IntPtr)(data.Length * Vector4.SizeInBytes),
data, data,
BufferUsageARB.StaticDraw); BufferUsageHint.StaticDraw);
GL.VertexAttribPointer((uint)address, 4, VertexAttribPointerType.Float, false, 0, 0); GL.VertexAttribPointer(address, 4, VertexAttribPointerType.Float, false, 0, 0);
GL.EnableVertexAttribArray((uint)address); GL.EnableVertexAttribArray(address);
} }
private static void CreateVBO(out BufferHandle vboAddress, Matrix4 data, int address) private static void CreateVBO(out int vboAddress, Matrix4 data, int address)
{ {
GL.CreateBuffer(out vboAddress); GL.GenBuffers(1, out vboAddress);
GL.UniformMatrix4f(address, false, in data); GL.UniformMatrix4(address, false, ref data);
} }
private static void CreateEBO(out BufferHandle address, int[] data) private static void CreateEBO(out int address, int[] data)
{ {
GL.CreateBuffer(out address); GL.GenBuffers(1, out address);
GL.BindBuffer(BufferTargetARB.ElementArrayBuffer, address); GL.BindBuffer(BufferTarget.ElementArrayBuffer, address);
GL.BufferData(BufferTargetARB.ElementArrayBuffer, GL.BufferData(BufferTarget.ElementArrayBuffer,
(IntPtr)(data.Length * sizeof(int)),
data, data,
BufferUsageARB.StaticDraw); BufferUsageHint.StaticDraw);
} }
private void CreateVAO() private void CreateVAO()
{ {
GL.DeleteVertexArray(vao); GL.DeleteVertexArray(vao);
GL.CreateVertexArray(out vao); GL.GenVertexArrays(1, out vao);
GL.BindVertexArray(vao); GL.BindVertexArray(vao);
CreateVBO(out var vboPositions, vertexData, attributeVertexPosition); CreateVBO(out var vboPositions, vertexData, attributeVertexPosition);
if (normalMode == 0) if (normalMode == 0)
@@ -2698,8 +2679,8 @@ namespace AssetStudioGUI
CreateVBO(out var vboViewMatrix, viewMatrixData, uniformViewMatrix); CreateVBO(out var vboViewMatrix, viewMatrixData, uniformViewMatrix);
CreateVBO(out var vboProjMatrix, projMatrixData, uniformProjMatrix); CreateVBO(out var vboProjMatrix, projMatrixData, uniformProjMatrix);
CreateEBO(out var eboElements, indiceData); CreateEBO(out var eboElements, indiceData);
GL.BindBuffer(BufferTargetARB.ArrayBuffer, BufferHandle.Zero); GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindVertexArray(VertexArrayHandle.Zero); GL.BindVertexArray(0);
} }
private void ChangeGLSize(Size size) private void ChangeGLSize(Size size)
@@ -2734,10 +2715,10 @@ namespace AssetStudioGUI
if (wireFrameMode == 0 || wireFrameMode == 2) if (wireFrameMode == 0 || wireFrameMode == 2)
{ {
GL.UseProgram(shadeMode == 0 ? pgmID : pgmColorID); GL.UseProgram(shadeMode == 0 ? pgmID : pgmColorID);
GL.UniformMatrix4f(uniformModelMatrix, false, in modelMatrixData); GL.UniformMatrix4(uniformModelMatrix, false, ref modelMatrixData);
GL.UniformMatrix4f(uniformViewMatrix, false, in viewMatrixData); GL.UniformMatrix4(uniformViewMatrix, false, ref viewMatrixData);
GL.UniformMatrix4f(uniformProjMatrix, false, in projMatrixData); GL.UniformMatrix4(uniformProjMatrix, false, ref projMatrixData);
GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Fill); GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
GL.DrawElements(PrimitiveType.Triangles, indiceData.Length, DrawElementsType.UnsignedInt, 0); GL.DrawElements(PrimitiveType.Triangles, indiceData.Length, DrawElementsType.UnsignedInt, 0);
} }
//Wireframe //Wireframe
@@ -2746,14 +2727,14 @@ namespace AssetStudioGUI
GL.Enable(EnableCap.PolygonOffsetLine); GL.Enable(EnableCap.PolygonOffsetLine);
GL.PolygonOffset(-1, -1); GL.PolygonOffset(-1, -1);
GL.UseProgram(pgmBlackID); GL.UseProgram(pgmBlackID);
GL.UniformMatrix4f(uniformModelMatrix, false, in modelMatrixData); GL.UniformMatrix4(uniformModelMatrix, false, ref modelMatrixData);
GL.UniformMatrix4f(uniformViewMatrix, false, in viewMatrixData); GL.UniformMatrix4(uniformViewMatrix, false, ref viewMatrixData);
GL.UniformMatrix4f(uniformProjMatrix, false, in projMatrixData); GL.UniformMatrix4(uniformProjMatrix, false, ref projMatrixData);
GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Line); GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
GL.DrawElements(PrimitiveType.Triangles, indiceData.Length, DrawElementsType.UnsignedInt, 0); GL.DrawElements(PrimitiveType.Triangles, indiceData.Length, DrawElementsType.UnsignedInt, 0);
GL.Disable(EnableCap.PolygonOffsetLine); GL.Disable(EnableCap.PolygonOffsetLine);
} }
GL.BindVertexArray(VertexArrayHandle.Zero); GL.BindVertexArray(0);
GL.Flush(); GL.Flush();
glControl.SwapBuffers(); glControl.SwapBuffers();
} }

View File

@@ -120,6 +120,9 @@
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>312, 17</value> <value>312, 17</value>
</metadata> </metadata>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>432, 17</value>
</metadata>
<data name="fontPreviewBox.Text" xml:space="preserve"> <data name="fontPreviewBox.Text" xml:space="preserve">
<value>abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWYZ <value>abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWYZ
1234567890.:,;'\"(!?)+-*/= 1234567890.:,;'\"(!?)+-*/=
@@ -138,9 +141,6 @@ The quick brown fox jumps over the lazy dog. 1234567890
The quick brown fox jumps over the lazy dog. 1234567890</value> The quick brown fox jumps over the lazy dog. 1234567890</value>
</data> </data>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>432, 17</value>
</metadata>
<metadata name="timer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="timer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>553, 17</value> <value>553, 17</value>
</metadata> </metadata>

View File

@@ -35,6 +35,8 @@ namespace AssetStudioGUI
OKbutton = new System.Windows.Forms.Button(); OKbutton = new System.Windows.Forms.Button();
Cancel = new System.Windows.Forms.Button(); Cancel = new System.Windows.Forms.Button();
groupBox1 = new System.Windows.Forms.GroupBox(); groupBox1 = new System.Windows.Forms.GroupBox();
assetGroupOptions = new System.Windows.Forms.ComboBox();
label7 = new System.Windows.Forms.Label();
openAfterExport = new System.Windows.Forms.CheckBox(); openAfterExport = new System.Windows.Forms.CheckBox();
restoreExtensionName = new System.Windows.Forms.CheckBox(); restoreExtensionName = new System.Windows.Forms.CheckBox();
assetGroupOptions = new System.Windows.Forms.ComboBox(); assetGroupOptions = new System.Windows.Forms.ComboBox();
@@ -71,11 +73,10 @@ namespace AssetStudioGUI
key = new System.Windows.Forms.NumericUpDown(); key = new System.Windows.Forms.NumericUpDown();
keyToolTip = new System.Windows.Forms.ToolTip(components); keyToolTip = new System.Windows.Forms.ToolTip(components);
groupBox4 = new System.Windows.Forms.GroupBox(); groupBox4 = new System.Windows.Forms.GroupBox();
disableAnimationClip = new System.Windows.Forms.CheckBox();
minimalAssetMap = new System.Windows.Forms.CheckBox(); minimalAssetMap = new System.Windows.Forms.CheckBox();
disableShader = new System.Windows.Forms.CheckBox(); disableShader = new System.Windows.Forms.CheckBox();
disableRenderer = new System.Windows.Forms.CheckBox(); disableRenderer = new System.Windows.Forms.CheckBox();
resolveToolTip = new System.Windows.Forms.ToolTip(components);
skipToolTip = new System.Windows.Forms.ToolTip(components);
groupBox1.SuspendLayout(); groupBox1.SuspendLayout();
panel1.SuspendLayout(); panel1.SuspendLayout();
groupBox2.SuspendLayout(); groupBox2.SuspendLayout();
@@ -112,6 +113,8 @@ namespace AssetStudioGUI
// groupBox1 // groupBox1
// //
groupBox1.AutoSize = true; groupBox1.AutoSize = true;
groupBox1.Controls.Add(assetGroupOptions);
groupBox1.Controls.Add(label7);
groupBox1.Controls.Add(openAfterExport); groupBox1.Controls.Add(openAfterExport);
groupBox1.Controls.Add(restoreExtensionName); groupBox1.Controls.Add(restoreExtensionName);
groupBox1.Controls.Add(assetGroupOptions); groupBox1.Controls.Add(assetGroupOptions);
@@ -123,17 +126,38 @@ namespace AssetStudioGUI
groupBox1.Margin = new System.Windows.Forms.Padding(4); groupBox1.Margin = new System.Windows.Forms.Padding(4);
groupBox1.Name = "groupBox1"; groupBox1.Name = "groupBox1";
groupBox1.Padding = new System.Windows.Forms.Padding(4); groupBox1.Padding = new System.Windows.Forms.Padding(4);
groupBox1.Size = new System.Drawing.Size(271, 243); groupBox1.Size = new System.Drawing.Size(271, 273);
groupBox1.TabIndex = 9; groupBox1.TabIndex = 9;
groupBox1.TabStop = false; groupBox1.TabStop = false;
groupBox1.Text = "Export"; groupBox1.Text = "Export";
// //
// assetGroupOptions
//
assetGroupOptions.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
assetGroupOptions.FormattingEnabled = true;
assetGroupOptions.Items.AddRange(new object[] { "type name", "container path", "source file name", "do not group" });
assetGroupOptions.Location = new System.Drawing.Point(7, 83);
assetGroupOptions.Margin = new System.Windows.Forms.Padding(4);
assetGroupOptions.Name = "assetGroupOptions";
assetGroupOptions.Size = new System.Drawing.Size(173, 23);
assetGroupOptions.TabIndex = 12;
//
// label7
//
label7.AutoSize = true;
label7.Location = new System.Drawing.Point(7, 64);
label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
label7.Name = "label7";
label7.Size = new System.Drawing.Size(140, 15);
label7.TabIndex = 11;
label7.Text = "Group exported assets by";
//
// openAfterExport // openAfterExport
// //
openAfterExport.AutoSize = true; openAfterExport.AutoSize = true;
openAfterExport.Checked = true; openAfterExport.Checked = true;
openAfterExport.CheckState = System.Windows.Forms.CheckState.Checked; openAfterExport.CheckState = System.Windows.Forms.CheckState.Checked;
openAfterExport.Location = new System.Drawing.Point(7, 200); openAfterExport.Location = new System.Drawing.Point(8, 230);
openAfterExport.Margin = new System.Windows.Forms.Padding(4); openAfterExport.Margin = new System.Windows.Forms.Padding(4);
openAfterExport.Name = "openAfterExport"; openAfterExport.Name = "openAfterExport";
openAfterExport.Size = new System.Drawing.Size(153, 19); openAfterExport.Size = new System.Drawing.Size(153, 19);
@@ -146,7 +170,7 @@ namespace AssetStudioGUI
restoreExtensionName.AutoSize = true; restoreExtensionName.AutoSize = true;
restoreExtensionName.Checked = true; restoreExtensionName.Checked = true;
restoreExtensionName.CheckState = System.Windows.Forms.CheckState.Checked; restoreExtensionName.CheckState = System.Windows.Forms.CheckState.Checked;
restoreExtensionName.Location = new System.Drawing.Point(7, 72); restoreExtensionName.Location = new System.Drawing.Point(7, 109);
restoreExtensionName.Margin = new System.Windows.Forms.Padding(4); restoreExtensionName.Margin = new System.Windows.Forms.Padding(4);
restoreExtensionName.Name = "restoreExtensionName"; restoreExtensionName.Name = "restoreExtensionName";
restoreExtensionName.Size = new System.Drawing.Size(204, 19); restoreExtensionName.Size = new System.Drawing.Size(204, 19);
@@ -171,7 +195,7 @@ namespace AssetStudioGUI
label6.Location = new System.Drawing.Point(7, 21); label6.Location = new System.Drawing.Point(7, 21);
label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
label6.Name = "label6"; label6.Name = "label6";
label6.Size = new System.Drawing.Size(140, 15); label6.Size = new System.Drawing.Size(80, 15);
label6.TabIndex = 7; label6.TabIndex = 7;
label6.Text = "Group exported assets by"; label6.Text = "Group exported assets by";
// //
@@ -180,7 +204,7 @@ namespace AssetStudioGUI
convertAudio.AutoSize = true; convertAudio.AutoSize = true;
convertAudio.Checked = true; convertAudio.Checked = true;
convertAudio.CheckState = System.Windows.Forms.CheckState.Checked; convertAudio.CheckState = System.Windows.Forms.CheckState.Checked;
convertAudio.Location = new System.Drawing.Point(7, 172); convertAudio.Location = new System.Drawing.Point(7, 204);
convertAudio.Margin = new System.Windows.Forms.Padding(4); convertAudio.Margin = new System.Windows.Forms.Padding(4);
convertAudio.Name = "convertAudio"; convertAudio.Name = "convertAudio";
convertAudio.Size = new System.Drawing.Size(200, 19); convertAudio.Size = new System.Drawing.Size(200, 19);
@@ -194,7 +218,7 @@ namespace AssetStudioGUI
panel1.Controls.Add(tojpg); panel1.Controls.Add(tojpg);
panel1.Controls.Add(topng); panel1.Controls.Add(topng);
panel1.Controls.Add(tobmp); panel1.Controls.Add(tobmp);
panel1.Location = new System.Drawing.Point(23, 128); panel1.Location = new System.Drawing.Point(23, 164);
panel1.Margin = new System.Windows.Forms.Padding(4); panel1.Margin = new System.Windows.Forms.Padding(4);
panel1.Name = "panel1"; panel1.Name = "panel1";
panel1.Size = new System.Drawing.Size(236, 38); panel1.Size = new System.Drawing.Size(236, 38);
@@ -251,7 +275,7 @@ namespace AssetStudioGUI
converttexture.AutoSize = true; converttexture.AutoSize = true;
converttexture.Checked = true; converttexture.Checked = true;
converttexture.CheckState = System.Windows.Forms.CheckState.Checked; converttexture.CheckState = System.Windows.Forms.CheckState.Checked;
converttexture.Location = new System.Drawing.Point(7, 100); converttexture.Location = new System.Drawing.Point(7, 136);
converttexture.Margin = new System.Windows.Forms.Padding(4); converttexture.Margin = new System.Windows.Forms.Padding(4);
converttexture.Name = "converttexture"; converttexture.Name = "converttexture";
converttexture.Size = new System.Drawing.Size(123, 19); converttexture.Size = new System.Drawing.Size(123, 19);
@@ -536,20 +560,32 @@ namespace AssetStudioGUI
// groupBox4 // groupBox4
// //
groupBox4.AutoSize = true; groupBox4.AutoSize = true;
groupBox4.Controls.Add(disableAnimationClip);
groupBox4.Controls.Add(minimalAssetMap); groupBox4.Controls.Add(minimalAssetMap);
groupBox4.Controls.Add(disableShader); groupBox4.Controls.Add(disableShader);
groupBox4.Controls.Add(disableRenderer); groupBox4.Controls.Add(disableRenderer);
groupBox4.Controls.Add(key); groupBox4.Controls.Add(key);
groupBox4.Controls.Add(encrypted); groupBox4.Controls.Add(encrypted);
groupBox4.Location = new System.Drawing.Point(13, 258); groupBox4.Location = new System.Drawing.Point(13, 287);
groupBox4.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); groupBox4.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
groupBox4.Name = "groupBox4"; groupBox4.Name = "groupBox4";
groupBox4.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); groupBox4.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
groupBox4.Size = new System.Drawing.Size(272, 187); groupBox4.Size = new System.Drawing.Size(272, 146);
groupBox4.TabIndex = 13; groupBox4.TabIndex = 13;
groupBox4.TabStop = false; groupBox4.TabStop = false;
groupBox4.Text = "Options"; groupBox4.Text = "Options";
// //
// disableAnimationClip
//
disableAnimationClip.AutoSize = true;
disableAnimationClip.Location = new System.Drawing.Point(119, 72);
disableAnimationClip.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
disableAnimationClip.Name = "disableAnimationClip";
disableAnimationClip.Size = new System.Drawing.Size(144, 19);
disableAnimationClip.TabIndex = 18;
disableAnimationClip.Text = "Disable AnimationClip";
disableAnimationClip.UseVisualStyleBackColor = true;
//
// minimalAssetMap // minimalAssetMap
// //
minimalAssetMap.AutoSize = true; minimalAssetMap.AutoSize = true;
@@ -569,7 +605,6 @@ namespace AssetStudioGUI
disableShader.Size = new System.Drawing.Size(103, 19); disableShader.Size = new System.Drawing.Size(103, 19);
disableShader.TabIndex = 16; disableShader.TabIndex = 16;
disableShader.Text = "Disable Shader"; disableShader.Text = "Disable Shader";
skipToolTip.SetToolTip(disableShader, "Skips the container recovery step.\nImproves loading when dealing with a large number of files.");
disableShader.UseVisualStyleBackColor = true; disableShader.UseVisualStyleBackColor = true;
// //
// disableRenderer // disableRenderer
@@ -581,7 +616,6 @@ namespace AssetStudioGUI
disableRenderer.Size = new System.Drawing.Size(114, 19); disableRenderer.Size = new System.Drawing.Size(114, 19);
disableRenderer.TabIndex = 15; disableRenderer.TabIndex = 15;
disableRenderer.Text = "Disable Renderer"; disableRenderer.Text = "Disable Renderer";
skipToolTip.SetToolTip(disableRenderer, "Skips the container recovery step.\nImproves loading when dealing with a large number of files.");
disableRenderer.UseVisualStyleBackColor = true; disableRenderer.UseVisualStyleBackColor = true;
// //
// ExportOptions // ExportOptions
@@ -661,10 +695,11 @@ namespace AssetStudioGUI
private System.Windows.Forms.ToolTip keyToolTip; private System.Windows.Forms.ToolTip keyToolTip;
private System.Windows.Forms.CheckBox exportUV0UV1; private System.Windows.Forms.CheckBox exportUV0UV1;
private System.Windows.Forms.GroupBox groupBox4; private System.Windows.Forms.GroupBox groupBox4;
private System.Windows.Forms.ToolTip resolveToolTip;
private System.Windows.Forms.ToolTip skipToolTip;
private System.Windows.Forms.CheckBox disableShader; private System.Windows.Forms.CheckBox disableShader;
private System.Windows.Forms.CheckBox disableRenderer; private System.Windows.Forms.CheckBox disableRenderer;
private System.Windows.Forms.CheckBox minimalAssetMap; private System.Windows.Forms.CheckBox minimalAssetMap;
private System.Windows.Forms.ComboBox assetGroupOptions;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.CheckBox disableAnimationClip;
} }
} }

View File

@@ -44,6 +44,7 @@ namespace AssetStudioGUI
key.Value = Properties.Settings.Default.key; key.Value = Properties.Settings.Default.key;
disableRenderer.Checked = Properties.Settings.Default.disableRenderer; disableRenderer.Checked = Properties.Settings.Default.disableRenderer;
disableShader.Checked = Properties.Settings.Default.disableShader; disableShader.Checked = Properties.Settings.Default.disableShader;
disableAnimationClip.Checked = Properties.Settings.Default.disableAnimationClip;
minimalAssetMap.Checked = Properties.Settings.Default.minimalAssetMap; minimalAssetMap.Checked = Properties.Settings.Default.minimalAssetMap;
} }
@@ -80,6 +81,7 @@ namespace AssetStudioGUI
Properties.Settings.Default.key = (byte)key.Value; Properties.Settings.Default.key = (byte)key.Value;
Properties.Settings.Default.disableRenderer = disableRenderer.Checked; Properties.Settings.Default.disableRenderer = disableRenderer.Checked;
Properties.Settings.Default.disableShader = disableShader.Checked; Properties.Settings.Default.disableShader = disableShader.Checked;
Properties.Settings.Default.disableAnimationClip = disableAnimationClip.Checked;
Properties.Settings.Default.minimalAssetMap = minimalAssetMap.Checked; Properties.Settings.Default.minimalAssetMap = minimalAssetMap.Checked;
Properties.Settings.Default.Save(); Properties.Settings.Default.Save();
MiHoYoBinData.Key = (byte)key.Value; MiHoYoBinData.Key = (byte)key.Value;
@@ -87,6 +89,7 @@ namespace AssetStudioGUI
AssetsHelper.Minimal = Properties.Settings.Default.minimalAssetMap; AssetsHelper.Minimal = Properties.Settings.Default.minimalAssetMap;
Renderer.Parsable = !Properties.Settings.Default.disableRenderer; Renderer.Parsable = !Properties.Settings.Default.disableRenderer;
Shader.Parsable = !Properties.Settings.Default.disableShader; Shader.Parsable = !Properties.Settings.Default.disableShader;
AnimationClip.Parsable = !Properties.Settings.Default.disableAnimationClip;
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
Close(); Close();
} }

View File

@@ -120,24 +120,9 @@
<metadata name="exportUvsTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="exportUvsTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value> <value>17, 17</value>
</metadata> </metadata>
<metadata name="exportUvsTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="keyToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="keyToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>162, 17</value> <value>162, 17</value>
</metadata> </metadata>
<metadata name="keyToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>162, 17</value>
</metadata>
<metadata name="skipToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>404, 17</value>
</metadata>
<metadata name="resolveToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>273, 17</value>
</metadata>
<metadata name="skipToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>404, 17</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>57</value> <value>57</value>
</metadata> </metadata>

View File

@@ -465,5 +465,41 @@ namespace AssetStudioGUI.Properties {
this["selectedCABMapName"] = value; this["selectedCABMapName"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool disableAnimationClip {
get {
return ((bool)(this["disableAnimationClip"]));
}
set {
this["disableAnimationClip"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool enableFileLogging {
get {
return ((bool)(this["enableFileLogging"]));
}
set {
this["enableFileLogging"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool enableVerbose {
get {
return ((bool)(this["enableVerbose"]));
}
set {
this["enableVerbose"] = value;
}
}
} }
} }

View File

@@ -113,5 +113,14 @@
<Setting Name="selectedCABMapName" Type="System.String" Scope="User"> <Setting Name="selectedCABMapName" Type="System.String" Scope="User">
<Value Profile="(Default)" /> <Value Profile="(Default)" />
</Setting> </Setting>
<Setting Name="disableAnimationClip" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="enableFileLogging" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="enableVerbose" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@@ -2,9 +2,9 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>0.90.00</Version> <Version>0.90.10</Version>
<AssemblyVersion>0.90.00</AssemblyVersion> <AssemblyVersion>0.90.10</AssemblyVersion>
<FileVersion>0.90.00</FileVersion> <FileVersion>0.90.10</FileVersion>
<Copyright>Copyright © Perfare 2018-2022</Copyright> <Copyright>Copyright © Perfare 2018-2022</Copyright>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -13,7 +13,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Kyaru.Texture2DDecoder" Version="0.17.0" /> <PackageReference Include="Kyaru.Texture2DDecoder" Version="0.17.0" />
<PackageReference Include="Kyaru.Texture2DDecoder.Windows" Version="0.1.0" /> <PackageReference Include="Kyaru.Texture2DDecoder.Windows" Version="0.1.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.4" /> <PackageReference Include="Mono.Cecil" Version="0.11.5" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" /> <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
</ItemGroup> </ItemGroup>

View File

@@ -244,7 +244,7 @@ namespace AssetStudio
private static void SetFrame(ImportedFrame frame, Vector3 t, Quaternion q, Vector3 s) private static void SetFrame(ImportedFrame frame, Vector3 t, Quaternion q, Vector3 s)
{ {
frame.LocalPosition = new Vector3(-t.X, t.Y, t.Z); frame.LocalPosition = new Vector3(-t.X, t.Y, t.Z);
frame.LocalRotation = Fbx.QuaternionToEuler(new Quaternion(q.X, -q.Y, -q.Z, q.W)); frame.LocalRotation = new Quaternion(q.X, -q.Y, -q.Z, q.W);
frame.LocalScale = s; frame.LocalScale = s;
} }
@@ -818,8 +818,8 @@ namespace AssetStudio
for (int i = 0; i < numKeys; i++) for (int i = 0; i < numKeys; i++)
{ {
var quat = quats[i]; var quat = quats[i];
var value = Fbx.QuaternionToEuler(new Quaternion(quat.X, -quat.Y, -quat.Z, quat.W)); var value = new Quaternion(quat.X, -quat.Y, -quat.Z, quat.W);
track.Rotations.Add(new ImportedKeyframe<Vector3>(times[i], value)); track.Rotations.Add(new ImportedKeyframe<Quaternion>(times[i], value));
} }
} }
foreach (var m_RotationCurve in animationClip.m_RotationCurves) foreach (var m_RotationCurve in animationClip.m_RotationCurves)
@@ -827,8 +827,8 @@ namespace AssetStudio
var track = iAnim.FindTrack(FixBonePath(animationClip, m_RotationCurve.path)); var track = iAnim.FindTrack(FixBonePath(animationClip, m_RotationCurve.path));
foreach (var m_Curve in m_RotationCurve.curve.m_Curve) foreach (var m_Curve in m_RotationCurve.curve.m_Curve)
{ {
var value = Fbx.QuaternionToEuler(new Quaternion(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z, m_Curve.value.W)); var value = new Quaternion(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z, m_Curve.value.W);
track.Rotations.Add(new ImportedKeyframe<Vector3>(m_Curve.time, value)); track.Rotations.Add(new ImportedKeyframe<Quaternion>(m_Curve.time, value));
} }
} }
foreach (var m_PositionCurve in animationClip.m_PositionCurves) foreach (var m_PositionCurve in animationClip.m_PositionCurves)
@@ -854,7 +854,8 @@ namespace AssetStudio
var track = iAnim.FindTrack(FixBonePath(animationClip, m_EulerCurve.path)); var track = iAnim.FindTrack(FixBonePath(animationClip, m_EulerCurve.path));
foreach (var m_Curve in m_EulerCurve.curve.m_Curve) foreach (var m_Curve in m_EulerCurve.curve.m_Curve)
{ {
track.Rotations.Add(new ImportedKeyframe<Vector3>(m_Curve.time, new Vector3(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z))); var value = Fbx.EulerToQuaternion(new Vector3(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z));
track.Rotations.Add(new ImportedKeyframe<Quaternion>(m_Curve.time, value));
} }
} }
} }
@@ -1009,14 +1010,13 @@ namespace AssetStudio
))); )));
break; break;
case 2: case 2:
var value = Fbx.QuaternionToEuler(new Quaternion track.Rotations.Add(new ImportedKeyframe<Quaternion>(time, new Quaternion
( (
data[curveIndex++ + offset], data[curveIndex++ + offset],
-data[curveIndex++ + offset], -data[curveIndex++ + offset],
-data[curveIndex++ + offset], -data[curveIndex++ + offset],
data[curveIndex++ + offset] data[curveIndex++ + offset]
)); )));
track.Rotations.Add(new ImportedKeyframe<Vector3>(time, value));
break; break;
case 3: case 3:
track.Scalings.Add(new ImportedKeyframe<Vector3>(time, new Vector3 track.Scalings.Add(new ImportedKeyframe<Vector3>(time, new Vector3
@@ -1027,12 +1027,13 @@ namespace AssetStudio
))); )));
break; break;
case 4: case 4:
track.Rotations.Add(new ImportedKeyframe<Vector3>(time, new Vector3 var value = Fbx.EulerToQuaternion(new Vector3
( (
data[curveIndex++ + offset], data[curveIndex++ + offset],
-data[curveIndex++ + offset], -data[curveIndex++ + offset],
-data[curveIndex++ + offset] -data[curveIndex++ + offset]
))); ));
track.Rotations.Add(new ImportedKeyframe<Quaternion>(time, value));
break; break;
default: default:
curveIndex++; curveIndex++;