Skip to content
This repository was archived by the owner on May 11, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3b87c1c
Add new naraka format
Rivelia Feb 5, 2024
5597235
- [CLI] allow `--types` to decide class processing behaviour.
Razmoth Feb 6, 2024
61ff13f
- [CLI] Added feature to search prebuild `AssetMap` to loaded filtede…
Razmoth Feb 6, 2024
6b31f63
- [Core] Added `Shader` decompilation/disassembly for `DXCB` program.
Razmoth Feb 6, 2024
3d5a1bc
- [Core] Added hashes to `Shader` output.
Razmoth Feb 6, 2024
14a6115
- [Core] handle `HasMesh` exception.
Razmoth Feb 6, 2024
7e595e2
- [Core] Fix incorrect output location for `GameObject`
Razmoth Feb 10, 2024
5db1b59
- [GUI] Update drag&drop effects.
Razmoth Feb 10, 2024
2e268e6
- [Core] Added shader flags.
Razmoth Feb 10, 2024
e1d7f53
- [Core] Update file foramt.
Razmoth Feb 11, 2024
cf2e750
- [Core] bug fixes #75
Razmoth Feb 18, 2024
90fd2a6
- [CLI] bug fixes
Razmoth Feb 19, 2024
2d1e5a0
- [Core] bug fixes.
Razmoth Feb 19, 2024
b5d54ce
- [Core] bug fixes.
Razmoth Feb 19, 2024
3946c0d
- [Core] bug fixes.
Razmoth Feb 19, 2024
462ac7b
- [Core] fix bug with `AssetMap`
Razmoth Feb 20, 2024
8ba29c2
- [CLI] fix bug with containers.
Razmoth Feb 20, 2024
55fcd60
- [CLI] allow case insensitive flags.
Razmoth Feb 20, 2024
ab73046
- [CLI] allow exported flags to override configs.
Razmoth Feb 20, 2024
63b4464
- [GUI] Highlight nodes that are exportable as fbx.
Razmoth Feb 27, 2024
5664441
- [GUI] change text
Razmoth Feb 27, 2024
d8cad9e
Update ImportHelper.cs
Razmoth Feb 28, 2024
c27ebec
- [Core] Added new entries.
Razmoth Feb 28, 2024
8a3517f
Merge branch 'main' of https://github.com/RazTools/Studio
Razmoth Feb 28, 2024
6c64b41
- [Core] fix parsing issues [ExA]
Razmoth Feb 29, 2024
d135a93
- [Core] bug fixes.
Razmoth Feb 29, 2024
7ed67c3
Support new naraka format
Rivelia Apr 5, 2024
6a1a587
Add new naraka format
Rivelia Feb 5, 2024
669b755
Support new naraka format
Rivelia Apr 5, 2024
37f4efa
Merge branch 'naraka-new-format' of https://github.com/Rivelia/Studio…
Rivelia Apr 5, 2024
0505e0f
support latest naraka patch
Rivelia May 1, 2024
d6014c0
update blocks size for latest naraka version
Rivelia Aug 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 81 additions & 24 deletions AssetStudio.CLI/Components/CommandLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.CommandLine.Binding;
using System.CommandLine.Parsing;
using System.Text.RegularExpressions;
using System.Collections.Generic;

namespace AssetStudio.CLI
{
Expand Down Expand Up @@ -49,7 +50,7 @@ public class Options
{
public bool Silent { get; set; }
public LoggerEvent[] LoggerFlags { get; set; }
public ClassIDType[] TypeFilter { get; set; }
public string[] TypeFilter { get; set; }
public Regex[] NameFilter { get; set; }
public Regex[] ContainerFilter { get; set; }
public string GameName { get; set; }
Expand All @@ -71,7 +72,7 @@ public class OptionsBinder : BinderBase<Options>
{
public readonly Option<bool> Silent;
public readonly Option<LoggerEvent[]> LoggerFlags;
public readonly Option<ClassIDType[]> TypeFilter;
public readonly Option<string[]> TypeFilter;
public readonly Option<Regex[]> NameFilter;
public readonly Option<Regex[]> ContainerFilter;
public readonly Option<string> GameName;
Expand All @@ -92,9 +93,69 @@ public OptionsBinder()
{
Silent = new Option<bool>("--silent", "Hide log messages.");
LoggerFlags = new Option<LoggerEvent[]>("--logger_flags", "Flags to control toggle log events.") { AllowMultipleArgumentsPerToken = true, ArgumentHelpName = "Verbose|Debug|Info|etc.." };
TypeFilter = new Option<ClassIDType[]>("--types", "Specify unity class type(s)") { AllowMultipleArgumentsPerToken = true, ArgumentHelpName = "Texture2D|Sprite|etc.." };
NameFilter = new Option<Regex[]>("--names", result => result.Tokens.Select(x => new Regex(x.Value, RegexOptions.IgnoreCase)).ToArray(), false, "Specify name regex filter(s).") { AllowMultipleArgumentsPerToken = true };
ContainerFilter = new Option<Regex[]>("--containers", result => result.Tokens.Select(x => new Regex(x.Value, RegexOptions.IgnoreCase)).ToArray(), false, "Specify container regex filter(s).") { AllowMultipleArgumentsPerToken = true };
TypeFilter = new Option<string[]>("--types", "Specify unity class type(s)") { AllowMultipleArgumentsPerToken = true, ArgumentHelpName = "Texture2D|Shader:Parse|Sprite:Both|etc.." };
NameFilter = new Option<Regex[]>("--names", result =>
{
var items = new List<Regex>();
var value = result.Tokens.Single().Value;
if (File.Exists(value))
{
var lines = File.ReadLines(value);
foreach (var line in lines)
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}

try
{
items.Add(new Regex(line, RegexOptions.IgnoreCase));
}
catch (ArgumentException e)
{
continue;
}
}
}
else
{
items.AddRange(result.Tokens.Select(x => new Regex(x.Value, RegexOptions.IgnoreCase)).ToArray());
}

return items.ToArray();
}, false, "Specify name regex filter(s).") { AllowMultipleArgumentsPerToken = true };
ContainerFilter = new Option<Regex[]>("--containers", result =>
{
var items = new List<Regex>();
var value = result.Tokens.Single().Value;
if (File.Exists(value))
{
var lines = File.ReadLines(value);
foreach(var line in lines)
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}

try
{
items.Add(new Regex(line, RegexOptions.IgnoreCase));
}
catch (ArgumentException e)
{
continue;
}
}
}
else
{
items.AddRange(result.Tokens.Select(x => new Regex(x.Value, RegexOptions.IgnoreCase)).ToArray());
}

return items.ToArray();
}, false, "Specify container regex filter(s).") { AllowMultipleArgumentsPerToken = true };
GameName = new Option<string>("--game", $"Specify Game.") { IsRequired = true };
KeyIndex = new Option<int>("--key_index", "Specify key index.") { ArgumentHelpName = UnityCNManager.ToString() };
MapOp = new Option<MapOpType>("--map_op", "Specify which map to build.");
Expand All @@ -110,16 +171,7 @@ public OptionsBinder()

Key = new Option<byte>("--key", result =>
{
var value = result.Tokens.Single().Value;
if (value.StartsWith("0x"))
{
value = value[2..];
return Convert.ToByte(value, 0x10);
}
else
{
return byte.Parse(value);
}
return ParseKey(result.Tokens.Single().Value);
}, false, "XOR key to decrypt MiHoYoBinData.");

LoggerFlags.AddValidator(FilterValidator);
Expand All @@ -131,15 +183,7 @@ public OptionsBinder()
var value = result.Tokens.Single().Value;
try
{
if (value.StartsWith("0x"))
{
value = value.Substring(2);
Convert.ToByte(value, 0x10);
}
else
{
byte.Parse(value);
}
ParseKey(value);
}
catch (Exception e)
{
Expand All @@ -156,6 +200,19 @@ public OptionsBinder()
MapType.SetDefaultValue(ExportListType.XML);
KeyIndex.SetDefaultValue(0);
}

public byte ParseKey(string value)
{
if (value.StartsWith("0x"))
{
value = value[2..];
return Convert.ToByte(value, 0x10);
}
else
{
return byte.Parse(value);
}
}

public void FilterValidator(OptionResult result)
{
Expand Down
2 changes: 1 addition & 1 deletion AssetStudio.CLI/Exporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ public static bool ExportGameObject(AssetItem item, string exportPath, List <Ass
return false;

var m_GameObject = (GameObject)item.Asset;
return ExportGameObject(m_GameObject, exportFullPath, animationList);
return ExportGameObject(m_GameObject, exportFullPath + Path.DirectorySeparatorChar, animationList);
}

public static bool ExportGameObject(GameObject gameObject, string exportPath, List<AssetItem> animationList = null)
Expand Down
97 changes: 73 additions & 24 deletions AssetStudio.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AssetStudio.CLI.Properties;
using Newtonsoft.Json;
using static AssetStudio.CLI.Studio;
Expand Down Expand Up @@ -46,20 +46,62 @@ public static void Run(Options o)
AssetsHelper.Minimal = Settings.Default.minimalAssetMap;
AssetsHelper.SetUnityVersion(o.UnityVersion);

if (o.TypeFilter == null)
{
TypeFlags.SetTypes(JsonConvert.DeserializeObject<Dictionary<ClassIDType, (bool, bool)>>(Settings.Default.types));
}
else
TypeFlags.SetTypes(JsonConvert.DeserializeObject<Dictionary<ClassIDType, (bool, bool)>>(Settings.Default.types));

var classTypeFilter = Array.Empty<ClassIDType>();
if (!o.TypeFilter.IsNullOrEmpty())
{
foreach (var type in o.TypeFilter)
var exportTexture2D = false;
var exportMaterial = false;
var classTypeFilterList = new List<ClassIDType>();
for (int i = 0; i < o.TypeFilter.Length; i++)
{
TypeFlags.SetType(type, true, true);
var typeStr = o.TypeFilter[i];
var type = ClassIDType.UnknownType;
var flag = TypeFlag.Both;

try
{
if (typeStr.Contains(':'))
{
var param = typeStr.Split(':');

flag = (TypeFlag)Enum.Parse(typeof(TypeFlag), param[1], true);

typeStr = param[0];
}

type = (ClassIDType)Enum.Parse(typeof(ClassIDType), typeStr, true);

if (type == ClassIDType.Texture2D)
{
exportTexture2D = flag.HasFlag(TypeFlag.Export);
}
else if (type == ClassIDType.Material)
{
exportMaterial = flag.HasFlag(TypeFlag.Export);
}

TypeFlags.SetType(type, flag.HasFlag(TypeFlag.Parse), flag.HasFlag(TypeFlag.Export));

classTypeFilterList.Add(type);
}
catch(Exception e)
{
Logger.Error($"{typeStr} has invalid format, skipping...");
continue;
}
}

classTypeFilter = classTypeFilterList.ToArray();

if (ClassIDType.GameObject.CanExport() || ClassIDType.Animator.CanExport())
{
TypeFlags.SetType(ClassIDType.Texture2D, true, false);
TypeFlags.SetType(ClassIDType.Texture2D, true, exportTexture2D);
if (Settings.Default.exportMaterials)
{
TypeFlags.SetType(ClassIDType.Material, true, exportMaterial);
}
if (ClassIDType.GameObject.CanExport())
{
TypeFlags.SetType(ClassIDType.Animator, true, false);
Expand All @@ -68,13 +110,14 @@ public static void Run(Options o)
{
TypeFlags.SetType(ClassIDType.GameObject, true, false);
}
if (Settings.Default.exportMaterials)
{
TypeFlags.SetType(ClassIDType.Material, true, false);
}
}
}

if (o.GroupAssetsType == AssetGroupOption.ByContainer)
{
TypeFlags.SetType(ClassIDType.AssetBundle, true, false);
}

assetsManager.Silent = o.Silent;
assetsManager.Game = game;
assetsManager.SpecifyUnityVersion = o.UnityVersion;
Expand Down Expand Up @@ -102,24 +145,30 @@ public static void Run(Options o)

if (o.MapOp.HasFlag(MapOpType.CABMap))
{
AssetsHelper.BuildCABMap(files, o.MapName, o.Input.FullName, game);
}
if (o.MapOp.HasFlag(MapOpType.Load))
{
AssetsHelper.LoadCABMapInternal(o.MapName);
assetsManager.ResolveDependencies = true;
if (o.MapOp.HasFlag(MapOpType.Load))
{
AssetsHelper.BuildCABMap(files, o.MapName, o.Input.FullName, game);
}
else
{
AssetsHelper.LoadCABMapInternal(o.MapName);
assetsManager.ResolveDependencies = true;
}
}
if (o.MapOp.HasFlag(MapOpType.AssetMap))
{
if (files.Length == 1)
if (o.MapOp.HasFlag(MapOpType.Load))
{
files = AssetsHelper.ParseAssetMap(o.MapName, o.MapType, classTypeFilter, o.NameFilter, o.ContainerFilter);
}
else
{
throw new Exception("Unable to build AssetMap with input_path as a file !!");
Task.Run(() => AssetsHelper.BuildAssetMap(files, o.MapName, game, o.Output.FullName, o.MapType, classTypeFilter, o.NameFilter, o.ContainerFilter)).Wait();
}
AssetsHelper.BuildAssetMap(files, o.MapName, game, o.Output.FullName, o.MapType, o.TypeFilter, o.NameFilter, o.ContainerFilter);
}
if (o.MapOp.HasFlag(MapOpType.Both))
{
AssetsHelper.BuildBoth(files, o.MapName, o.Input.FullName, game, o.Output.FullName, o.MapType, o.TypeFilter, o.NameFilter, o.ContainerFilter);
Task.Run(() => AssetsHelper.BuildBoth(files, o.MapName, o.Input.FullName, game, o.Output.FullName, o.MapType, classTypeFilter, o.NameFilter, o.ContainerFilter)).Wait();
}
if (o.MapOp.Equals(MapOpType.None) || o.MapOp.HasFlag(MapOpType.Load))
{
Expand All @@ -135,7 +184,7 @@ public static void Run(Options o)
assetsManager.LoadFiles(file);
if (assetsManager.assetsFileList.Count > 0)
{
BuildAssetData(o.TypeFilter, o.NameFilter, o.ContainerFilter, ref i);
BuildAssetData(classTypeFilter, o.NameFilter, o.ContainerFilter, ref i);
ExportAssets(o.Output.FullName, exportableAssets, o.GroupAssetsType, o.AssetExportType);
}
exportableAssets.Clear();
Expand Down
30 changes: 15 additions & 15 deletions AssetStudio.CLI/Studio.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ public static void BuildAssetData(ClassIDType[] typeFilters, Regex[] nameFilters
{
foreach (var asset in assetsFile.Objects)
{
ProcessAssetData(asset, typeFilters, nameFilters, objectAssetItemDic, mihoyoBinDataNames, containers, ref i);
ProcessAssetData(asset, objectAssetItemDic, mihoyoBinDataNames, containers, ref i);
}
}
foreach ((var pptr, var name) in mihoyoBinDataNames)
Expand All @@ -257,15 +257,7 @@ public static void BuildAssetData(ClassIDType[] typeFilters, Regex[] nameFilters
{
if (pptr.TryGet(out var obj))
{
var item = objectAssetItemDic[obj];
if (containerFilters.IsNullOrEmpty() || containerFilters.Any(x => x.IsMatch(container)))
{
item.Container = container;
}
else
{
exportableAssets.Remove(item);
}
objectAssetItemDic[obj].Container = container;
}
}
containers.Clear();
Expand All @@ -274,9 +266,19 @@ public static void BuildAssetData(ClassIDType[] typeFilters, Regex[] nameFilters
UpdateContainers();
}
}

var matches = exportableAssets.Where(x =>
{
var isMatchRegex = nameFilters.IsNullOrEmpty() || nameFilters.Any(y => y.IsMatch(x.Text));
var isFilteredType = typeFilters.IsNullOrEmpty() || typeFilters.Contains(x.Type);
var isContainerMatch = containerFilters.IsNullOrEmpty() || containerFilters.Any(y => y.IsMatch(x.Container));
return isMatchRegex && isFilteredType && isContainerMatch;
}).ToArray();
exportableAssets.Clear();
exportableAssets.AddRange(matches);
}

public static void ProcessAssetData(Object asset, ClassIDType[] typeFilters, Regex[] nameFilters, Dictionary<Object, AssetItem> objectAssetItemDic, List<(PPtr<Object>, string)> mihoyoBinDataNames, List<(PPtr<Object>, string)> containers, ref int i)
public static void ProcessAssetData(Object asset, Dictionary<Object, AssetItem> objectAssetItemDic, List<(PPtr<Object>, string)> mihoyoBinDataNames, List<(PPtr<Object>, string)> containers, ref int i)
{
var assetItem = new AssetItem(asset);
objectAssetItemDic.Add(asset, assetItem);
Expand Down Expand Up @@ -352,10 +354,8 @@ public static void ProcessAssetData(Object asset, ClassIDType[] typeFilters, Reg
{
assetItem.Text = assetItem.TypeString + assetItem.UniqueID;
}

var isMatchRegex = nameFilters.IsNullOrEmpty() || nameFilters.Any(x => x.IsMatch(assetItem.Text));
var isFilteredType = typeFilters.IsNullOrEmpty() || typeFilters.Contains(assetItem.Type);
if (isMatchRegex && isFilteredType && exportable)

if (exportable)
{
exportableAssets.Add(assetItem);
}
Expand Down
Loading