diff --git a/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj b/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj index 4cd2ed6..890019c 100644 --- a/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj +++ b/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj @@ -2,9 +2,9 @@ net6.0;net7.0 - 0.80.30 - 0.80.30 - 0.80.30 + 0.80.65 + 0.80.65 + 0.80.65 Copyright © Perfare 2020-2022; Copyright © hozuki 2020 embedded diff --git a/AssetStudio/AssetStudio.csproj b/AssetStudio/AssetStudio.csproj index fd85873..1717872 100644 --- a/AssetStudio/AssetStudio.csproj +++ b/AssetStudio/AssetStudio.csproj @@ -2,9 +2,9 @@ net6.0;net7.0 - 0.80.30 - 0.80.30 - 0.80.30 + 0.80.65 + 0.80.65 + 0.80.65 Copyright © Razmoth 2022; Copyright © Perfare 2018-2022 embedded diff --git a/AssetStudio/AssetsHelper.cs b/AssetStudio/AssetsHelper.cs index 5e09f93..62a330c 100644 --- a/AssetStudio/AssetsHelper.cs +++ b/AssetStudio/AssetsHelper.cs @@ -19,7 +19,7 @@ namespace AssetStudio public static CancellationTokenSource tokenSource = new CancellationTokenSource(); private static string BaseFolder = ""; - private static Dictionary Map = new Dictionary(StringComparer.OrdinalIgnoreCase); + private static Dictionary CABMap = new Dictionary(StringComparer.OrdinalIgnoreCase); private static Dictionary> Offsets = new Dictionary>(); private static AssetsManager assetsManager = new AssetsManager() { Silent = true, SkipProcess = true, ResolveDependencies = false }; @@ -39,7 +39,7 @@ namespace AssetStudio public static void Clear() { - Map.Clear(); + CABMap.Clear(); Offsets.Clear(); BaseFolder = string.Empty; @@ -56,7 +56,7 @@ namespace AssetStudio public static bool TryAdd(string name, out string path) { - if (Map.TryGetValue(name, out var entry)) + if (CABMap.TryGetValue(name, out var entry)) { path = Path.Combine(BaseFolder, entry.Path); if (!Offsets.ContainsKey(path)) @@ -81,269 +81,326 @@ namespace AssetStudio return false; } - public static void BuildMap(string[] files, string mapName, string baseFolder, Game game) + public static void BuildCABMap(string[] files, string mapName, string baseFolder, Game game) { - Logger.Info($"Building Map..."); + Logger.Info("Building CABMap..."); try { - Map.Clear(); + CABMap.Clear(); Progress.Reset(); var collision = 0; BaseFolder = baseFolder; assetsManager.Game = game; - for (int i = 0; i < files.Length; i++) + foreach (var file in LoadFiles(files)) { - var file = files[i]; - assetsManager.LoadFiles(file); - if (assetsManager.assetsFileList.Count > 0) - { - var relativePath = Path.GetRelativePath(BaseFolder, file); - foreach (var assetsFile in assetsManager.assetsFileList) - { - if (tokenSource.IsCancellationRequested) - { - Logger.Info("Building Map has been aborted !!"); - return; - } - var dependencies = assetsFile.m_Externals.Select(x => x.fileName).ToArray(); - var entry = new Entry() - { - Path = relativePath, - Offset = assetsFile.offset, - Dependencies = dependencies - }; - - if (Map.ContainsKey(assetsFile.fileName)) - { - collision++; - continue; - } - Map.Add(assetsFile.fileName, entry); - } - Logger.Info($"[{i + 1}/{files.Length}] Processed {Path.GetFileName(file)}"); - Progress.Report(i + 1, files.Length); - } - assetsManager.Clear(); + BuildCABMap(file, ref collision); } - Map = Map.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value, StringComparer.OrdinalIgnoreCase); - var outputFile = Path.Combine(MapName, $"{mapName}.bin"); + DumpCABMap(mapName); - Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); - - using (var binaryFile = File.OpenWrite(outputFile)) - using (var writer = new BinaryWriter(binaryFile)) - { - writer.Write(BaseFolder); - writer.Write(Map.Count); - foreach (var kv in Map) - { - writer.Write(kv.Key); - writer.Write(kv.Value.Path); - writer.Write(kv.Value.Offset); - writer.Write(kv.Value.Dependencies.Length); - foreach (var cab in kv.Value.Dependencies) - { - writer.Write(cab); - } - } - } - - Logger.Info($"Map build successfully !! {collision} collisions found"); + Logger.Info($"CABMap build successfully !! {collision} collisions found"); } catch (Exception e) { - Logger.Warning($"Map was not build, {e}"); + Logger.Warning($"CABMap was not build, {e}"); } } - public static void BuildBoth(string[] files, string mapName, string baseFolder, Game game, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null, Regex[] nameFilters = null, Regex[] containerFilters = null) + private static IEnumerable LoadFiles(string[] files) { - Logger.Info($"Building Both..."); - Map.Clear(); - Progress.Reset(); - var collision = 0; - BaseFolder = baseFolder; - assetsManager.Game = game; - var assets = new List(); for (int i = 0; i < files.Length; i++) { var file = files[i]; assetsManager.LoadFiles(file); if (assetsManager.assetsFileList.Count > 0) { - var relativePath = Path.GetRelativePath(BaseFolder, file); - var containers = new List<(PPtr, string)>(); - var mihoyoBinDataNames = new List<(PPtr, string)>(); - var objectAssetItemDic = new Dictionary(); - var animators = new List<(PPtr, AssetEntry)>(); - foreach (var assetsFile in assetsManager.assetsFileList) - { - if (tokenSource.IsCancellationRequested) - { - Logger.Info("Building Map has been aborted !!"); - return; - } - var dependencies = assetsFile.m_Externals.Select(x => x.fileName).ToArray(); - var entry = new Entry() - { - Path = relativePath, - Offset = assetsFile.offset, - Dependencies = dependencies - }; - - if (Map.ContainsKey(assetsFile.fileName)) - { - collision++; - continue; - } - Map.Add(assetsFile.fileName, entry); - - foreach (var objInfo in assetsFile.m_Objects) - { - var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objInfo, game); - var obj = new Object(objectReader); - var asset = new AssetEntry() - { - Source = file, - PathID = objectReader.m_PathID, - Type = objectReader.type, - Container = string.Empty, - Name = string.Empty - }; - - var exportable = true; - try - { - switch (objectReader.type) - { - case ClassIDType.AssetBundle: - var assetBundle = new AssetBundle(objectReader); - foreach (var m_Container in assetBundle.m_Container) - { - var preloadIndex = m_Container.Value.preloadIndex; - var preloadSize = m_Container.Value.preloadSize; - var preloadEnd = preloadIndex + preloadSize; - for (int k = preloadIndex; k < preloadEnd; k++) - { - containers.Add((assetBundle.m_PreloadTable[k], m_Container.Key)); - } - } - obj = null; - asset.Name = assetBundle.m_Name; - exportable = false; - break; - case ClassIDType.GameObject: - var gameObject = new GameObject(objectReader); - obj = gameObject; - asset.Name = gameObject.m_Name; - exportable = false; - break; - case ClassIDType.Shader: - asset.Name = objectReader.ReadAlignedString(); - if (string.IsNullOrEmpty(asset.Name)) - { - var m_parsedForm = new SerializedShader(objectReader); - asset.Name = m_parsedForm.m_Name; - } - break; - case ClassIDType.Animator: - var component = new PPtr(objectReader); - animators.Add((component, asset)); - break; - case ClassIDType.MiHoYoBinData: - var MiHoYoBinData = new MiHoYoBinData(objectReader); - obj = MiHoYoBinData; - exportable = true; - break; - case ClassIDType.IndexObject: - var indexObject = new IndexObject(objectReader); - obj = null; - foreach (var index in indexObject.AssetMap) - { - mihoyoBinDataNames.Add((index.Value.Object, index.Key)); - } - asset.Name = "IndexObject"; - break; - case ClassIDType.Font: - case ClassIDType.Material: - case ClassIDType.Texture: - case ClassIDType.Mesh: - case ClassIDType.Sprite: - case ClassIDType.TextAsset: - case ClassIDType.Texture2D: - case ClassIDType.VideoClip: - case ClassIDType.AudioClip: - asset.Name = objectReader.ReadAlignedString(); - break; - default: - exportable = false; - break; - } - } - catch (Exception e) - { - var sb = new StringBuilder(); - sb.AppendLine("Unable to load object") - .AppendLine($"Assets {assetsFile.fileName}") - .AppendLine($"Path {assetsFile.originalPath}") - .AppendLine($"Type {objectReader.type}") - .AppendLine($"PathID {objectReader.m_PathID}") - .Append(e); - Logger.Error(sb.ToString()); - } - if (obj != null) - { - objectAssetItemDic.Add(obj, asset); - assetsFile.AddObject(obj); - } - var isMatchRegex = nameFilters.IsNullOrEmpty() || nameFilters.Any(x => x.IsMatch(asset.Name) || asset.Type == ClassIDType.Animator); - if (isMatchRegex && exportable) - { - assets.Add(asset); - } - } - } - foreach ((var pptr, var asset) in animators) - { - if (pptr.TryGet(out var gameObject) && (nameFilters.IsNullOrEmpty() || nameFilters.Any(x => x.IsMatch(gameObject.m_Name)))) - { - asset.Name = gameObject.m_Name; - } - } - foreach ((var pptr, var name) in mihoyoBinDataNames) - { - if (pptr.TryGet(out var miHoYoBinData)) - { - var asset = objectAssetItemDic[miHoYoBinData]; - if (int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var hash)) - { - asset.Name = name; - asset.Container = hash.ToString(); - } - else asset.Name = $"BinFile #{asset.PathID}"; - } - } - foreach ((var pptr, var container) in containers) - { - if (pptr.TryGet(out var obj)) - { - var item = objectAssetItemDic[obj]; - if (containerFilters.IsNullOrEmpty() || containerFilters.Any(x => x.IsMatch(container))) - { - item.Container = container; - } - else - { - assets.Remove(item); - } - } - } + yield return file; Logger.Info($"[{i + 1}/{files.Length}] Processed {Path.GetFileName(file)}"); Progress.Report(i + 1, files.Length); } assetsManager.Clear(); } + } + + private static void BuildCABMap(string file, ref int collision) + { + var relativePath = Path.GetRelativePath(BaseFolder, file); + foreach (var assetsFile in assetsManager.assetsFileList) + { + if (tokenSource.IsCancellationRequested) + { + Logger.Info("Building CABMap has been cancelled !!"); + return; + } + var dependencies = assetsFile.m_Externals.Select(x => x.fileName).ToArray(); + var entry = new Entry() + { + Path = relativePath, + Offset = assetsFile.offset, + Dependencies = dependencies + }; + + if (CABMap.ContainsKey(assetsFile.fileName)) + { + collision++; + continue; + } + CABMap.Add(assetsFile.fileName, entry); + } + } + + private static void DumpCABMap(string mapName) + { + CABMap = CABMap.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value, StringComparer.OrdinalIgnoreCase); + var outputFile = Path.Combine(MapName, $"{mapName}.bin"); + + Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); + + using (var binaryFile = File.OpenWrite(outputFile)) + using (var writer = new BinaryWriter(binaryFile)) + { + writer.Write(BaseFolder); + writer.Write(CABMap.Count); + foreach (var kv in CABMap) + { + writer.Write(kv.Key); + writer.Write(kv.Value.Path); + writer.Write(kv.Value.Offset); + writer.Write(kv.Value.Dependencies.Length); + foreach (var cab in kv.Value.Dependencies) + { + writer.Write(cab); + } + } + } + } + + public static void LoadCABMap(string mapName) + { + Logger.Info($"Loading {mapName}"); + try + { + CABMap.Clear(); + using (var fs = File.OpenRead(Path.Combine(MapName, $"{mapName}.bin"))) + using (var reader = new BinaryReader(fs)) + { + BaseFolder = reader.ReadString(); + var count = reader.ReadInt32(); + for (int i = 0; i < count; i++) + { + var cab = reader.ReadString(); + var path = reader.ReadString(); + var offset = reader.ReadInt64(); + var depCount = reader.ReadInt32(); + var dependencies = new List(); + for (int j = 0; j < depCount; j++) + { + var dependancy = reader.ReadString(); + dependencies.Add(dependancy); + } + var entry = new Entry() + { + Path = path, + Offset = offset, + Dependencies = dependencies.ToArray() + }; + CABMap.Add(cab, entry); + } + } + Logger.Info($"Loaded {mapName} !!"); + } + catch (Exception e) + { + Logger.Warning($"{mapName} was not loaded, {e}"); + } + } + + public static void BuildAssetMap(string[] files, string mapName, Game game, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null, ClassIDType[] typeFilters = null, Regex[] nameFilters = null, Regex[] containerFilters = null) + { + Logger.Info("Building AssetMap..."); + try + { + Progress.Reset(); + assetsManager.Game = game; + var assets = new List(); + foreach (var file in LoadFiles(files)) + { + BuildAssetMap(file, assets, typeFilters, nameFilters, containerFilters); + } + + UpdateContainers(assets, game); + + ExportAssetsMap(assets.ToArray(), mapName, savePath, exportListType, resetEvent); + } + catch(Exception e) + { + Logger.Warning($"AssetMap was not build, {e}"); + } + + } + + private static void BuildAssetMap(string file, List assets, ClassIDType[] typeFilters = null, Regex[] nameFilters = null, Regex[] containerFilters = null) + { + var containers = new List<(PPtr, string)>(); + var mihoyoBinDataNames = new List<(PPtr, string)>(); + var objectAssetItemDic = new Dictionary(); + var animators = new List<(PPtr, AssetEntry)>(); + foreach (var assetsFile in assetsManager.assetsFileList) + { + foreach (var objInfo in assetsFile.m_Objects) + { + if (tokenSource.IsCancellationRequested) + { + Logger.Info("Building AssetMap has been cancelled !!"); + return; + } + var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objInfo, assetsManager.Game); + var obj = new Object(objectReader); + var asset = new AssetEntry() + { + Source = file, + PathID = objectReader.m_PathID, + Type = objectReader.type, + Container = "" + }; + + var exportable = true; + try + { + switch (objectReader.type) + { + case ClassIDType.AssetBundle: + var assetBundle = new AssetBundle(objectReader); + foreach (var m_Container in assetBundle.m_Container) + { + var preloadIndex = m_Container.Value.preloadIndex; + var preloadSize = m_Container.Value.preloadSize; + var preloadEnd = preloadIndex + preloadSize; + for (int k = preloadIndex; k < preloadEnd; k++) + { + containers.Add((assetBundle.m_PreloadTable[k], m_Container.Key)); + } + } + obj = null; + asset.Name = assetBundle.m_Name; + exportable = false; + break; + case ClassIDType.GameObject: + var gameObject = new GameObject(objectReader); + obj = gameObject; + asset.Name = gameObject.m_Name; + exportable = false; + break; + case ClassIDType.Shader when Shader.Parsable: + asset.Name = objectReader.ReadAlignedString(); + if (string.IsNullOrEmpty(asset.Name)) + { + var m_parsedForm = new SerializedShader(objectReader); + asset.Name = m_parsedForm.m_Name; + } + break; + case ClassIDType.Animator: + var component = new PPtr(objectReader); + animators.Add((component, asset)); + break; + case ClassIDType.MiHoYoBinData: + var MiHoYoBinData = new MiHoYoBinData(objectReader); + obj = MiHoYoBinData; + exportable = true; + break; + case ClassIDType.IndexObject: + var indexObject = new IndexObject(objectReader); + obj = null; + foreach (var index in indexObject.AssetMap) + { + mihoyoBinDataNames.Add((index.Value.Object, index.Key)); + } + asset.Name = "IndexObject"; + break; + case ClassIDType.Font: + case ClassIDType.Material: + case ClassIDType.Texture: + case ClassIDType.Mesh: + case ClassIDType.Sprite: + case ClassIDType.TextAsset: + case ClassIDType.Texture2D: + case ClassIDType.VideoClip: + case ClassIDType.AudioClip: + asset.Name = objectReader.ReadAlignedString(); + break; + default: + exportable = false; + break; + } + } + catch (Exception e) + { + var sb = new StringBuilder(); + sb.AppendLine("Unable to load object") + .AppendLine($"Assets {assetsFile.fileName}") + .AppendLine($"Path {assetsFile.originalPath}") + .AppendLine($"Type {objectReader.type}") + .AppendLine($"PathID {objectReader.m_PathID}") + .Append(e); + Logger.Error(sb.ToString()); + } + if (obj != null) + { + objectAssetItemDic.Add(obj, asset); + assetsFile.AddObject(obj); + } + var isMatchRegex = nameFilters.IsNullOrEmpty() || nameFilters.Any(x => x.IsMatch(asset.Name) || asset.Type == ClassIDType.Animator); + var isFilteredType = typeFilters.IsNullOrEmpty() || typeFilters.Contains(asset.Type) || asset.Type == ClassIDType.Animator; + if (isMatchRegex && isFilteredType && exportable) + { + assets.Add(asset); + } + } + } + foreach ((var pptr, var asset) in animators) + { + if (pptr.TryGet(out var gameObject) && (nameFilters.IsNullOrEmpty() || nameFilters.Any(x => x.IsMatch(gameObject.m_Name))) && (typeFilters.IsNullOrEmpty() || typeFilters.Contains(asset.Type))) + { + asset.Name = gameObject.m_Name; + } + else + { + assets.Remove(asset); + } + + } + foreach ((var pptr, var name) in mihoyoBinDataNames) + { + if (pptr.TryGet(out var miHoYoBinData)) + { + var asset = objectAssetItemDic[miHoYoBinData]; + if (int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var hash)) + { + asset.Name = name; + asset.Container = hash.ToString(); + } + else asset.Name = $"BinFile #{asset.PathID}"; + } + } + foreach ((var pptr, var container) in containers) + { + if (pptr.TryGet(out var obj)) + { + var item = objectAssetItemDic[obj]; + if (containerFilters.IsNullOrEmpty() || containerFilters.Any(x => x.IsMatch(container))) + { + item.Container = container; + } + else + { + assets.Remove(item); + } + } + } + } + + private static void UpdateContainers(List assets, Game game) + { if (game.Type.IsGISubGroup() && assets.Count > 0) { Logger.Info("Updating Containers..."); @@ -369,267 +426,9 @@ namespace AssetStudio } Logger.Info("Updated !!"); } - - Map = Map.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value, StringComparer.OrdinalIgnoreCase); - var outputFile = Path.Combine(MapName, $"{mapName}.bin"); - - Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); - - using (var binaryFile = File.OpenWrite(outputFile)) - using (var writer = new BinaryWriter(binaryFile)) - { - writer.Write(BaseFolder); - writer.Write(Map.Count); - foreach (var kv in Map) - { - writer.Write(kv.Key); - writer.Write(kv.Value.Path); - writer.Write(kv.Value.Offset); - writer.Write(kv.Value.Dependencies.Length); - foreach (var cab in kv.Value.Dependencies) - { - writer.Write(cab); - } - } - } - - Logger.Info($"Map build successfully !! {collision} collisions found"); - ExportAssetsMap(assets.ToArray(), mapName, savePath, exportListType, resetEvent); } - public static bool LoadMap(string mapName) - { - Logger.Info($"Loading {mapName}"); - try - { - Map.Clear(); - using (var fs = File.OpenRead(Path.Combine(MapName, $"{mapName}.bin"))) - using (var reader = new BinaryReader(fs)) - { - BaseFolder = reader.ReadString(); - var count = reader.ReadInt32(); - for (int i = 0; i < count; i++) - { - var cab = reader.ReadString(); - var path = reader.ReadString(); - var offset = reader.ReadInt64(); - var depCount = reader.ReadInt32(); - var dependencies = new List(); - for (int j = 0; j < depCount; j++) - { - var dependancy = reader.ReadString(); - dependencies.Add(dependancy); - } - var entry = new Entry() - { - Path = path, - Offset = offset, - Dependencies = dependencies.ToArray() - }; - Map.Add(cab, entry); - } - } - Logger.Info($"Loaded {mapName} !!"); - return true; - } - catch (Exception e) - { - Logger.Warning($"{mapName} was not loaded, {e}"); - } - - return false; - } - - public static AssetEntry[] BuildAssetMap(string[] files, Game game, Regex[] nameFilters = null, Regex[] containerFilters = null) - { - Progress.Reset(); - assetsManager.Game = game; - var assets = new List(); - for (int i = 0; i < files.Length; i++) - { - var file = files[i]; - assetsManager.LoadFiles(file); - if (assetsManager.assetsFileList.Count > 0) - { - var containers = new List<(PPtr, string)>(); - var mihoyoBinDataNames = new List<(PPtr, string)>(); - var objectAssetItemDic = new Dictionary(); - var animators = new List<(PPtr, AssetEntry)>(); - foreach (var assetsFile in assetsManager.assetsFileList) - { - foreach (var objInfo in assetsFile.m_Objects) - { - var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objInfo, game); - var obj = new Object(objectReader); - var asset = new AssetEntry() - { - Source = file, - PathID = objectReader.m_PathID, - Type = objectReader.type, - Container = "" - }; - - var exportable = true; - try - { - switch (objectReader.type) - { - case ClassIDType.AssetBundle: - var assetBundle = new AssetBundle(objectReader); - foreach (var m_Container in assetBundle.m_Container) - { - var preloadIndex = m_Container.Value.preloadIndex; - var preloadSize = m_Container.Value.preloadSize; - var preloadEnd = preloadIndex + preloadSize; - for (int k = preloadIndex; k < preloadEnd; k++) - { - containers.Add((assetBundle.m_PreloadTable[k], m_Container.Key)); - } - } - obj = null; - asset.Name = assetBundle.m_Name; - exportable = false; - break; - case ClassIDType.GameObject: - var gameObject = new GameObject(objectReader); - obj = gameObject; - asset.Name = gameObject.m_Name; - exportable = false; - break; - case ClassIDType.Shader: - asset.Name = objectReader.ReadAlignedString(); - if (string.IsNullOrEmpty(asset.Name)) - { - var m_parsedForm = new SerializedShader(objectReader); - asset.Name = m_parsedForm.m_Name; - } - break; - case ClassIDType.Animator: - var component = new PPtr(objectReader); - animators.Add((component, asset)); - break; - case ClassIDType.MiHoYoBinData: - var MiHoYoBinData = new MiHoYoBinData(objectReader); - obj = MiHoYoBinData; - exportable = true; - break; - case ClassIDType.IndexObject: - var indexObject = new IndexObject(objectReader); - obj = null; - foreach (var index in indexObject.AssetMap) - { - mihoyoBinDataNames.Add((index.Value.Object, index.Key)); - } - asset.Name = "IndexObject"; - break; - case ClassIDType.Font: - case ClassIDType.Material: - case ClassIDType.Texture: - case ClassIDType.Mesh: - case ClassIDType.Sprite: - case ClassIDType.TextAsset: - case ClassIDType.Texture2D: - case ClassIDType.VideoClip: - case ClassIDType.AudioClip: - asset.Name = objectReader.ReadAlignedString(); - break; - default: - exportable = false; - break; - } - } - catch (Exception e) - { - var sb = new StringBuilder(); - sb.AppendLine("Unable to load object") - .AppendLine($"Assets {assetsFile.fileName}") - .AppendLine($"Path {assetsFile.originalPath}") - .AppendLine($"Type {objectReader.type}") - .AppendLine($"PathID {objectReader.m_PathID}") - .Append(e); - Logger.Error(sb.ToString()); - } - if (obj != null) - { - objectAssetItemDic.Add(obj, asset); - assetsFile.AddObject(obj); - } - var isMatchRegex = nameFilters.IsNullOrEmpty() || nameFilters.Any(x => x.IsMatch(asset.Name) || asset.Type == ClassIDType.Animator); - if (isMatchRegex && exportable) - { - assets.Add(asset); - } - } - } - foreach ((var pptr, var asset) in animators) - { - if (pptr.TryGet(out var gameObject) && (nameFilters.IsNullOrEmpty() || nameFilters.Any(x => x.IsMatch(gameObject.m_Name)))) - { - asset.Name = gameObject.m_Name; - } - } - foreach((var pptr, var name) in mihoyoBinDataNames) - { - if (pptr.TryGet(out var miHoYoBinData)) - { - var asset = objectAssetItemDic[miHoYoBinData]; - if (int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var hash)) - { - asset.Name = name; - asset.Container = hash.ToString(); - } - else asset.Name = $"BinFile #{asset.PathID}"; - } - } - foreach ((var pptr, var container) in containers) - { - if (pptr.TryGet(out var obj)) - { - var item = objectAssetItemDic[obj]; - if (containerFilters.IsNullOrEmpty() || containerFilters.Any(x => x.IsMatch(container))) - { - item.Container = container; - } - else - { - assets.Remove(item); - } - } - } - Logger.Info($"[{i + 1}/{files.Length}] Processed {Path.GetFileName(file)}"); - Progress.Report(i + 1, files.Length); - } - assetsManager.Clear(); - } - if (game.Type.IsGISubGroup() && assets.Count > 0) - { - Logger.Info("Updating Containers..."); - foreach (var asset in assets) - { - if (int.TryParse(asset.Container, out var value)) - { - var last = unchecked((uint)value); - var name = Path.GetFileNameWithoutExtension(asset.Source); - if (uint.TryParse(name, out var id)) - { - var path = ResourceIndex.GetContainer(id, last); - if (!string.IsNullOrEmpty(path)) - { - asset.Container = path; - if (asset.Type == ClassIDType.MiHoYoBinData) - { - asset.Name = Path.GetFileNameWithoutExtension(path); - } - } - } - } - Logger.Info("Updated !!"); - } - } - return assets.ToArray(); - } - - public static void ExportAssetsMap(AssetEntry[] toExportAssets, string name, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null) + private static void ExportAssetsMap(AssetEntry[] toExportAssets, string name, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null) { ThreadPool.QueueUserWorkItem(state => { @@ -683,5 +482,26 @@ namespace AssetStudio resetEvent?.Set(); }); } + public static void BuildBoth(string[] files, string mapName, string baseFolder, Game game, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null, ClassIDType[] typeFilters = null, Regex[] nameFilters = null, Regex[] containerFilters = null) + { + Logger.Info($"Building Both..."); + CABMap.Clear(); + Progress.Reset(); + var collision = 0; + BaseFolder = baseFolder; + assetsManager.Game = game; + var assets = new List(); + foreach(var file in LoadFiles(files)) + { + BuildCABMap(file, ref collision); + BuildAssetMap(file, assets, typeFilters, nameFilters, containerFilters); + } + + UpdateContainers(assets, game); + DumpCABMap(mapName); + + Logger.Info($"Map build successfully !! {collision} collisions found"); + ExportAssetsMap(assets.ToArray(), mapName, savePath, exportListType, resetEvent); + } } } diff --git a/AssetStudio/AssetsManager.cs b/AssetStudio/AssetsManager.cs index 839a58d..e5e281d 100644 --- a/AssetStudio/AssetsManager.cs +++ b/AssetStudio/AssetsManager.cs @@ -19,7 +19,6 @@ namespace AssetStudio public string SpecifyUnityVersion; public CancellationTokenSource tokenSource = new CancellationTokenSource(); public List assetsFileList = new List(); - public Dictionary ExportableTypes = new Dictionary(); internal Dictionary assetsFileIndexCache = new Dictionary(StringComparer.OrdinalIgnoreCase); internal Dictionary resourceFileReaders = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -29,25 +28,6 @@ namespace AssetStudio internal HashSet noexistFiles = new HashSet(StringComparer.OrdinalIgnoreCase); internal HashSet assetsFileListHash = new HashSet(StringComparer.OrdinalIgnoreCase); - public AssetsManager() - { - ExportableTypes.Add(ClassIDType.GameObject, true); - ExportableTypes.Add(ClassIDType.Material, true); - ExportableTypes.Add(ClassIDType.Texture2D, true); - ExportableTypes.Add(ClassIDType.AudioClip, true); - ExportableTypes.Add(ClassIDType.VideoClip, true); - ExportableTypes.Add(ClassIDType.Mesh, false); - ExportableTypes.Add(ClassIDType.Renderer, false); - ExportableTypes.Add(ClassIDType.Shader, true); - ExportableTypes.Add(ClassIDType.TextAsset, true); - ExportableTypes.Add(ClassIDType.AnimationClip, true); - ExportableTypes.Add(ClassIDType.MonoBehaviour, true); - ExportableTypes.Add(ClassIDType.Font, true); - ExportableTypes.Add(ClassIDType.Sprite, true); - ExportableTypes.Add(ClassIDType.Animator, true); - ExportableTypes.Add(ClassIDType.MiHoYoBinData, true); - } - public void LoadFiles(params string[] files) { if (Silent) @@ -115,7 +95,7 @@ namespace AssetStudio assetsFileListHash.Clear(); AssetsHelper.ClearOffsets(); - if (!SkipProcess && !tokenSource.IsCancellationRequested) + if (!SkipProcess) { ReadAssets(); ProcessAssets(); @@ -457,7 +437,7 @@ namespace AssetStudio Logger.Info("Loading " + reader.FullPath); try { - using var stream = new SubStream(reader.BaseStream, 0); + using var stream = new OffsetStream(reader.BaseStream, 0); if (AssetsHelper.TryGet(reader.FullPath, out var offsets)) { foreach (var offset in offsets) @@ -527,7 +507,7 @@ namespace AssetStudio { do { - var name = stream.Position.ToString("X8"); + var name = stream.AbsolutePosition.ToString("X8"); Logger.Info($"Loading Block {name}"); var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name); @@ -535,14 +515,14 @@ namespace AssetStudio switch (subReader.FileType) { case FileType.BundleFile: - LoadBundleFile(subReader, reader.FullPath, stream.Position, false); + LoadBundleFile(subReader, reader.FullPath, stream.AbsolutePosition, false); break; case FileType.Mhy0File: - LoadMhy0File(subReader, reader.FullPath, stream.Position, false); + LoadMhy0File(subReader, reader.FullPath, stream.AbsolutePosition, false); break; } - stream.Offset += stream.Position; + stream.Offset = stream.AbsolutePosition; } while (stream.Remaining > 0); } } @@ -650,13 +630,9 @@ namespace AssetStudio { if (tokenSource.IsCancellationRequested) { - Logger.Info("Reading assets has been aborted !!"); + Logger.Info("Reading assets has been cancelled !!"); return; } - if (assetsFile.IsLoaded(objectInfo)) - { - continue; - } var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objectInfo, Game); try { @@ -666,10 +642,10 @@ namespace AssetStudio case ClassIDType.Animation: obj = new Animation(objectReader); break; - case ClassIDType.AnimationClip when ExportableTypes[ClassIDType.AnimationClip]: + case ClassIDType.AnimationClip: obj = new AnimationClip(objectReader); break; - case ClassIDType.Animator when ExportableTypes[ClassIDType.Animator]: + case ClassIDType.Animator: obj = new Animator(objectReader); break; case ClassIDType.AnimatorController: @@ -681,37 +657,37 @@ namespace AssetStudio case ClassIDType.AssetBundle: obj = new AssetBundle(objectReader); break; - case ClassIDType.AudioClip when ExportableTypes[ClassIDType.AudioClip]: + case ClassIDType.AudioClip: obj = new AudioClip(objectReader); break; case ClassIDType.Avatar: obj = new Avatar(objectReader); break; - case ClassIDType.Font when ExportableTypes[ClassIDType.Font]: + case ClassIDType.Font: obj = new Font(objectReader); break; - case ClassIDType.GameObject when ExportableTypes[ClassIDType.GameObject]: + case ClassIDType.GameObject: obj = new GameObject(objectReader); break; - case ClassIDType.IndexObject when ExportableTypes[ClassIDType.MiHoYoBinData]: + case ClassIDType.IndexObject: obj = new IndexObject(objectReader); break; - case ClassIDType.Material when ExportableTypes[ClassIDType.Material]: + case ClassIDType.Material: obj = new Material(objectReader); break; - case ClassIDType.Mesh when ExportableTypes[ClassIDType.Mesh]: + case ClassIDType.Mesh: obj = new Mesh(objectReader); break; case ClassIDType.MeshFilter: obj = new MeshFilter(objectReader); break; - case ClassIDType.MeshRenderer when ExportableTypes[ClassIDType.Renderer]: + case ClassIDType.MeshRenderer when Renderer.Parsable: obj = new MeshRenderer(objectReader); break; - case ClassIDType.MiHoYoBinData when ExportableTypes[ClassIDType.MiHoYoBinData]: + case ClassIDType.MiHoYoBinData: obj = new MiHoYoBinData(objectReader); break; - case ClassIDType.MonoBehaviour when ExportableTypes[ClassIDType.MonoBehaviour]: + case ClassIDType.MonoBehaviour: obj = new MonoBehaviour(objectReader); break; case ClassIDType.MonoScript: @@ -726,28 +702,28 @@ namespace AssetStudio case ClassIDType.RectTransform: obj = new RectTransform(objectReader); break; - case ClassIDType.Shader when ExportableTypes[ClassIDType.Shader]: + case ClassIDType.Shader when Shader.Parsable: obj = new Shader(objectReader); break; - case ClassIDType.SkinnedMeshRenderer when ExportableTypes[ClassIDType.Renderer]: + case ClassIDType.SkinnedMeshRenderer when Renderer.Parsable: obj = new SkinnedMeshRenderer(objectReader); break; - case ClassIDType.Sprite when ExportableTypes[ClassIDType.Sprite]: + case ClassIDType.Sprite: obj = new Sprite(objectReader); break; case ClassIDType.SpriteAtlas: obj = new SpriteAtlas(objectReader); break; - case ClassIDType.TextAsset when ExportableTypes[ClassIDType.TextAsset]: + case ClassIDType.TextAsset: obj = new TextAsset(objectReader); break; - case ClassIDType.Texture2D when ExportableTypes[ClassIDType.Texture2D]: + case ClassIDType.Texture2D: obj = new Texture2D(objectReader); break; case ClassIDType.Transform: obj = new Transform(objectReader); break; - case ClassIDType.VideoClip when ExportableTypes[ClassIDType.VideoClip]: + case ClassIDType.VideoClip: obj = new VideoClip(objectReader); break; case ClassIDType.ResourceManager: @@ -778,9 +754,6 @@ namespace AssetStudio private void ProcessAssets() { - if (tokenSource.IsCancellationRequested) - return; - Logger.Info("Process Assets..."); foreach (var assetsFile in assetsFileList) @@ -789,62 +762,62 @@ namespace AssetStudio { if (tokenSource.IsCancellationRequested) { - Logger.Info("Processing assets has been aborted !!"); + Logger.Info("Processing assets has been cancelled !!"); return; } if (obj is GameObject m_GameObject) { - foreach (var pptr in m_GameObject.m_Components) - { - if (pptr.TryGet(out var m_Component)) + foreach (var pptr in m_GameObject.m_Components) { - switch (m_Component) + if (pptr.TryGet(out var m_Component)) { + switch (m_Component) + { case Transform m_Transform: m_GameObject.m_Transform = m_Transform; - break; + break; case MeshRenderer m_MeshRenderer: m_GameObject.m_MeshRenderer = m_MeshRenderer; - break; + break; case MeshFilter m_MeshFilter: m_GameObject.m_MeshFilter = m_MeshFilter; - break; + break; case SkinnedMeshRenderer m_SkinnedMeshRenderer: m_GameObject.m_SkinnedMeshRenderer = m_SkinnedMeshRenderer; - break; + break; case Animator m_Animator: m_GameObject.m_Animator = m_Animator; - break; + break; case Animation m_Animation: m_GameObject.m_Animation = m_Animation; - break; + break; + } } } - } } else if (obj is SpriteAtlas m_SpriteAtlas) { - if (m_SpriteAtlas.m_RenderDataMap.Count > 0) - { - foreach (var m_PackedSprite in m_SpriteAtlas.m_PackedSprites) + if (m_SpriteAtlas.m_RenderDataMap.Count > 0) { - if (m_PackedSprite.TryGet(out var m_Sprite)) + foreach (var m_PackedSprite in m_SpriteAtlas.m_PackedSprites) { - if (m_Sprite.m_SpriteAtlas.IsNull) + if (m_PackedSprite.TryGet(out var m_Sprite)) { - m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas); - } - else - { - m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlaOld); - if (m_SpriteAtlaOld.m_IsVariant) + if (m_Sprite.m_SpriteAtlas.IsNull) { m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas); } + else + { + m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlaOld); + if (m_SpriteAtlaOld.m_IsVariant) + { + m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas); + } + } } } } - } } } } diff --git a/AssetStudio/Classes/Animator.cs b/AssetStudio/Classes/Animator.cs index a36bf1e..c565493 100644 --- a/AssetStudio/Classes/Animator.cs +++ b/AssetStudio/Classes/Animator.cs @@ -25,6 +25,11 @@ namespace AssetStudio { var m_UpdateMode = reader.ReadInt32(); } + + if (reader.Game.Type.IsSR()) + { + var m_MotionSkeletonMode = reader.ReadInt32(); + } var m_ApplyRootMotion = reader.ReadBoolean(); if (version[0] == 4 && version[1] >= 5) //4.5 and up - 5.0 down diff --git a/AssetStudio/Classes/Renderer.cs b/AssetStudio/Classes/Renderer.cs index 6c01922..8f86fc8 100644 --- a/AssetStudio/Classes/Renderer.cs +++ b/AssetStudio/Classes/Renderer.cs @@ -19,6 +19,8 @@ namespace AssetStudio public abstract class Renderer : Component { + public static bool Parsable; + public PPtr[] m_Materials; public StaticBatchInfo m_StaticBatchInfo; public uint[] m_SubsetIndices; @@ -218,7 +220,7 @@ namespace AssetStudio var m_UseHighestMip = reader.ReadBoolean(); reader.AlignStream(); } - if (reader.Game.Type.IsSRCB3()) + if (reader.Game.Type.IsSR()) { var RenderFlag = reader.ReadUInt32(); reader.AlignStream(); diff --git a/AssetStudio/Classes/Shader.cs b/AssetStudio/Classes/Shader.cs index e8c94b0..9a99a49 100644 --- a/AssetStudio/Classes/Shader.cs +++ b/AssetStudio/Classes/Shader.cs @@ -578,6 +578,9 @@ namespace AssetStudio public UAVParameter[] m_UAVParams; public SamplerParameter[] m_Samplers; + private static bool HasGlobalLocalKeywordIndices(ObjectReader reader) => reader.serializedType.Match("E99740711222CD922E9A6F92FF1EB07A") || reader.serializedType.Match("450A058C218DAF000647948F2F59DA6D"); + private static bool HasInstancedStructuredBuffers(ObjectReader reader) => reader.serializedType.Match("E99740711222CD922E9A6F92FF1EB07A"); + public SerializedSubProgram(ObjectReader reader) { var version = reader.version; @@ -585,7 +588,7 @@ namespace AssetStudio m_BlobIndex = reader.ReadUInt32(); m_Channels = new ParserBindChannels(reader); - if ((version[0] >= 2019 && version[0] < 2021) || (version[0] == 2021 && version[1] < 2) || reader.Match("E99740711222CD922E9A6F92FF1EB07A") || reader.Match("450A058C218DAF000647948F2F59DA6D")) //2019 ~2021.1 + if ((version[0] >= 2019 && version[0] < 2021) || (version[0] == 2021 && version[1] < 2) || HasGlobalLocalKeywordIndices(reader)) //2019 ~2021.1 { var m_GlobalKeywordIndices = reader.ReadUInt16Array(); reader.AlignStream(); @@ -687,7 +690,7 @@ namespace AssetStudio } } - if (reader.Match("E99740711222CD922E9A6F92FF1EB07A")) + if (HasInstancedStructuredBuffers(reader)) { int numInstancedStructuredBuffers = reader.ReadInt32(); var m_InstancedStructuredBuffers = new ConstantBuffer[numInstancedStructuredBuffers]; @@ -974,6 +977,8 @@ namespace AssetStudio public class Shader : NamedObject { + public static bool Parsable; + public byte[] m_Script; //5.3 - 5.4 public uint decompressedSize; diff --git a/AssetStudio/FileReader.cs b/AssetStudio/FileReader.cs index dfab944..b1c3a68 100644 --- a/AssetStudio/FileReader.cs +++ b/AssetStudio/FileReader.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Linq; using static AssetStudio.ImportHelper; @@ -172,7 +173,20 @@ namespace AssetStudio } if (reader.FileType == FileType.BundleFile && game.Type.IsBlockFile()) { - reader.FileType = FileType.BlockFile; + try + { + var signature = reader.ReadStringToNull(); + reader.ReadInt32(); + reader.ReadStringToNull(); + reader.ReadStringToNull(); + var size = reader.ReadInt64(); + if (!(signature == "UnityFS" && size == reader.BaseStream.Length)) + { + reader.FileType = FileType.BlockFile; + } + } + catch (Exception) { } + reader.Position = 0; } return reader; diff --git a/AssetStudio/GameManager.cs b/AssetStudio/GameManager.cs index e9962b4..06b43fb 100644 --- a/AssetStudio/GameManager.cs +++ b/AssetStudio/GameManager.cs @@ -21,7 +21,7 @@ namespace AssetStudio Games.Add(index++, new Mr0k(GameType.BH3, BH3ExpansionKey, BH3SBox, BH3InitVector, BH3BlockKey)); Games.Add(index++, new Mr0k(GameType.BH3_Pre, PackExpansionKey, blockKey: PackBlockKey)); Games.Add(index++, new Mr0k(GameType.SR_CB2, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey)); - Games.Add(index++, new Mr0k(GameType.SR_CB3, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey)); + Games.Add(index++, new Mr0k(GameType.SR, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey)); Games.Add(index++, new Mr0k(GameType.ZZZ_CB1, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey)); Games.Add(index++, new Mr0k(GameType.TOT, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey, postKey: ToTKey)); Games.Add(index++, new Game(GameType.Naraka)); @@ -124,7 +124,7 @@ namespace AssetStudio BH3_Pre, ZZZ_CB1, SR_CB2, - SR_CB3, + SR, TOT, Naraka, EnsembleStars, @@ -148,7 +148,7 @@ namespace AssetStudio public static bool IsBH3Pre(this GameType type) => type == GameType.BH3_Pre; public static bool IsZZZCB1(this GameType type) => type == GameType.ZZZ_CB1; public static bool IsSRCB2(this GameType type) => type == GameType.SR_CB2; - public static bool IsSRCB3(this GameType type) => type == GameType.SR_CB3; + public static bool IsSR(this GameType type) => type == GameType.SR; public static bool IsTOT(this GameType type) => type == GameType.TOT; public static bool IsNaraka(this GameType type) => type == GameType.Naraka; public static bool IsOPFP(this GameType type) => type == GameType.OPFP; @@ -172,19 +172,19 @@ namespace AssetStudio public static bool IsSRGroup(this GameType type) => type switch { - GameType.SR_CB2 or GameType.SR_CB3 => true, + GameType.SR_CB2 or GameType.SR => true, _ => false, }; public static bool IsBlockFile(this GameType type) => type switch { - GameType.BH3 or GameType.BH3_Pre or GameType.SR_CB3 or GameType.GI_Pack or GameType.TOT => true, + GameType.BH3 or GameType.BH3_Pre or GameType.SR or GameType.GI_Pack or GameType.TOT => true, _ => false, }; public static bool IsMhyGroup(this GameType type) => type switch { - GameType.GI or GameType.GI_Pack or GameType.GI_CB1 or GameType.GI_CB2 or GameType.GI_CB3 or GameType.GI_CB3Pre or GameType.BH3 or GameType.SR_CB2 or GameType.SR_CB3 or GameType.TOT => true, + GameType.GI or GameType.GI_Pack or GameType.GI_CB1 or GameType.GI_CB2 or GameType.GI_CB3 or GameType.GI_CB3Pre or GameType.BH3 or GameType.SR_CB2 or GameType.SR or GameType.TOT => true, _ => false, }; } diff --git a/AssetStudio/ImportHelper.cs b/AssetStudio/ImportHelper.cs index a26bcc3..1aa2498 100644 --- a/AssetStudio/ImportHelper.cs +++ b/AssetStudio/ImportHelper.cs @@ -279,7 +279,7 @@ namespace AssetStudio var idx = data.Search("UnityFS"); if (idx != -1) { - stream = new SubStream(stream, idx); + stream = new OffsetStream(stream, idx); } return new FileReader(reader.FullPath, stream); @@ -295,11 +295,11 @@ namespace AssetStudio var idx2 = data[(idx + 1)..].Search("UnityFS"); if (idx2 != -1) { - stream = new SubStream(stream, idx + idx2 + 1); + stream = new OffsetStream(stream, idx + idx2 + 1); } else { - stream = new SubStream(stream, idx); + stream = new OffsetStream(stream, idx); } } @@ -371,7 +371,7 @@ namespace AssetStudio reader.Position = 0; return reader; } - var stream = new SubStream(reader.BaseStream, idx); + var stream = new OffsetStream(reader.BaseStream, idx); return new FileReader(reader.FullPath, stream); } public static FileReader ParseHelixWaltz2(FileReader reader) diff --git a/AssetStudio/ObjectReader.cs b/AssetStudio/ObjectReader.cs index 4f902e9..a043c4a 100644 --- a/AssetStudio/ObjectReader.cs +++ b/AssetStudio/ObjectReader.cs @@ -21,7 +21,7 @@ namespace AssetStudio public int[] version => assetsFile.version; public BuildType buildType => assetsFile.buildType; - public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo, Game game) : base(new SubStream(reader.BaseStream, objectInfo.byteStart, objectInfo.byteSize), reader.Endian) + public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo, Game game) : base(reader.BaseStream, reader.Endian) { this.assetsFile = assetsFile; Game = game; @@ -41,11 +41,9 @@ namespace AssetStudio m_Version = assetsFile.header.m_Version; } - public bool Match(string hash) => Convert.ToHexString(serializedType.m_OldTypeHash) == hash; - public void Reset() { - Position = 0; + Position = byteStart; } } } diff --git a/AssetStudio/SubStream.cs b/AssetStudio/OffsetStream.cs similarity index 64% rename from AssetStudio/SubStream.cs rename to AssetStudio/OffsetStream.cs index aa8ce62..824890f 100644 --- a/AssetStudio/SubStream.cs +++ b/AssetStudio/OffsetStream.cs @@ -3,29 +3,15 @@ using System.IO; namespace AssetStudio { - public class SubStream : Stream + public class OffsetStream : Stream { private readonly Stream _baseStream; private long _offset; - private long _size; public override bool CanRead => _baseStream.CanRead; public override bool CanSeek => _baseStream.CanSeek; public override bool CanWrite => false; - public long Size - { - get => _size; - set - { - if (value < 0 || value > _baseStream.Length || value + _offset > _baseStream.Length) - { - throw new IOException($"{nameof(Size)} is out of stream bound"); - } - _size = value; - } - } - public long Offset { get => _offset; @@ -35,44 +21,30 @@ namespace AssetStudio { throw new IOException($"{nameof(Offset)} is out of stream bound"); } - if (value + _size > _baseStream.Length) - { - _size = _baseStream.Length - value; - } _offset = value; + Seek(0, SeekOrigin.Begin); } } public long AbsolutePosition => _baseStream.Position; public long Remaining => Length - Position; - public override long Length => Size; + public override long Length => _baseStream.Length - _offset; public override long Position { get => _baseStream.Position - _offset; set => Seek(value, SeekOrigin.Begin); } - public SubStream(Stream stream, long offset) + public OffsetStream(Stream stream, long offset) { _baseStream = stream; Offset = offset; - Size = _baseStream.Length - _offset; - Seek(0, SeekOrigin.Begin); - } - - public SubStream(Stream stream, long offset, long size) - { - _baseStream = stream; - - Offset = offset; - Size = size; - Seek(0, SeekOrigin.Begin); } public override long Seek(long offset, SeekOrigin origin) { - if (offset > _size) + if (offset > _baseStream.Length) { throw new IOException("Unable to seek beyond stream bound"); } @@ -81,7 +53,7 @@ namespace AssetStudio { SeekOrigin.Begin => offset + _offset, SeekOrigin.Current => offset + Position, - SeekOrigin.End => offset + _size, + SeekOrigin.End => offset + _baseStream.Length, _ => throw new NotSupportedException() }; @@ -90,7 +62,7 @@ namespace AssetStudio } public override int Read(byte[] buffer, int offset, int count) { - if (offset > _size || Position + count > _size) + if (offset > _baseStream.Length || Position + count > _baseStream.Length) { throw new IOException("Unable to read beyond stream bound"); } diff --git a/AssetStudio/SerializedFile.cs b/AssetStudio/SerializedFile.cs index 151d21e..e3167e5 100644 --- a/AssetStudio/SerializedFile.cs +++ b/AssetStudio/SerializedFile.cs @@ -387,11 +387,6 @@ namespace AssetStudio ObjectsDic.Add(obj.m_PathID, obj); } - public bool IsLoaded(ObjectInfo objInfo) - { - return ObjectsDic.TryGetValue(objInfo.m_PathID, out var obj) && obj.type == (ClassIDType)objInfo.classID; - } - private static int DecodeClassID(int value) { var bytes = BitConverter.GetBytes(value); diff --git a/AssetStudio/SerializedType.cs b/AssetStudio/SerializedType.cs index a4e3db6..0a003e1 100644 --- a/AssetStudio/SerializedType.cs +++ b/AssetStudio/SerializedType.cs @@ -17,5 +17,7 @@ namespace AssetStudio public string m_KlassName; public string m_NameSpace; public string m_AsmName; + + public bool Match(string hash) => Convert.ToHexString(m_OldTypeHash) == hash; } } diff --git a/AssetStudio/XORStream.cs b/AssetStudio/XORStream.cs index 3ad7106..0b7667c 100644 --- a/AssetStudio/XORStream.cs +++ b/AssetStudio/XORStream.cs @@ -2,11 +2,13 @@ namespace AssetStudio { - public class XORStream : SubStream + public class XORStream : OffsetStream { private readonly byte[] _xorpad; private readonly long _offset; + private long Index => AbsolutePosition - _offset; + public XORStream(Stream stream, long offset, byte[] xorpad) : base(stream, offset) { _xorpad = xorpad; @@ -15,11 +17,14 @@ namespace AssetStudio public override int Read(byte[] buffer, int offset, int count) { - var pos = AbsolutePosition - _offset; + var pos = Index; var read = base.Read(buffer, offset, count); - for (int i = 0; i < count; i++) + if (pos >= 0) { - buffer[i] ^= _xorpad[pos++ % _xorpad.Length]; + for (int i = offset; i < count; i++) + { + buffer[i] ^= _xorpad[pos++ % _xorpad.Length]; + } } return read; } diff --git a/AssetStudioCLI/App.config b/AssetStudioCLI/App.config index 934689a..06ca71a 100644 --- a/AssetStudioCLI/App.config +++ b/AssetStudioCLI/App.config @@ -20,5 +20,7 @@ + + \ No newline at end of file diff --git a/AssetStudioCLI/AssetStudioCLI.csproj b/AssetStudioCLI/AssetStudioCLI.csproj index 31a985e..5e0b00f 100644 --- a/AssetStudioCLI/AssetStudioCLI.csproj +++ b/AssetStudioCLI/AssetStudioCLI.csproj @@ -3,9 +3,9 @@ Exe net6.0-windows;net7.0-windows Resources\as.ico - 0.80.30 - 0.80.30 - 0.80.30 + 0.80.65 + 0.80.65 + 0.80.65 Copyright © Razmoth 2022; Copyright © Perfare 2018-2022 ..\AssetStudioGUI\bin diff --git a/AssetStudioCLI/Exporter.cs b/AssetStudioCLI/Exporter.cs index 8a912a8..7b3a1bc 100644 --- a/AssetStudioCLI/Exporter.cs +++ b/AssetStudioCLI/Exporter.cs @@ -334,26 +334,24 @@ namespace AssetStudioCLI public static bool ExportGameObject(AssetItem item, string exportPath, List animationList = null) { var m_GameObject = (GameObject)item.Asset; - ExportGameObject(m_GameObject, exportPath, animationList); - return true; + exportPath = Path.Combine(exportPath, m_GameObject.m_Name) + Path.DirectorySeparatorChar; + return ExportGameObject(m_GameObject, exportPath, animationList); } - public static void ExportGameObject(GameObject gameObject, string exportPath, List animationList = null) + public static bool ExportGameObject(GameObject gameObject, string exportPath, List animationList = null) { var convert = animationList != null ? new ModelConverter(gameObject, Properties.Settings.Default.convertType, Studio.Game, Properties.Settings.Default.collectAnimations, animationList.Select(x => (AnimationClip)x.Asset).ToArray()) : new ModelConverter(gameObject, Properties.Settings.Default.convertType, Studio.Game, Properties.Settings.Default.collectAnimations); + + if (convert.MeshList.Count == 0) + { + Logger.Info($"GameObject {gameObject.m_Name} has no mesh, skipping..."); + return false; + } exportPath = exportPath + FixFileName(gameObject.m_Name) + ".fbx"; ExportFbx(convert, exportPath); - } - - public static void ExportGameObjectMerge(List gameObject, string exportPath, List animationList = null) - { - var rootName = Path.GetFileNameWithoutExtension(exportPath); - var convert = animationList != null - ? new ModelConverter(rootName, gameObject, Properties.Settings.Default.convertType, Studio.Game, Properties.Settings.Default.collectAnimations, animationList.Select(x => (AnimationClip)x.Asset).ToArray()) - : new ModelConverter(rootName, gameObject, Properties.Settings.Default.convertType, Studio.Game, Properties.Settings.Default.collectAnimations); - ExportFbx(convert, exportPath); + return true; } private static void ExportFbx(IImported convert, string exportPath) diff --git a/AssetStudioCLI/Program.cs b/AssetStudioCLI/Program.cs index 012fdfb..70edf67 100644 --- a/AssetStudioCLI/Program.cs +++ b/AssetStudioCLI/Program.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Threading; using AssetStudio; +using AssetStudioCLI.Properties; using static AssetStudioCLI.Studio; namespace AssetStudioCLI @@ -26,47 +27,16 @@ namespace AssetStudioCLI Studio.Game = game; Logger.Default = new ConsoleLogger(); + Shader.Parsable = !Settings.Default.disableShader; + Renderer.Parsable = !Settings.Default.disableRenderer; assetsManager.Silent = o.Silent; assetsManager.Game = game; - - - if (!o.TypeFilter.IsNullOrEmpty()) - { - foreach (var kv in assetsManager.ExportableTypes) - { - assetsManager.ExportableTypes[kv.Key] = o.TypeFilter.Contains(kv.Key); - } - - } - - if (o.Model) - { - foreach (var kv in assetsManager.ExportableTypes) - { - assetsManager.ExportableTypes[kv.Key] = false; - } - - assetsManager.ExportableTypes[ClassIDType.Animator] = true; - assetsManager.ExportableTypes[ClassIDType.GameObject] = true; - assetsManager.ExportableTypes[ClassIDType.Texture2D] = true; - assetsManager.ExportableTypes[ClassIDType.Material] = true; - assetsManager.ExportableTypes[ClassIDType.Renderer] = true; - assetsManager.ExportableTypes[ClassIDType.Mesh] = true; - - ModelOnly = true; - } + ModelOnly = o.Model; if (o.Key != default) { - if (!assetsManager.ExportableTypes[ClassIDType.MiHoYoBinData]) - { - Logger.Warning("Key is set but MiHoYoBinData is skipped, ignoring key..."); - } - else - { - MiHoYoBinData.Encrypted = true; - MiHoYoBinData.Key = o.Key; - } + MiHoYoBinData.Encrypted = true; + MiHoYoBinData.Key = o.Key; } if (o.AIFile != null && game.Type.IsGISubGroup()) @@ -82,36 +52,35 @@ namespace AssetStudioCLI Logger.Info("Scanning for files..."); var files = o.Input.Attributes.HasFlag(FileAttributes.Directory) ? Directory.GetFiles(o.Input.FullName, "*.*", SearchOption.AllDirectories).OrderBy(x => x.Length).ToArray() : new string[] { o.Input.FullName }; files = files.Where(x => FileReader.IsReadable(x, game)).ToArray(); - Logger.Info(string.Format("Found {0} file(s)", files.Length)); + Logger.Info($"Found {files.Length} files"); - if (o.MapOp.HasFlag(MapOpType.Build)) + if (o.MapOp.HasFlag(MapOpType.CABMap)) { - AssetsHelper.BuildMap(files, o.MapName, o.Input.FullName, game); + AssetsHelper.BuildCABMap(files, o.MapName, o.Input.FullName, game); } if (o.MapOp.HasFlag(MapOpType.Load)) { - AssetsHelper.LoadMap(o.MapName); + AssetsHelper.LoadCABMap(o.MapName); assetsManager.ResolveDependencies = true; } - if (o.MapOp.HasFlag(MapOpType.List)) + if (o.MapOp.HasFlag(MapOpType.AssetMap)) { if (files.Length == 1) { throw new Exception("Unable to build AssetMap with input_path as a file !!"); } - var assets = AssetsHelper.BuildAssetMap(files, game, o.NameFilter, o.ContainerFilter); if (!o.Output.Exists) { o.Output.Create(); } var resetEvent = new ManualResetEvent(false); - AssetsHelper.ExportAssetsMap(assets, o.MapName, o.Output.FullName, o.MapType, resetEvent); + AssetsHelper.BuildAssetMap(files, o.MapName, game, o.Output.FullName, o.MapType, resetEvent, o.TypeFilter, o.NameFilter, o.ContainerFilter); resetEvent.WaitOne(); } if (o.MapOp.HasFlag(MapOpType.Both)) { var resetEvent = new ManualResetEvent(false); - AssetsHelper.BuildBoth(files, o.MapName, o.Input.FullName, game, o.Output.FullName, o.MapType, resetEvent, o.NameFilter, o.ContainerFilter); + AssetsHelper.BuildBoth(files, o.MapName, o.Input.FullName, game, o.Output.FullName, o.MapType, resetEvent, o.TypeFilter, o.NameFilter, o.ContainerFilter); resetEvent.WaitOne(); } if (o.MapOp.Equals(MapOpType.None) || o.MapOp.HasFlag(MapOpType.Load)) @@ -122,7 +91,7 @@ namespace AssetStudioCLI assetsManager.LoadFiles(file); if (assetsManager.assetsFileList.Count > 0) { - BuildAssetData(o.NameFilter, o.ContainerFilter, ref i); + BuildAssetData(o.TypeFilter, o.NameFilter, o.ContainerFilter, ref i); ExportAssets(o.Output.FullName, exportableAssets, o.GroupAssetsType); } exportableAssets.Clear(); diff --git a/AssetStudioCLI/Settings.cs b/AssetStudioCLI/Settings.cs index 55aa057..0f2cf7c 100644 --- a/AssetStudioCLI/Settings.cs +++ b/AssetStudioCLI/Settings.cs @@ -62,11 +62,13 @@ namespace AssetStudioCLI.Properties { public bool exportUV0UV1 => AppSettings.Get("exportUV0UV1", false); public bool encrypted => AppSettings.Get("encrypted", true); public byte key => AppSettings.Get("key", (byte)0x93); - public bool skipRenderer => AppSettings.Get("skipRenderer", false); public int selectedGame => AppSettings.Get("selectedGame", 0); public bool enableResolveDependencies => AppSettings.Get("enableResolveDependencies", true); public int selectedCNUnityKey => AppSettings.Get("selectedCNUnityKey", 0); public int selectedAssetMapType => AppSettings.Get("selectedAssetMapType", 0); public bool exportMiHoYoBinData => AppSettings.Get("exportMiHoYoBinData", true); + public bool disableShader => AppSettings.Get("disableShader", false); + public bool disableRenderer => AppSettings.Get("disableRenderer", false); + } } diff --git a/AssetStudioCLI/Studio.cs b/AssetStudioCLI/Studio.cs index 50fabb7..d446051 100644 --- a/AssetStudioCLI/Studio.cs +++ b/AssetStudioCLI/Studio.cs @@ -19,8 +19,8 @@ namespace AssetStudioCLI { None, Load, - Build, - List = 4, + CABMap, + AssetMap = 4, Both = 8, All = Both | Load, } @@ -153,7 +153,7 @@ namespace AssetStudioCLI { int total = 0; Logger.Info($"Decompressing {reader.FileName} ..."); - using var stream = new SubStream(reader.BaseStream, 0); + using var stream = new OffsetStream(reader.BaseStream, 0); do { stream.Offset = stream.AbsolutePosition; @@ -238,7 +238,7 @@ namespace AssetStudioCLI } } - public static void BuildAssetData(Regex[] nameFilters, Regex[] containerFilters, ref int i) + public static void BuildAssetData(ClassIDType[] typeFilters, Regex[] nameFilters, Regex[] containerFilters, ref int i) { var objectAssetItemDic = new Dictionary(); var mihoyoBinDataNames = new List<(PPtr, string)>(); @@ -247,7 +247,7 @@ namespace AssetStudioCLI { foreach (var asset in assetsFile.Objects) { - ProcessAssetData(asset, nameFilters, objectAssetItemDic, mihoyoBinDataNames, containers, ref i); + ProcessAssetData(asset, typeFilters, nameFilters, objectAssetItemDic, mihoyoBinDataNames, containers, ref i); } } foreach ((var pptr, var name) in mihoyoBinDataNames) @@ -269,11 +269,7 @@ namespace AssetStudioCLI { if (pptr.TryGet(out var obj)) { - if (!objectAssetItemDic.TryGetValue(obj, out var item)) - { - ProcessAssetData(obj, nameFilters, objectAssetItemDic, mihoyoBinDataNames, containers, ref i); - item = objectAssetItemDic[obj]; - } + var item = objectAssetItemDic[obj]; if (containerFilters.IsNullOrEmpty() || containerFilters.Any(x => x.IsMatch(container))) { item.Container = container; @@ -292,7 +288,7 @@ namespace AssetStudioCLI } } - public static void ProcessAssetData(Object asset, Regex[] nameFilters, Dictionary objectAssetItemDic, List<(PPtr, string)> mihoyoBinDataNames, List<(PPtr, string)> containers, ref int i) + public static void ProcessAssetData(Object asset, ClassIDType[] typeFilters, Regex[] nameFilters, Dictionary objectAssetItemDic, List<(PPtr, string)> mihoyoBinDataNames, List<(PPtr, string)> containers, ref int i) { var assetItem = new AssetItem(asset); objectAssetItemDic.Add(asset, assetItem); @@ -322,7 +318,7 @@ namespace AssetStudioCLI assetItem.Text = m_VideoClip.m_Name; exportable = !ModelOnly; break; - case Shader m_Shader: + case Shader m_Shader when Shader.Parsable: assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name; exportable = !ModelOnly; break; @@ -351,7 +347,7 @@ namespace AssetStudioCLI { assetItem.Text = m_MonoBehaviour.m_Name; } - exportable = !ModelOnly; + exportable = !ModelOnly && assemblyLoader.Loaded; break; case AssetBundle m_AssetBundle: foreach (var m_Container in m_AssetBundle.m_Container) @@ -390,8 +386,10 @@ namespace AssetStudioCLI { assetItem.Text = assetItem.TypeString + assetItem.UniqueID; } - var isMatchRegex = nameFilters.Length == 0 || nameFilters.Any(x => x.IsMatch(assetItem.Text)); - if (isMatchRegex && exportable) + + var isMatchRegex = nameFilters.IsNullOrEmpty() || nameFilters.Any(x => x.IsMatch(assetItem.Text)); + var isFilteredType = typeFilters.IsNullOrEmpty() || typeFilters.Contains(assetItem.Type); + if (isMatchRegex && isFilteredType && exportable) { exportableAssets.Add(assetItem); } diff --git a/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj b/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj index b4dec47..e958323 100644 --- a/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj +++ b/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj @@ -3,9 +3,9 @@ net6.0;net7.0 true - 0.80.30 - 0.80.30 - 0.80.30 + 0.80.65 + 0.80.65 + 0.80.65 Copyright © Perfare 2018-2022; Copyright © hozuki 2020 embedded diff --git a/AssetStudioGUI/App.config b/AssetStudioGUI/App.config index d8c2e20..acc4f2a 100644 --- a/AssetStudioGUI/App.config +++ b/AssetStudioGUI/App.config @@ -94,8 +94,11 @@ False - - {"GameObject":true,"Material":true,"Texture2D":true,"Mesh":true,"Renderer":true,"Shader":true,"TextAsset":true,"AnimationClip":true,"Font":true,"Sprite":true,"Animator":true,"MiHoYoBinData":true,"AssetBundle":true} + + False + + + False diff --git a/AssetStudioGUI/AssetStudioGUI.csproj b/AssetStudioGUI/AssetStudioGUI.csproj index cb23992..dd8f311 100644 --- a/AssetStudioGUI/AssetStudioGUI.csproj +++ b/AssetStudioGUI/AssetStudioGUI.csproj @@ -5,9 +5,9 @@ net6.0-windows;net7.0-windows true Resources\as.ico - 0.80.30 - 0.80.30 - 0.80.30 + 0.80.65 + 0.80.65 + 0.80.65 Copyright © Razmoth 2022; Copyright © Perfare 2018-2022 embedded diff --git a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs index 6c9d998..e79639e 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs @@ -92,16 +92,18 @@ namespace AssetStudioGUI enableConsole = new System.Windows.Forms.ToolStripMenuItem(); clearConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); miscToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + MapNameComboBox = new System.Windows.Forms.ToolStripComboBox(); + buildBothToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + clearMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator(); + assetMapNameTextBox = new System.Windows.Forms.ToolStripTextBox(); + buildAssetMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + assetMapTypeComboBox = new System.Windows.Forms.ToolStripComboBox(); + toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator(); + loadAIToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); assetHelpersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); MapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - MapNameComboBox = new System.Windows.Forms.ToolStripComboBox(); - toolStripMenuItem20 = new System.Windows.Forms.ToolStripMenuItem(); - toolStripMenuItem21 = new System.Windows.Forms.ToolStripMenuItem(); assetMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - assetMapNameTextBox = new System.Windows.Forms.ToolStripTextBox(); - toolStripMenuItem22 = new System.Windows.Forms.ToolStripMenuItem(); - assetMapTypeComboBox = new System.Windows.Forms.ToolStripComboBox(); - loadAIToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator(); splitContainer1 = new System.Windows.Forms.SplitContainer(); tabControl1 = new System.Windows.Forms.TabControl(); @@ -153,6 +155,7 @@ namespace AssetStudioGUI exportAnimatorwithselectedAnimationClipMenuItem = new System.Windows.Forms.ToolStripMenuItem(); goToSceneHierarchyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); showOriginalFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + buildMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); menuStrip1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit(); splitContainer1.Panel1.SuspendLayout(); @@ -597,25 +600,11 @@ namespace AssetStudioGUI // // miscToolStripMenuItem // - miscToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { assetHelpersToolStripMenuItem, loadAIToolStripMenuItem }); + miscToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { MapNameComboBox, buildMapToolStripMenuItem, buildBothToolStripMenuItem, clearMapToolStripMenuItem, toolStripSeparator7, assetMapNameTextBox, buildAssetMapToolStripMenuItem, assetMapTypeComboBox, toolStripSeparator8, loadAIToolStripMenuItem }); miscToolStripMenuItem.Name = "miscToolStripMenuItem"; miscToolStripMenuItem.Size = new System.Drawing.Size(47, 20); miscToolStripMenuItem.Text = "Misc."; - // - // assetHelpersToolStripMenuItem - // - assetHelpersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { MapToolStripMenuItem, assetMapToolStripMenuItem }); - assetHelpersToolStripMenuItem.Name = "assetHelpersToolStripMenuItem"; - assetHelpersToolStripMenuItem.Size = new System.Drawing.Size(145, 22); - assetHelpersToolStripMenuItem.Text = "Asset Helpers"; - assetHelpersToolStripMenuItem.DropDownOpening += assetHelpersToolStripMenuItem_DropDownOpening; - // - // MapToolStripMenuItem - // - MapToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { MapNameComboBox, toolStripMenuItem20, toolStripMenuItem21 }); - MapToolStripMenuItem.Name = "MapToolStripMenuItem"; - MapToolStripMenuItem.Size = new System.Drawing.Size(126, 22); - MapToolStripMenuItem.Text = "Map"; + miscToolStripMenuItem.DropDownOpening += miscToolStripMenuItem_DropDownOpening; // // MapNameComboBox // @@ -623,26 +612,31 @@ namespace AssetStudioGUI MapNameComboBox.Size = new System.Drawing.Size(121, 23); MapNameComboBox.ToolTipText = "Enter name of Map here"; // - // toolStripMenuItem20 + // buildMapToolStripMenuItem // - toolStripMenuItem20.Name = "toolStripMenuItem20"; - toolStripMenuItem20.Size = new System.Drawing.Size(181, 22); - toolStripMenuItem20.Text = "Build Map"; - toolStripMenuItem20.Click += toolStripMenuItem20_Click; + buildMapToolStripMenuItem.Name = "buildMapToolStripMenuItem"; + buildMapToolStripMenuItem.Size = new System.Drawing.Size(181, 22); + buildMapToolStripMenuItem.Text = "Build Map"; + buildMapToolStripMenuItem.Click += buildMapToolStripMenuItem_Click; // - // toolStripMenuItem21 + // buildBothToolStripMenuItem // - toolStripMenuItem21.Name = "toolStripMenuItem21"; - toolStripMenuItem21.Size = new System.Drawing.Size(181, 22); - toolStripMenuItem21.Text = "Clear Map"; - toolStripMenuItem21.Click += toolStripMenuItem21_Click; + buildBothToolStripMenuItem.Name = "buildBothToolStripMenuItem"; + buildBothToolStripMenuItem.Size = new System.Drawing.Size(181, 22); + buildBothToolStripMenuItem.Text = "Build Both"; + buildBothToolStripMenuItem.Click += buildBothToolStripMenuItem_Click; // - // assetMapToolStripMenuItem + // clearMapToolStripMenuItem // - assetMapToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { assetMapNameTextBox, toolStripMenuItem22, assetMapTypeComboBox }); - assetMapToolStripMenuItem.Name = "assetMapToolStripMenuItem"; - assetMapToolStripMenuItem.Size = new System.Drawing.Size(126, 22); - assetMapToolStripMenuItem.Text = "AssetMap"; + clearMapToolStripMenuItem.Name = "clearMapToolStripMenuItem"; + clearMapToolStripMenuItem.Size = new System.Drawing.Size(181, 22); + clearMapToolStripMenuItem.Text = "Clear Map"; + clearMapToolStripMenuItem.Click += clearMapToolStripMenuItem_Click; + // + // toolStripSeparator7 + // + toolStripSeparator7.Name = "toolStripSeparator7"; + toolStripSeparator7.Size = new System.Drawing.Size(178, 6); // // assetMapNameTextBox // @@ -650,12 +644,12 @@ namespace AssetStudioGUI assetMapNameTextBox.Size = new System.Drawing.Size(100, 23); assetMapNameTextBox.ToolTipText = "Enter name of AssetMap here"; // - // toolStripMenuItem22 + // buildAssetMapToolStripMenuItem // - toolStripMenuItem22.Name = "toolStripMenuItem22"; - toolStripMenuItem22.Size = new System.Drawing.Size(181, 22); - toolStripMenuItem22.Text = "Build AssetMap"; - toolStripMenuItem22.Click += toolStripMenuItem22_Click; + buildAssetMapToolStripMenuItem.Name = "buildAssetMapToolStripMenuItem"; + buildAssetMapToolStripMenuItem.Size = new System.Drawing.Size(181, 22); + buildAssetMapToolStripMenuItem.Text = "Build AssetMap"; + buildAssetMapToolStripMenuItem.Click += buildAssetMapToolStripMenuItem_Click; // // assetMapTypeComboBox // @@ -664,13 +658,33 @@ namespace AssetStudioGUI assetMapTypeComboBox.Size = new System.Drawing.Size(121, 23); assetMapTypeComboBox.SelectedIndexChanged += assetMapTypeComboBox_SelectedIndexChanged; // + // toolStripSeparator8 + // + toolStripSeparator8.Name = "toolStripSeparator8"; + toolStripSeparator8.Size = new System.Drawing.Size(178, 6); + // // loadAIToolStripMenuItem // loadAIToolStripMenuItem.Name = "loadAIToolStripMenuItem"; - loadAIToolStripMenuItem.Size = new System.Drawing.Size(145, 22); + loadAIToolStripMenuItem.Size = new System.Drawing.Size(181, 22); loadAIToolStripMenuItem.Text = "Load AI"; loadAIToolStripMenuItem.Click += loadAIToolStripMenuItem_Click; // + // assetHelpersToolStripMenuItem + // + assetHelpersToolStripMenuItem.Name = "assetHelpersToolStripMenuItem"; + assetHelpersToolStripMenuItem.Size = new System.Drawing.Size(32, 19); + // + // MapToolStripMenuItem + // + MapToolStripMenuItem.Name = "MapToolStripMenuItem"; + MapToolStripMenuItem.Size = new System.Drawing.Size(32, 19); + // + // assetMapToolStripMenuItem + // + assetMapToolStripMenuItem.Name = "assetMapToolStripMenuItem"; + assetMapToolStripMenuItem.Size = new System.Drawing.Size(32, 19); + // // toolStripSeparator5 // toolStripSeparator5.Name = "toolStripSeparator5"; @@ -1360,20 +1374,23 @@ namespace AssetStudioGUI private System.Windows.Forms.ToolStripMenuItem enableConsole; private System.Windows.Forms.ToolStripMenuItem miscToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem assetHelpersToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem20; - private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem22; + private System.Windows.Forms.ToolStripMenuItem buildBothToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem buildAssetMapToolStripMenuItem; private System.Windows.Forms.ToolStripComboBox MapNameComboBox; private System.Windows.Forms.ToolStripSeparator toolStripSeparator5; private System.Windows.Forms.ToolStripSeparator toolStripSeparator6; private System.Windows.Forms.ToolStripMenuItem resetToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem abortStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem21; + private System.Windows.Forms.ToolStripMenuItem clearMapToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem MapToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem assetMapToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem loadAIToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem clearConsoleToolStripMenuItem; private System.Windows.Forms.ToolStripTextBox assetMapNameTextBox; private System.Windows.Forms.ToolStripComboBox assetMapTypeComboBox; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator7; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator8; + private System.Windows.Forms.ToolStripMenuItem buildMapToolStripMenuItem; } } diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index 5648ffc..f1d5d11 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -110,13 +110,8 @@ namespace AssetStudioGUI assetsManager.ResolveDependencies = Properties.Settings.Default.enableResolveDependencies; MiHoYoBinData.Encrypted = Properties.Settings.Default.encrypted; MiHoYoBinData.Key = Properties.Settings.Default.key; - - var types = JsonConvert.DeserializeObject>(Properties.Settings.Default.exportableTypes); - foreach (var exportable in types) - { - if (assetsManager.ExportableTypes.ContainsKey(exportable.Key)) - assetsManager.ExportableTypes[exportable.Key] = exportable.Value; - } + Renderer.Parsable = !Properties.Settings.Default.disableRenderer; + Shader.Parsable = !Properties.Settings.Default.disableShader; } private void InitializeLogger() @@ -515,12 +510,6 @@ namespace AssetStudioGUI { var exportOpt = new ExportOptions(); exportOpt.ShowDialog(this); - var types = JsonConvert.DeserializeObject>(Properties.Settings.Default.exportableTypes); - foreach (var exportable in types) - { - if (assetsManager.ExportableTypes.ContainsKey(exportable.Key)) - assetsManager.ExportableTypes[exportable.Key] = exportable.Value; - } } private void assetListView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) @@ -763,6 +752,9 @@ namespace AssetStudioGUI { switch (assetItem.Asset) { + case GameObject m_GameObject: + PreviewGameObject(m_GameObject); + break; case Texture2D m_Texture2D: PreviewTexture2D(assetItem, m_Texture2D); break; @@ -791,8 +783,9 @@ namespace AssetStudioGUI case Sprite m_Sprite: PreviewSprite(assetItem, m_Sprite); break; - case Animator _: - StatusStripUpdate("Can be exported to FBX file."); + case Animator m_Animator: + //StatusStripUpdate("Can be exported to FBX file."); + PreviewAnimator(m_Animator); break; case AnimationClip _: StatusStripUpdate("Can be exported with Animator or Objects"); @@ -1237,6 +1230,163 @@ namespace AssetStudioGUI } } + private void PreviewGameObject(GameObject m_GameObject) + { + var model = new ModelConverter(m_GameObject, Properties.Settings.Default.convertType, Studio.Game, false, Array.Empty()); + if (model.MeshList.Count > 0) + { + viewMatrixData = Matrix4.CreateRotationY(-(float)Math.PI / 4) * Matrix4.CreateRotationX(-(float)Math.PI / 6); + #region Vertices + vertexData = model.MeshList.SelectMany(x => x.VertexList).Select(x => new Vector3(x.Vertex.X, x.Vertex.Y, x.Vertex.Z)).ToArray(); + // Calculate Bounding + Vector3 min = vertexData.Aggregate(Vector3.ComponentMin); + Vector3 max = vertexData.Aggregate(Vector3.ComponentMax); + + // Calculate modelMatrix + Vector3 dist = max - min; + Vector3 offset = (max - min) / 2; + float d = Math.Max(1e-5f, dist.Length); + modelMatrixData = Matrix4.CreateTranslation(-offset) * Matrix4.CreateScale(2f / d); + #endregion + #region Indicies + int meshOffset = 0; + var indices = new List(); + foreach (var mesh in model.MeshList) + { + foreach (var submesh in mesh.SubmeshList) + { + foreach (var face in submesh.FaceList) + { + foreach (var index in face.VertexIndices) + { + indices.Add(submesh.BaseVertex + index + meshOffset); + } + } + } + meshOffset += mesh.VertexList.Count; + } + indiceData = indices.ToArray(); + #endregion + #region Normals + normalData = model.MeshList.SelectMany(x => x.VertexList).Select(x => new Vector3(x.Normal.X, x.Normal.Y, x.Normal.Z)).ToArray(); + // calculate normal by ourself + normal2Data = new Vector3[vertexData.Length]; + int[] normalCalculatedCount = new int[vertexData.Length]; + Array.Fill(normal2Data, Vector3.Zero); + Array.Fill(normalCalculatedCount, 0); + for (int j = 0; j < indiceData.Length; j += 3) + { + Vector3 dir1 = vertexData[indiceData[j + 1]] - vertexData[indiceData[j]]; + Vector3 dir2 = vertexData[indiceData[j + 2]] - vertexData[indiceData[j]]; + Vector3 normal = Vector3.Cross(dir1, dir2); + normal.Normalize(); + for (int k = 0; k < 3; k++) + { + normal2Data[indiceData[j + k]] += normal; + normalCalculatedCount[indiceData[j + k]]++; + } + } + for (int j = 0; j < vertexData.Length; j++) + { + if (normalCalculatedCount[j] == 0) + normal2Data[j] = new Vector3(0, 1, 0); + else + normal2Data[j] /= normalCalculatedCount[j]; + } + #endregion + #region Colors + colorData = model.MeshList.SelectMany(x => x.VertexList).Select(x => new Vector4(x.Color.R, x.Color.G, x.Color.B, x.Color.A)).ToArray(); + #endregion + glControl.Visible = true; + CreateVAO(); + StatusStripUpdate("Using OpenGL Version: " + GL.GetString(StringName.Version) + "\n" + + "'Mouse Left'=Rotate | 'Mouse Right'=Move | 'Mouse Wheel'=Zoom \n" + + "'Ctrl W'=Wireframe | 'Ctrl S'=Shade | 'Ctrl N'=ReNormal "); + } + else + { + StatusStripUpdate("Unable to preview this model"); + } + } + private void PreviewAnimator(Animator m_Animator) + { + var model = new ModelConverter(m_Animator, Properties.Settings.Default.convertType, Studio.Game, false, Array.Empty()); + if (model.MeshList.Count > 0) + { + viewMatrixData = Matrix4.CreateRotationY(-(float)Math.PI / 4) * Matrix4.CreateRotationX(-(float)Math.PI / 6); + #region Vertices + vertexData = model.MeshList.SelectMany(x => x.VertexList).Select(x => new Vector3(x.Vertex.X, x.Vertex.Y, x.Vertex.Z)).ToArray(); + // Calculate Bounding + Vector3 min = vertexData.Aggregate(Vector3.ComponentMin); + Vector3 max = vertexData.Aggregate(Vector3.ComponentMax); + + // Calculate modelMatrix + Vector3 dist = max - min; + Vector3 offset = (max - min) / 2; + float d = Math.Max(1e-5f, dist.Length); + modelMatrixData = Matrix4.CreateTranslation(-offset) * Matrix4.CreateScale(2f / d); + #endregion + #region Indicies + int meshOffset = 0; + var indices = new List(); + foreach (var mesh in model.MeshList) + { + foreach (var submesh in mesh.SubmeshList) + { + foreach (var face in submesh.FaceList) + { + foreach (var index in face.VertexIndices) + { + indices.Add(submesh.BaseVertex + index + meshOffset); + } + } + } + meshOffset += mesh.VertexList.Count; + } + indiceData = indices.ToArray(); + #endregion + #region Normals + normalData = model.MeshList.SelectMany(x => x.VertexList).Select(x => new Vector3(x.Normal.X, x.Normal.Y, x.Normal.Z)).ToArray(); + // calculate normal by ourself + normal2Data = new Vector3[vertexData.Length]; + int[] normalCalculatedCount = new int[vertexData.Length]; + Array.Fill(normal2Data, Vector3.Zero); + Array.Fill(normalCalculatedCount, 0); + for (int j = 0; j < indiceData.Length; j += 3) + { + Vector3 dir1 = vertexData[indiceData[j + 1]] - vertexData[indiceData[j]]; + Vector3 dir2 = vertexData[indiceData[j + 2]] - vertexData[indiceData[j]]; + Vector3 normal = Vector3.Cross(dir1, dir2); + normal.Normalize(); + for (int k = 0; k < 3; k++) + { + normal2Data[indiceData[j + k]] += normal; + normalCalculatedCount[indiceData[j + k]]++; + } + } + for (int j = 0; j < vertexData.Length; j++) + { + if (normalCalculatedCount[j] == 0) + normal2Data[j] = new Vector3(0, 1, 0); + else + normal2Data[j] /= normalCalculatedCount[j]; + } + #endregion + #region Colors + colorData = model.MeshList.SelectMany(x => x.VertexList).Select(x => new Vector4(x.Color.R, x.Color.G, x.Color.B, x.Color.A)).ToArray(); + #endregion + glControl.Visible = true; + CreateVAO(); + StatusStripUpdate("Using OpenGL Version: " + GL.GetString(StringName.Version) + "\n" + + "'Mouse Left'=Rotate | 'Mouse Right'=Move | 'Mouse Wheel'=Zoom \n" + + "'Ctrl W'=Wireframe | 'Ctrl S'=Shade | 'Ctrl N'=ReNormal "); + } + else + { + StatusStripUpdate("Unable to preview this model"); + } + } + private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite) { var image = m_Sprite.GetImage(); @@ -1714,9 +1864,9 @@ namespace AssetStudioGUI } } - private void assetHelpersToolStripMenuItem_DropDownOpening(object sender, EventArgs e) + private void miscToolStripMenuItem_DropDownOpening(object sender, EventArgs e) { - if (assetHelpersToolStripMenuItem.Enabled) + if (miscToolStripMenuItem.Enabled) { MapNameComboBox.Items.Clear(); MapNameComboBox.Items.AddRange(AssetsHelper.GetMaps()); @@ -1831,10 +1981,11 @@ namespace AssetStudioGUI Properties.Settings.Default.selectedGame = specifyGame.SelectedIndex; Properties.Settings.Default.Save(); + ResetForm(); + Studio.Game = GameManager.GetGame(Properties.Settings.Default.selectedGame); Logger.Info($"Target Game is {Studio.Game.Name}"); - ResetForm(); assetsManager.SpecifyUnityVersion = specifyUnityVersion.Text; assetsManager.Game = Studio.Game; } @@ -1844,17 +1995,18 @@ namespace AssetStudioGUI miscToolStripMenuItem.DropDown.Visible = false; InvokeUpdate(miscToolStripMenuItem, false); - var name = MapNameComboBox.SelectedItem.ToString(); - await Task.Run(() => AssetsHelper.LoadMap(name)); - ResetForm(); + + var name = MapNameComboBox.SelectedItem.ToString(); + await Task.Run(() => AssetsHelper.LoadCABMap(name)); + assetsManager.SpecifyUnityVersion = specifyUnityVersion.Text; assetsManager.Game = Studio.Game; InvokeUpdate(miscToolStripMenuItem, true); } - private async void toolStripMenuItem20_Click(object sender, EventArgs e) + private async void buildMapToolStripMenuItem_Click(object sender, EventArgs e) { miscToolStripMenuItem.DropDown.Visible = false; InvokeUpdate(miscToolStripMenuItem, false); @@ -1903,12 +2055,83 @@ namespace AssetStudioGUI var files = Directory.GetFiles(openFolderDialog.Folder, "*.*", SearchOption.AllDirectories).ToArray(); files = files.Where(x => FileReader.IsReadable(x, Studio.Game)).ToArray(); Logger.Info($"Found {files.Length} files"); - await Task.Run(() => AssetsHelper.BuildMap(files, name, openFolderDialog.Folder, Studio.Game)); + await Task.Run(() => AssetsHelper.BuildCABMap(files, name, openFolderDialog.Folder, Studio.Game)); } InvokeUpdate(miscToolStripMenuItem, true); } - private void toolStripMenuItem21_Click(object sender, EventArgs e) + private async void buildBothToolStripMenuItem_Click(object sender, EventArgs e) + { + miscToolStripMenuItem.DropDown.Visible = false; + InvokeUpdate(miscToolStripMenuItem, false); + + var input = MapNameComboBox.Text; + var selectedText = MapNameComboBox.SelectedText; + var exportListType = (ExportListType)assetMapTypeComboBox.SelectedItem; + var name = ""; + + if (!string.IsNullOrEmpty(selectedText)) + { + name = selectedText; + } + else if (!string.IsNullOrEmpty(input)) + { + if (input.IndexOfAny(Path.GetInvalidFileNameChars()) != -1) + { + Logger.Warning("Name has invalid characters !!"); + InvokeUpdate(miscToolStripMenuItem, true); + return; + } + + name = input; + } + else + { + Logger.Error("Map name is empty, please enter any name in ComboBox above"); + InvokeUpdate(miscToolStripMenuItem, true); + return; + } + + if (File.Exists(Path.Combine(AssetsHelper.MapName, $"{name}.bin"))) + { + var acceptOverride = MessageBox.Show("Map already exist, Do you want to override it ?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + if (acceptOverride != DialogResult.Yes) + { + InvokeUpdate(miscToolStripMenuItem, true); + return; + } + } + + var openFolderDialog = new OpenFolderDialog(); + openFolderDialog.Title = "Select Game Folder"; + if (openFolderDialog.ShowDialog(this) == DialogResult.OK) + { + var saveFolderDialog = new OpenFolderDialog(); + saveFolderDialog.InitialFolder = saveDirectoryBackup; + saveFolderDialog.Title = "Select Output Folder"; + if (saveFolderDialog.ShowDialog(this) == DialogResult.OK) + { + if (File.Exists(Path.Combine(saveFolderDialog.Folder, $"{name}{exportListType.GetExtension()}"))) + { + var acceptOverride = MessageBox.Show("AssetMap already exist, Do you want to override it ?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + if (acceptOverride != DialogResult.Yes) + { + InvokeUpdate(miscToolStripMenuItem, true); + return; + } + } + saveDirectoryBackup = saveFolderDialog.Folder; + Logger.Info("Scanning for files..."); + var files = Directory.GetFiles(openFolderDialog.Folder, "*.*", SearchOption.AllDirectories).ToArray(); + files = files.Where(x => FileReader.IsReadable(x, Studio.Game)).ToArray(); + Logger.Info($"Found {files.Length} files"); + await Task.Run(() => AssetsHelper.BuildBoth(files, name, openFolderDialog.Folder, Studio.Game, saveFolderDialog.Folder, exportListType)); + } + } + InvokeUpdate(miscToolStripMenuItem, true); + } + + private void clearMapToolStripMenuItem_Click(object sender, EventArgs e) { miscToolStripMenuItem.DropDown.Visible = false; InvokeUpdate(miscToolStripMenuItem, false); @@ -1993,7 +2216,7 @@ namespace AssetStudioGUI Console.Clear(); } - private async void toolStripMenuItem22_Click(object sender, EventArgs e) + private async void buildAssetMapToolStripMenuItem_Click(object sender, EventArgs e) { miscToolStripMenuItem.DropDown.Visible = false; InvokeUpdate(miscToolStripMenuItem, false); @@ -2038,8 +2261,7 @@ namespace AssetStudioGUI } } saveDirectoryBackup = saveFolderDialog.Folder; - var toExportAssets = await Task.Run(() => AssetsHelper.BuildAssetMap(files, Studio.Game)); - AssetsHelper.ExportAssetsMap(toExportAssets, name, saveFolderDialog.Folder, exportListType); + await Task.Run(() => AssetsHelper.BuildAssetMap(files, name, Studio.Game, saveFolderDialog.Folder, exportListType)); } } InvokeUpdate(miscToolStripMenuItem, true); diff --git a/AssetStudioGUI/AssetStudioGUIForm.resx b/AssetStudioGUI/AssetStudioGUIForm.resx index 3d8c540..ee3dd05 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.resx +++ b/AssetStudioGUI/AssetStudioGUIForm.resx @@ -60,6 +60,9 @@ 312, 17 + + 432, 17 + abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWYZ 1234567890.:,;'\"(!?)+-*/= @@ -78,9 +81,6 @@ The quick brown fox jumps over the lazy dog. 1234567890 The quick brown fox jumps over the lazy dog. 1234567890 - - 432, 17 - 553, 17 diff --git a/AssetStudioGUI/ExportOptions.Designer.cs b/AssetStudioGUI/ExportOptions.Designer.cs index 4f89738..78a43e7 100644 --- a/AssetStudioGUI/ExportOptions.Designer.cs +++ b/AssetStudioGUI/ExportOptions.Designer.cs @@ -71,12 +71,12 @@ namespace AssetStudioGUI key = new System.Windows.Forms.NumericUpDown(); keyToolTip = new System.Windows.Forms.ToolTip(components); groupBox4 = new System.Windows.Forms.GroupBox(); - label7 = new System.Windows.Forms.Label(); - exportableTypes = new System.Windows.Forms.CheckedListBox(); skipContainer = new System.Windows.Forms.CheckBox(); enableResolveDependencies = new System.Windows.Forms.CheckBox(); resolveToolTip = new System.Windows.Forms.ToolTip(components); skipToolTip = new System.Windows.Forms.ToolTip(components); + disableRenderer = new System.Windows.Forms.CheckBox(); + disableShader = new System.Windows.Forms.CheckBox(); groupBox1.SuspendLayout(); panel1.SuspendLayout(); groupBox2.SuspendLayout(); @@ -537,8 +537,8 @@ namespace AssetStudioGUI // groupBox4 // groupBox4.AutoSize = true; - groupBox4.Controls.Add(label7); - groupBox4.Controls.Add(exportableTypes); + groupBox4.Controls.Add(disableShader); + groupBox4.Controls.Add(disableRenderer); groupBox4.Controls.Add(skipContainer); groupBox4.Controls.Add(key); groupBox4.Controls.Add(encrypted); @@ -552,24 +552,6 @@ namespace AssetStudioGUI groupBox4.TabStop = false; groupBox4.Text = "Options"; // - // label7 - // - label7.AutoSize = true; - label7.Location = new System.Drawing.Point(7, 94); - label7.Name = "label7"; - label7.Size = new System.Drawing.Size(95, 15); - label7.TabIndex = 16; - label7.Text = "Exportable Types"; - // - // exportableTypes - // - exportableTypes.CheckOnClick = true; - exportableTypes.FormattingEnabled = true; - exportableTypes.Location = new System.Drawing.Point(102, 94); - exportableTypes.Name = "exportableTypes"; - exportableTypes.Size = new System.Drawing.Size(152, 58); - exportableTypes.TabIndex = 15; - // // skipContainer // skipContainer.AutoSize = true; @@ -598,6 +580,30 @@ namespace AssetStudioGUI resolveToolTip.SetToolTip(enableResolveDependencies, "Toggle the behaviour of loading assets.\r\nDisable to load file(s) without its dependencies."); enableResolveDependencies.UseVisualStyleBackColor = true; // + // disableRenderer + // + disableRenderer.AutoSize = true; + disableRenderer.Location = new System.Drawing.Point(8, 96); + disableRenderer.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + disableRenderer.Name = "disableRenderer"; + disableRenderer.Size = new System.Drawing.Size(114, 19); + disableRenderer.TabIndex = 15; + 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; + // + // disableShader + // + disableShader.AutoSize = true; + disableShader.Location = new System.Drawing.Point(8, 121); + disableShader.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + disableShader.Name = "disableShader"; + disableShader.Size = new System.Drawing.Size(103, 19); + disableShader.TabIndex = 16; + 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; + // // ExportOptions // AcceptButton = OKbutton; @@ -679,7 +685,7 @@ namespace AssetStudioGUI private System.Windows.Forms.CheckBox skipContainer; private System.Windows.Forms.ToolTip resolveToolTip; private System.Windows.Forms.ToolTip skipToolTip; - private System.Windows.Forms.CheckedListBox exportableTypes; - private System.Windows.Forms.Label label7; + private System.Windows.Forms.CheckBox disableShader; + private System.Windows.Forms.CheckBox disableRenderer; } } \ No newline at end of file diff --git a/AssetStudioGUI/ExportOptions.cs b/AssetStudioGUI/ExportOptions.cs index 6195c61..c23476e 100644 --- a/AssetStudioGUI/ExportOptions.cs +++ b/AssetStudioGUI/ExportOptions.cs @@ -42,27 +42,12 @@ namespace AssetStudioGUI collectAnimations.Checked = Properties.Settings.Default.collectAnimations; encrypted.Checked = Properties.Settings.Default.encrypted; key.Value = Properties.Settings.Default.key; - - exportableTypes.Items.AddRange(Studio.assetsManager.ExportableTypes.Keys.Select(x => x.ToString()).ToArray()); - var types = JsonConvert.DeserializeObject>(Properties.Settings.Default.exportableTypes); - foreach (var exportable in types) - { - var idx = exportableTypes.Items.IndexOf(exportable.Key.ToString()); - if (idx != -1) - exportableTypes.SetItemChecked(idx, exportable.Value); - } + disableRenderer.Checked = Properties.Settings.Default.disableRenderer; + disableShader.Checked = Properties.Settings.Default.disableShader; } private void OKbutton_Click(object sender, EventArgs e) { - var types = new Dictionary(); - for (int i = 0; i < exportableTypes.Items.Count; i++) - { - var type = Enum.Parse(exportableTypes.Items[i].ToString()); - var state = exportableTypes.GetItemChecked(i); - types.Add(type, state); - } - Properties.Settings.Default.exportableTypes = JsonConvert.SerializeObject(types); Properties.Settings.Default.assetGroupOption = assetGroupOptions.SelectedIndex; Properties.Settings.Default.restoreExtensionName = restoreExtensionName.Checked; Properties.Settings.Default.convertTexture = converttexture.Checked; @@ -92,9 +77,13 @@ namespace AssetStudioGUI Properties.Settings.Default.collectAnimations = collectAnimations.Checked; Properties.Settings.Default.encrypted = encrypted.Checked; Properties.Settings.Default.key = (byte)key.Value; + Properties.Settings.Default.disableRenderer = disableRenderer.Checked; + Properties.Settings.Default.disableShader = disableShader.Checked; Properties.Settings.Default.Save(); MiHoYoBinData.Key = (byte)key.Value; MiHoYoBinData.Encrypted = encrypted.Checked; + Renderer.Parsable = !Properties.Settings.Default.disableRenderer; + Shader.Parsable = !Properties.Settings.Default.disableShader; DialogResult = DialogResult.OK; Close(); } diff --git a/AssetStudioGUI/ExportOptions.resx b/AssetStudioGUI/ExportOptions.resx index b787a62..485af80 100644 --- a/AssetStudioGUI/ExportOptions.resx +++ b/AssetStudioGUI/ExportOptions.resx @@ -60,6 +60,12 @@ 17, 17 + + 17, 17 + + + 162, 17 + 162, 17 @@ -69,6 +75,9 @@ 273, 17 + + 273, 17 + 57 diff --git a/AssetStudioGUI/Properties/Settings.Designer.cs b/AssetStudioGUI/Properties/Settings.Designer.cs index 455994a..ef464bb 100644 --- a/AssetStudioGUI/Properties/Settings.Designer.cs +++ b/AssetStudioGUI/Properties/Settings.Designer.cs @@ -385,15 +385,25 @@ namespace AssetStudioGUI.Properties { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("{\"GameObject\":true,\"Material\":true,\"Texture2D\":true,\"Mesh\":true,\"Renderer\":true,\"" + - "Shader\":true,\"TextAsset\":true,\"AnimationClip\":true,\"Font\":true,\"Sprite\":true,\"An" + - "imator\":true,\"MiHoYoBinData\":true,\"AssetBundle\":true}")] - public string exportableTypes { + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool disableShader { get { - return ((string)(this["exportableTypes"])); + return ((bool)(this["disableShader"])); } set { - this["exportableTypes"] = value; + this["disableShader"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool disableRenderer { + get { + return ((bool)(this["disableRenderer"])); + } + set { + this["disableRenderer"] = value; } } } diff --git a/AssetStudioGUI/Properties/Settings.settings b/AssetStudioGUI/Properties/Settings.settings index bf4a6ef..4495fcb 100644 --- a/AssetStudioGUI/Properties/Settings.settings +++ b/AssetStudioGUI/Properties/Settings.settings @@ -92,8 +92,11 @@ False - - {"GameObject":true,"Material":true,"Texture2D":true,"Mesh":true,"Renderer":true,"Shader":true,"TextAsset":true,"AnimationClip":true,"Font":true,"Sprite":true,"Animator":true,"MiHoYoBinData":true,"AssetBundle":true} + + False + + + False \ No newline at end of file diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs index dd5b650..a1b6b5e 100644 --- a/AssetStudioGUI/Studio.cs +++ b/AssetStudioGUI/Studio.cs @@ -154,7 +154,7 @@ namespace AssetStudioGUI { int total = 0; StatusStripUpdate($"Decompressing {reader.FileName} ..."); - using var stream = new SubStream(reader.BaseStream, 0); + using var stream = new OffsetStream(reader.BaseStream, 0); do { stream.Offset = stream.AbsolutePosition; @@ -224,17 +224,17 @@ namespace AssetStudioGUI if (uint.TryParse(name, out var id)) { var path = ResourceIndex.GetContainer(id, last); - if (!string.IsNullOrEmpty(path)) - { - asset.Container = path; - if (asset.Type == ClassIDType.MiHoYoBinData) + if (!string.IsNullOrEmpty(path)) { - asset.Text = Path.GetFileNameWithoutExtension(path); + asset.Container = path; + if (asset.Type == ClassIDType.MiHoYoBinData) + { + asset.Text = Path.GetFileNameWithoutExtension(path); + } } } } } - } Logger.Info("Updated !!"); } } @@ -347,10 +347,10 @@ namespace AssetStudioGUI exportable = true; break; case ResourceManager m_ResourceManager: - foreach (var m_Container in m_ResourceManager.m_Container) - { - containers.Add((m_Container.Value, m_Container.Key)); - } + foreach (var m_Container in m_ResourceManager.m_Container) + { + containers.Add((m_Container.Value, m_Container.Key)); + } break; case NamedObject m_NamedObject: assetItem.Text = m_NamedObject.m_Name; @@ -369,6 +369,11 @@ namespace AssetStudioGUI } foreach((var pptr, var name) in mihoyoBinDataNames) { + if (assetsManager.tokenSource.IsCancellationRequested) + { + Logger.Info("Processing asset namnes has been cancelled !!"); + return (string.Empty, Array.Empty().ToList()); + } if (pptr.TryGet(out var obj)) { var assetItem = objectAssetItemDic[obj]; @@ -384,6 +389,11 @@ namespace AssetStudioGUI { foreach ((var pptr, var container) in containers) { + if (assetsManager.tokenSource.IsCancellationRequested) + { + Logger.Info("Processing containers been cancelled !!"); + return (string.Empty, Array.Empty().ToList()); + } if (pptr.TryGet(out var obj)) { objectAssetItemDic[obj].Container = container; @@ -397,6 +407,11 @@ namespace AssetStudioGUI } foreach (var tmp in exportableAssets) { + if (assetsManager.tokenSource.IsCancellationRequested) + { + Logger.Info("Processing subitems been cancelled !!"); + return (string.Empty, Array.Empty().ToList()); + } tmp.SetSubItems(); } @@ -492,6 +507,11 @@ namespace AssetStudioGUI var typeMap = new Dictionary>(); foreach (var assetsFile in assetsManager.assetsFileList) { + if (assetsManager.tokenSource.IsCancellationRequested) + { + Logger.Info("Processing class structure been cancelled !!"); + return new Dictionary>(); + } if (typeMap.TryGetValue(assetsFile.unityVersion, out var curVer)) { foreach (var type in assetsFile.m_Types.Where(x => x.m_Type != null)) diff --git a/AssetStudioUtility/AssetStudioUtility.csproj b/AssetStudioUtility/AssetStudioUtility.csproj index 710d209..8b0d8c2 100644 --- a/AssetStudioUtility/AssetStudioUtility.csproj +++ b/AssetStudioUtility/AssetStudioUtility.csproj @@ -2,9 +2,9 @@ net6.0;net7.0 - 0.80.30 - 0.80.30 - 0.80.30 + 0.80.65 + 0.80.65 + 0.80.65 Copyright © Perfare 2018-2022 embedded true