v0.80.30
This commit is contained in:
35
AssetStudioCLI/App.config
Normal file
35
AssetStudioCLI/App.config
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="displayAll" value="False" />
|
||||
<add key="enablePreview" value="True" />
|
||||
<add key="displayInfo" value="True" />
|
||||
<add key="openAfterExport" value="True" />
|
||||
<add key="assetGroupOption" value="0" />
|
||||
<add key="convertTexture" value="True" />
|
||||
<add key="convertAudio" value="True" />
|
||||
<add key="convertType" value="Png" />
|
||||
<add key="eulerFilter" value="True" />
|
||||
<add key="filterPrecision" value="0.25" />
|
||||
<add key="exportAllNodes" value="True" />
|
||||
<add key="exportSkins" value="True" />
|
||||
<add key="exportAnimations" value="True" />
|
||||
<add key="boneSize" value="10" />
|
||||
<add key="fbxVersion" value="3" />
|
||||
<add key="fbxFormat" value="0" />
|
||||
<add key="scaleFactor" value="1" />
|
||||
<add key="exportBlendShape" value="True" />
|
||||
<add key="castToBone" value="False" />
|
||||
<add key="restoreExtensionName" value="True" />
|
||||
<add key="exportAllUvsAsDiffuseMaps" value="False" />
|
||||
<add key="key" value="147" />
|
||||
<add key="encrypted" value="True" />
|
||||
<add key="skipRenderer" value="False" />
|
||||
<add key="selectedGame" value="0" />
|
||||
<add key="collectAnimations" value="False" />
|
||||
<add key="enableResolveDependencies" value="True" />
|
||||
<add key="selectedCNUnityKey" value="0" />
|
||||
<add key="selectedAssetMapType" value="0" />
|
||||
<add key="exportMiHoYoBinData" value="True" />
|
||||
</appSettings>
|
||||
</configuration>
|
||||
@@ -1,19 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<ApplicationIcon>Resources\as.ico</ApplicationIcon>
|
||||
<Version>0.18.60</Version>
|
||||
<AssemblyVersion>0.18.60</AssemblyVersion>
|
||||
<FileVersion>0.18.60</FileVersion>
|
||||
<Copyright>Copyright © Razmoth 2022</Copyright>
|
||||
<Version>0.80.30</Version>
|
||||
<AssemblyVersion>0.80.30</AssemblyVersion>
|
||||
<FileVersion>0.80.30</FileVersion>
|
||||
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
|
||||
<BaseOutputPath>..\AssetStudioGUI\bin</BaseOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -22,7 +22,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
<Compile Update="Options.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyExtraFiles" AfterTargets="AfterBuild">
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using AssetStudio;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AssetStudio;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AssetStudioCLI
|
||||
{
|
||||
@@ -12,18 +13,28 @@ namespace AssetStudioCLI
|
||||
public static bool ExportTexture2D(AssetItem item, string exportPath)
|
||||
{
|
||||
var m_Texture2D = (Texture2D)item.Asset;
|
||||
var type = ImageFormat.Png;
|
||||
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
|
||||
return false;
|
||||
var image = m_Texture2D.ConvertToImage(true);
|
||||
if (image == null)
|
||||
return false;
|
||||
using (image)
|
||||
if (Properties.Settings.Default.convertTexture)
|
||||
{
|
||||
using (var file = File.OpenWrite(exportFullPath))
|
||||
var type = Properties.Settings.Default.convertType;
|
||||
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
|
||||
return false;
|
||||
var image = m_Texture2D.ConvertToImage(true);
|
||||
if (image == null)
|
||||
return false;
|
||||
using (image)
|
||||
{
|
||||
image.WriteToStream(file, type);
|
||||
using (var file = File.OpenWrite(exportFullPath))
|
||||
{
|
||||
image.WriteToStream(file, type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath))
|
||||
return false;
|
||||
File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -35,7 +46,7 @@ namespace AssetStudioCLI
|
||||
if (m_AudioData == null || m_AudioData.Length == 0)
|
||||
return false;
|
||||
var converter = new AudioClipConverter(m_AudioClip);
|
||||
if (converter.IsSupport)
|
||||
if (Properties.Settings.Default.convertAudio && converter.IsSupport)
|
||||
{
|
||||
if (!TryExportFile(exportPath, item, ".wav", out var exportFullPath))
|
||||
return false;
|
||||
@@ -67,9 +78,12 @@ namespace AssetStudioCLI
|
||||
{
|
||||
var m_TextAsset = (TextAsset)(item.Asset);
|
||||
var extension = ".txt";
|
||||
if (!string.IsNullOrEmpty(item.Container))
|
||||
if (Properties.Settings.Default.restoreExtensionName)
|
||||
{
|
||||
extension = Path.GetExtension(item.Container);
|
||||
if (!string.IsNullOrEmpty(item.Container))
|
||||
{
|
||||
extension = Path.GetExtension(item.Container);
|
||||
}
|
||||
}
|
||||
if (!TryExportFile(exportPath, item, extension, out var exportFullPath))
|
||||
return false;
|
||||
@@ -85,6 +99,7 @@ namespace AssetStudioCLI
|
||||
switch (m_MiHoYoBinData.Type)
|
||||
{
|
||||
case MiHoYoBinDataType.JSON:
|
||||
|
||||
if (!TryExportFile(exportPath, item, ".json", out exportFullPath))
|
||||
return false;
|
||||
var json = m_MiHoYoBinData.Dump() as string;
|
||||
@@ -95,10 +110,18 @@ namespace AssetStudioCLI
|
||||
}
|
||||
break;
|
||||
case MiHoYoBinDataType.Bytes:
|
||||
if (!TryExportFile(exportPath, item, ".bin", out exportFullPath))
|
||||
var extension = ".bin";
|
||||
if (Properties.Settings.Default.restoreExtensionName)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(item.Container))
|
||||
{
|
||||
extension = Path.GetExtension(item.Container);
|
||||
}
|
||||
}
|
||||
if (!TryExportFile(exportPath, item, extension, out exportFullPath))
|
||||
return false;
|
||||
var bytes = m_MiHoYoBinData.Dump() as byte[];
|
||||
if (bytes.Length != 0)
|
||||
if (!bytes.IsNullOrEmpty())
|
||||
{
|
||||
File.WriteAllBytes(exportFullPath, bytes);
|
||||
return true;
|
||||
@@ -233,7 +256,7 @@ namespace AssetStudioCLI
|
||||
|
||||
public static bool ExportSprite(AssetItem item, string exportPath)
|
||||
{
|
||||
var type = ImageFormat.Png;
|
||||
var type = Properties.Settings.Default.convertType;
|
||||
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
|
||||
return false;
|
||||
var image = ((Sprite)item.Asset).GetImage();
|
||||
@@ -251,22 +274,6 @@ namespace AssetStudioCLI
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ExportJsonFile(AssetItem item, string exportPath)
|
||||
{
|
||||
if (!TryExportFile(exportPath, item, ".json", out var exportFullPath))
|
||||
return false;
|
||||
var str = JsonConvert.SerializeObject(item.Asset, Formatting.Indented);
|
||||
if (!string.IsNullOrEmpty(str) && str != "{}")
|
||||
{
|
||||
File.WriteAllText(exportFullPath, str);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ExportRawFile(item, exportPath);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ExportRawFile(AssetItem item, string exportPath)
|
||||
{
|
||||
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath))
|
||||
@@ -293,16 +300,57 @@ namespace AssetStudioCLI
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ExportAnimationClip(AssetItem item, string exportPath)
|
||||
public static bool ExportAnimator(AssetItem item, string exportPath, List<AssetItem> animationList = null)
|
||||
{
|
||||
if (!TryExportFile(exportPath, item, ".anim", out var exportFullPath))
|
||||
return false;
|
||||
var m_AnimationClip = (AnimationClip)item.Asset;
|
||||
var str = m_AnimationClip.Convert(Studio.Game);
|
||||
File.WriteAllText(exportFullPath, str);
|
||||
var exportFullPath = Path.Combine(exportPath, item.Text, item.Text + ".fbx");
|
||||
if (File.Exists(exportFullPath))
|
||||
{
|
||||
exportFullPath = Path.Combine(exportPath, item.Text + item.UniqueID, item.Text + ".fbx");
|
||||
}
|
||||
var m_Animator = (Animator)item.Asset;
|
||||
var convert = animationList != null
|
||||
? new ModelConverter(m_Animator, Properties.Settings.Default.convertType, Studio.Game, Properties.Settings.Default.collectAnimations, animationList.Select(x => (AnimationClip)x.Asset).ToArray())
|
||||
: new ModelConverter(m_Animator, Properties.Settings.Default.convertType, Studio.Game, Properties.Settings.Default.collectAnimations);
|
||||
ExportFbx(convert, exportFullPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void ExportGameObject(GameObject gameObject, string exportPath, List<AssetItem> 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);
|
||||
exportPath = exportPath + FixFileName(gameObject.m_Name) + ".fbx";
|
||||
ExportFbx(convert, exportPath);
|
||||
}
|
||||
|
||||
public static void ExportGameObjectMerge(List<GameObject> gameObject, string exportPath, List<AssetItem> 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);
|
||||
}
|
||||
|
||||
private static void ExportFbx(IImported convert, string exportPath)
|
||||
{
|
||||
var eulerFilter = Properties.Settings.Default.eulerFilter;
|
||||
var filterPrecision = (float)Properties.Settings.Default.filterPrecision;
|
||||
var exportAllNodes = Properties.Settings.Default.exportAllNodes;
|
||||
var exportSkins = Properties.Settings.Default.exportSkins;
|
||||
var exportAnimations = Properties.Settings.Default.exportAnimations;
|
||||
var exportBlendShape = Properties.Settings.Default.exportBlendShape;
|
||||
var castToBone = Properties.Settings.Default.castToBone;
|
||||
var boneSize = (int)Properties.Settings.Default.boneSize;
|
||||
var exportAllUvsAsDiffuseMaps = Properties.Settings.Default.exportAllUvsAsDiffuseMaps;
|
||||
var scaleFactor = (float)Properties.Settings.Default.scaleFactor;
|
||||
var fbxVersion = Properties.Settings.Default.fbxVersion;
|
||||
var fbxFormat = Properties.Settings.Default.fbxFormat;
|
||||
ModelExporter.ExportFbx(exportPath, convert, eulerFilter, filterPrecision,
|
||||
exportAllNodes, exportSkins, exportAnimations, exportBlendShape, castToBone, boneSize, exportAllUvsAsDiffuseMaps, scaleFactor, fbxVersion, fbxFormat == 1);
|
||||
}
|
||||
|
||||
public static bool ExportDumpFile(AssetItem item, string exportPath)
|
||||
{
|
||||
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath))
|
||||
@@ -341,16 +389,28 @@ namespace AssetStudioCLI
|
||||
case ClassIDType.Sprite:
|
||||
return ExportSprite(item, exportPath);
|
||||
case ClassIDType.Animator:
|
||||
return false;
|
||||
return ExportAnimator(item, exportPath, new List<AssetItem>());
|
||||
case ClassIDType.AnimationClip:
|
||||
return ExportAnimationClip(item, exportPath);
|
||||
return false;
|
||||
case ClassIDType.MiHoYoBinData:
|
||||
return ExportMiHoYoBinData(item, exportPath);
|
||||
default:
|
||||
return ExportJsonFile(item, exportPath);
|
||||
return ExportRawFile(item, exportPath);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ExportJSONFile(AssetItem item, string exportPath)
|
||||
{
|
||||
if (!TryExportFile(exportPath, item, ".json", out var exportFullPath))
|
||||
return false;
|
||||
|
||||
var settings = new JsonSerializerSettings();
|
||||
settings.Converters.Add(new StringEnumConverter());
|
||||
var str = JsonConvert.SerializeObject(item.Asset, Formatting.Indented, settings);
|
||||
File.WriteAllText(exportFullPath, str);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string FixFileName(string str)
|
||||
{
|
||||
if (str.Length >= 260) return Path.GetRandomFileName();
|
||||
|
||||
70
AssetStudioCLI/Options.cs
Normal file
70
AssetStudioCLI/Options.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using AssetStudio;
|
||||
using System.ComponentModel;
|
||||
using System.Configuration;
|
||||
|
||||
namespace AssetStudioCLI.Properties {
|
||||
public static class AppSettings
|
||||
{
|
||||
public static string Get(string key)
|
||||
{
|
||||
return ConfigurationManager.AppSettings[key];
|
||||
}
|
||||
|
||||
public static TValue Get<TValue>(string key, TValue defaultValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = Get(key);
|
||||
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return defaultValue;
|
||||
|
||||
return (TValue)TypeDescriptor.GetConverter(typeof(TValue)).ConvertFromInvariantString(value);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class Settings
|
||||
{
|
||||
private static Settings defaultInstance = new Settings();
|
||||
|
||||
public static Settings Default => defaultInstance;
|
||||
|
||||
public bool displayAll => AppSettings.Get("displayAll", false);
|
||||
public bool enablePreview => AppSettings.Get("enablePreview", true);
|
||||
public bool displayInfo => AppSettings.Get("displayInfo", true);
|
||||
public bool openAfterExport => AppSettings.Get("openAfterExport", true);
|
||||
public int assetGroupOption => AppSettings.Get("assetGroupOption", 0);
|
||||
public bool convertTexture => AppSettings.Get("convertTexture", true);
|
||||
public bool convertAudio => AppSettings.Get("convertAudio", true);
|
||||
public ImageFormat convertType => AppSettings.Get("convertType", ImageFormat.Png);
|
||||
public bool eulerFilter => AppSettings.Get("eulerFilter", true);
|
||||
public decimal filterPrecision => AppSettings.Get("filterPrecision", (decimal)0.25);
|
||||
public bool exportAllNodes => AppSettings.Get("exportAllNodes", true);
|
||||
public bool exportSkins => AppSettings.Get("exportSkins", true);
|
||||
public bool collectAnimations => AppSettings.Get("collectAnimations", true);
|
||||
public bool exportAnimations => AppSettings.Get("exportAnimations", true);
|
||||
public decimal boneSize => AppSettings.Get("boneSize", (decimal)10);
|
||||
public int fbxVersion => AppSettings.Get("fbxVersion", 3);
|
||||
public int fbxFormat => AppSettings.Get("fbxFormat", 0);
|
||||
public decimal scaleFactor => AppSettings.Get("scaleFactor", (decimal)1);
|
||||
public bool exportBlendShape => AppSettings.Get("exportBlendShape", true);
|
||||
public bool castToBone => AppSettings.Get("castToBone", false);
|
||||
public bool restoreExtensionName => AppSettings.Get("restoreExtensionName", true);
|
||||
public bool exportAllUvsAsDiffuseMaps => AppSettings.Get("exportAllUvsAsDiffuseMaps", 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);
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,9 @@ namespace AssetStudioCLI
|
||||
optionsBinder.MapType,
|
||||
optionsBinder.MapName,
|
||||
optionsBinder.GroupAssetsType,
|
||||
optionsBinder.NoAssetBundle,
|
||||
optionsBinder.NoIndexObject,
|
||||
optionsBinder.XorByte,
|
||||
optionsBinder.IncludeAnimators,
|
||||
optionsBinder.SkipMiHoYoBinData,
|
||||
optionsBinder.Key,
|
||||
optionsBinder.AIFile,
|
||||
optionsBinder.Input,
|
||||
optionsBinder.Output
|
||||
@@ -54,36 +54,32 @@ namespace AssetStudioCLI
|
||||
}
|
||||
|
||||
Studio.Game = game;
|
||||
Logger.Default = new ConsoleLogger();
|
||||
assetsManager.Silent = o.Silent;
|
||||
assetsManager.Game = game;
|
||||
AssetBundle.Exportable = !o.NoAssetBundle;
|
||||
IndexObject.Exportable = !o.NoIndexObject;
|
||||
MiHoYoBinData.Exportable = !o.SkipMiHoYoBinData;
|
||||
|
||||
if (!o.Silent)
|
||||
if (o.Key != default)
|
||||
{
|
||||
Logger.Default = new ConsoleLogger();
|
||||
}
|
||||
|
||||
if (o.XorKey != default)
|
||||
{
|
||||
if (o.NoIndexObject)
|
||||
if (o.SkipMiHoYoBinData)
|
||||
{
|
||||
Logger.Warning("XOR key is set but IndexObject/MiHoYoBinData is excluded, ignoring key...");
|
||||
Logger.Warning("Key is set but IndexObject/MiHoYoBinData is excluded, ignoring key...");
|
||||
}
|
||||
else
|
||||
{
|
||||
MiHoYoBinData.doXOR = true;
|
||||
MiHoYoBinData.Key = o.XorKey;
|
||||
MiHoYoBinData.Encrypted = true;
|
||||
MiHoYoBinData.Key = o.Key;
|
||||
}
|
||||
}
|
||||
|
||||
if (o.AIFile != null && game.Name == "GI" || game.Name == "GI_CB2" || game.Name == "GI_CB3")
|
||||
if (o.AIFile != null && game.Type.IsGISubGroup())
|
||||
{
|
||||
ResourceIndex.FromFile(o.AIFile.FullName);
|
||||
}
|
||||
|
||||
Logger.Info("Scanning for files");
|
||||
var files = o.Input.Attributes.HasFlag(FileAttributes.Directory) ? Directory.GetFiles(o.Input.FullName, $"*{game.Extension}", SearchOption.AllDirectories).OrderBy(x => x.Length).ToArray() : new string[] { o.Input.FullName };
|
||||
Logger.Info(string.Format("Found {0} file(s)", files.Count()));
|
||||
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 };
|
||||
Logger.Info(string.Format("Found {0} file(s)", files.Length));
|
||||
|
||||
if (o.MapOp.Equals(MapOpType.None))
|
||||
{
|
||||
@@ -91,15 +87,18 @@ namespace AssetStudioCLI
|
||||
foreach (var file in files)
|
||||
{
|
||||
assetsManager.LoadFiles(file);
|
||||
BuildAssetData(o.TypeFilter, o.NameFilter, o.ContainerFilter, ref i);
|
||||
ExportAssets(o.Output.FullName, exportableAssets, o.GroupAssetsType);
|
||||
if (assetsManager.assetsFileList.Count > 0)
|
||||
{
|
||||
BuildAssetData(o.TypeFilter, o.NameFilter, o.ContainerFilter, ref i);
|
||||
ExportAssets(o.Output.FullName, exportableAssets, o.GroupAssetsType);
|
||||
}
|
||||
exportableAssets.Clear();
|
||||
assetsManager.Clear();
|
||||
}
|
||||
}
|
||||
if (o.MapOp.HasFlag(MapOpType.CABMap))
|
||||
{
|
||||
CABManager.BuildMap(files.ToList(), game);
|
||||
AssetsHelper.BuildCABMap(files, "", "", game);
|
||||
}
|
||||
if (o.MapOp.HasFlag(MapOpType.AssetMap))
|
||||
{
|
||||
@@ -107,16 +106,16 @@ namespace AssetStudioCLI
|
||||
{
|
||||
throw new Exception("Unable to build AssetMap with input_path as a file !!");
|
||||
}
|
||||
var assets = BuildAssetMap(files.ToList(), o.TypeFilter, o.NameFilter, o.ContainerFilter);
|
||||
var assets = AssetsHelper.BuildAssetMap(files, game, o.TypeFilter, o.NameFilter, o.ContainerFilter);
|
||||
if (!o.Output.Exists)
|
||||
{
|
||||
o.Output.Create();
|
||||
}
|
||||
if (string.IsNullOrEmpty(o.MapName))
|
||||
{
|
||||
o.MapName = $"assets_map_{game.Name}";
|
||||
o.MapName = "assets_map";
|
||||
}
|
||||
ExportAssetsMap(o.Output.FullName, assets, o.MapName, o.MapType);
|
||||
AssetsHelper.ExportAssetsMap(assets, o.MapName, o.Output.FullName, o.MapType);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -141,9 +140,9 @@ namespace AssetStudioCLI
|
||||
public ExportListType MapType { get; set; }
|
||||
public string MapName { get; set; }
|
||||
public AssetGroupOption GroupAssetsType { get; set; }
|
||||
public bool NoAssetBundle { get; set; }
|
||||
public bool NoIndexObject { get; set; }
|
||||
public byte XorKey { get; set; }
|
||||
public bool SkipRenderer { get; set; }
|
||||
public bool SkipMiHoYoBinData { get; set; }
|
||||
public byte Key { get; set; }
|
||||
public FileInfo AIFile { get; set; }
|
||||
public FileInfo Input { get; set; }
|
||||
public DirectoryInfo Output { get; set; }
|
||||
@@ -160,9 +159,9 @@ namespace AssetStudioCLI
|
||||
public readonly Option<ExportListType> MapType;
|
||||
public readonly Option<string> MapName;
|
||||
public readonly Option<AssetGroupOption> GroupAssetsType;
|
||||
public readonly Option<bool> NoAssetBundle;
|
||||
public readonly Option<bool> NoIndexObject;
|
||||
public readonly Option<byte> XorByte;
|
||||
public readonly Option<bool> IncludeAnimators;
|
||||
public readonly Option<bool> SkipMiHoYoBinData;
|
||||
public readonly Option<byte> Key;
|
||||
public readonly Option<FileInfo> AIFile;
|
||||
public readonly Argument<FileInfo> Input;
|
||||
public readonly Argument<DirectoryInfo> Output;
|
||||
@@ -178,13 +177,13 @@ namespace AssetStudioCLI
|
||||
MapType = new Option<ExportListType>("--map_type", "AssetMap output type.");
|
||||
MapName = new Option<string>("--map_name", "Specify AssetMap file name.");
|
||||
GroupAssetsType = new Option<AssetGroupOption>("--group_assets_type", "Specify how exported assets should be grouped.");
|
||||
NoAssetBundle = new Option<bool>("--no_asset_bundle", "Exclude AssetBundle from AssetMap/Export.");
|
||||
NoIndexObject = new Option<bool>("--no_index_object", "Exclude IndexObject/MiHoYoBinData from AssetMap/Export.");
|
||||
IncludeAnimators = new Option<bool>("--skip_re", "Include Animator with Export.");
|
||||
SkipMiHoYoBinData = new Option<bool>("--skip_mihoyobindata", "Exclude IndexObject/MiHoYoBinData from AssetMap/Export.");
|
||||
AIFile = new Option<FileInfo>("--ai_file", "Specify asset_index json file path (to recover GI containers).").LegalFilePathsOnly();
|
||||
Input = new Argument<FileInfo>("input_path", "Input file/folder.").LegalFilePathsOnly();
|
||||
Output = new Argument<DirectoryInfo>("output_path", "Output folder.").LegalFilePathsOnly();
|
||||
|
||||
XorByte = new Option<byte>("--xor_key", result =>
|
||||
Key = new Option<byte>("--key", result =>
|
||||
{
|
||||
var value = result.Tokens.Single().Value;
|
||||
if (value.StartsWith("0x"))
|
||||
@@ -196,12 +195,12 @@ namespace AssetStudioCLI
|
||||
{
|
||||
return byte.Parse(value);
|
||||
}
|
||||
}, false, "XOR key to decrypt MiHoYoBinData.");
|
||||
}, false, "Key to decrypt MiHoYoBinData.");
|
||||
|
||||
TypeFilter.AddValidator(FilterValidator);
|
||||
NameFilter.AddValidator(FilterValidator);
|
||||
ContainerFilter.AddValidator(FilterValidator);
|
||||
XorByte.AddValidator(result =>
|
||||
Key.AddValidator(result =>
|
||||
{
|
||||
var value = result.Tokens.Single().Value;
|
||||
try
|
||||
@@ -266,9 +265,9 @@ namespace AssetStudioCLI
|
||||
MapType = bindingContext.ParseResult.GetValueForOption(MapType),
|
||||
MapName = bindingContext.ParseResult.GetValueForOption(MapName),
|
||||
GroupAssetsType = bindingContext.ParseResult.GetValueForOption(GroupAssetsType),
|
||||
NoAssetBundle = bindingContext.ParseResult.GetValueForOption(NoAssetBundle),
|
||||
NoIndexObject = bindingContext.ParseResult.GetValueForOption(NoIndexObject),
|
||||
XorKey = bindingContext.ParseResult.GetValueForOption(XorByte),
|
||||
SkipRenderer = bindingContext.ParseResult.GetValueForOption(IncludeAnimators),
|
||||
SkipMiHoYoBinData = bindingContext.ParseResult.GetValueForOption(SkipMiHoYoBinData),
|
||||
Key = bindingContext.ParseResult.GetValueForOption(Key),
|
||||
AIFile = bindingContext.ParseResult.GetValueForOption(AIFile),
|
||||
Input = bindingContext.ParseResult.GetValueForArgument(Input),
|
||||
Output = bindingContext.ParseResult.GetValueForArgument(Output)
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using static AssetStudioCLI.Exporter;
|
||||
using Object = AssetStudio.Object;
|
||||
using System.Globalization;
|
||||
|
||||
namespace AssetStudioCLI
|
||||
{
|
||||
@@ -20,11 +21,7 @@ namespace AssetStudioCLI
|
||||
CABMap,
|
||||
Both
|
||||
}
|
||||
public enum ExportListType
|
||||
{
|
||||
XML,
|
||||
JSON
|
||||
}
|
||||
|
||||
public enum AssetGroupOption
|
||||
{
|
||||
ByType,
|
||||
@@ -35,7 +32,7 @@ namespace AssetStudioCLI
|
||||
|
||||
internal static class Studio
|
||||
{
|
||||
public static AssetsManager assetsManager = new AssetsManager() { ResolveDependancies = false };
|
||||
public static AssetsManager assetsManager = new AssetsManager() { ResolveDependencies = false };
|
||||
public static List<AssetItem> exportableAssets = new List<AssetItem>();
|
||||
public static Game Game;
|
||||
|
||||
@@ -67,13 +64,16 @@ namespace AssetStudioCLI
|
||||
public static int ExtractFile(string fileName, string savePath)
|
||||
{
|
||||
int extractedCount = 0;
|
||||
var reader = new FileReader(fileName, Game);
|
||||
var reader = new FileReader(fileName);
|
||||
reader = reader.PreProcessing(Game);
|
||||
if (reader.FileType == FileType.BundleFile)
|
||||
extractedCount += ExtractBundleFile(reader, savePath);
|
||||
else if (reader.FileType == FileType.WebFile)
|
||||
extractedCount += ExtractWebDataFile(reader, savePath);
|
||||
else if (reader.FileType == FileType.GameFile)
|
||||
extractedCount += ExtractGameFile(reader, savePath);
|
||||
else if (reader.FileType == FileType.BlkFile)
|
||||
extractedCount += ExtractBlkFile(reader, savePath);
|
||||
else if (reader.FileType == FileType.BlockFile)
|
||||
extractedCount += ExtractBlockFile(reader, savePath);
|
||||
else
|
||||
reader.Dispose();
|
||||
return extractedCount;
|
||||
@@ -82,12 +82,19 @@ namespace AssetStudioCLI
|
||||
private static int ExtractBundleFile(FileReader reader, string savePath)
|
||||
{
|
||||
Logger.Info($"Decompressing {reader.FileName} ...");
|
||||
var bundleFile = new BundleFile(reader);
|
||||
reader.Dispose();
|
||||
if (bundleFile.FileList.Length > 0)
|
||||
try
|
||||
{
|
||||
var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked");
|
||||
return ExtractStreamFile(extractPath, bundleFile.FileList);
|
||||
var bundleFile = new BundleFile(reader, Game);
|
||||
reader.Dispose();
|
||||
if (bundleFile.fileList.Length > 0)
|
||||
{
|
||||
var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked");
|
||||
return ExtractStreamFile(extractPath, bundleFile.fileList);
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
Logger.Error($"Game type mismatch, Expected {nameof(Mr0k)} but got {Game.Name} ({Game.GetType().Name}) !!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -105,16 +112,69 @@ namespace AssetStudioCLI
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int ExtractGameFile(FileReader reader, string savePath)
|
||||
private static int ExtractBlkFile(FileReader reader, string savePath)
|
||||
{
|
||||
Logger.Info($"Decompressing {reader.FileName}...");
|
||||
var gameFile = new GameFile(reader);
|
||||
reader.Dispose();
|
||||
var fileList = gameFile.Bundles.SelectMany(x => x.Value).ToList();
|
||||
if (fileList.Count > 0)
|
||||
int total = 0;
|
||||
Logger.Info($"Decompressing {reader.FileName} ...");
|
||||
try
|
||||
{
|
||||
var extractPath = Path.Combine(savePath, Path.GetFileNameWithoutExtension(reader.FileName));
|
||||
return ExtractStreamFile(extractPath, fileList.ToArray());
|
||||
using var stream = BlkUtils.Decrypt(reader, (Blk)Game);
|
||||
do
|
||||
{
|
||||
stream.Offset = stream.RelativePosition;
|
||||
var dummyPath = Path.Combine(reader.FullPath, stream.RelativePosition.ToString("X8"));
|
||||
var subReader = new FileReader(dummyPath, stream, true);
|
||||
var subSavePath = Path.Combine(savePath, reader.FileName + "_unpacked");
|
||||
switch (subReader.FileType)
|
||||
{
|
||||
case FileType.BundleFile:
|
||||
total += ExtractBundleFile(subReader, subSavePath);
|
||||
break;
|
||||
case FileType.Mhy0File:
|
||||
total += ExtractMhy0File(subReader, subSavePath);
|
||||
break;
|
||||
}
|
||||
} while (stream.Remaining > 0);
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
Logger.Error($"Game type mismatch, Expected {nameof(Blk)} but got {Game.Name} ({Game.GetType().Name}) !!");
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private static int ExtractBlockFile(FileReader reader, string savePath)
|
||||
{
|
||||
int total = 0;
|
||||
Logger.Info($"Decompressing {reader.FileName} ...");
|
||||
using var stream = new BlockStream(reader.BaseStream, 0);
|
||||
do
|
||||
{
|
||||
stream.Offset = stream.RelativePosition;
|
||||
var subSavePath = Path.Combine(savePath, reader.FileName + "_unpacked");
|
||||
var dummyPath = Path.Combine(reader.FullPath, stream.RelativePosition.ToString("X8"));
|
||||
var subReader = new FileReader(dummyPath, stream, true);
|
||||
total += ExtractBundleFile(subReader, subSavePath);
|
||||
} while (stream.Remaining > 0);
|
||||
return total;
|
||||
}
|
||||
|
||||
private static int ExtractMhy0File(FileReader reader, string savePath)
|
||||
{
|
||||
Logger.Info($"Decompressing {reader.FileName} ...");
|
||||
try
|
||||
{
|
||||
var mhy0File = new Mhy0File(reader, reader.FullPath, (Mhy0)Game);
|
||||
reader.Dispose();
|
||||
if (mhy0File.fileList.Length > 0)
|
||||
{
|
||||
var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked");
|
||||
return ExtractStreamFile(extractPath, mhy0File.fileList);
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
Logger.Error($"Game type mismatch, Expected {nameof(Mhy0)} but got {Game.Name} ({Game.GetType().Name}) !!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -143,165 +203,29 @@ namespace AssetStudioCLI
|
||||
return extractedCount;
|
||||
}
|
||||
|
||||
public static List<AssetEntry> BuildAssetMap(List<string> files, ClassIDType[] typeFilters, Regex[] nameFilters, Regex[] containerFilters)
|
||||
public static void UpdateContainers()
|
||||
{
|
||||
var assets = new List<AssetEntry>();
|
||||
for (int i = 0; i < files.Count; i++)
|
||||
if (exportableAssets.Count > 0)
|
||||
{
|
||||
var file = files[i];
|
||||
var reader = new FileReader(file, Game);
|
||||
var gameFile = new GameFile(reader);
|
||||
reader.Dispose();
|
||||
|
||||
foreach (var bundle in gameFile.Bundles)
|
||||
Logger.Info("Updating Containers...");
|
||||
foreach (var asset in exportableAssets)
|
||||
{
|
||||
foreach (var cab in bundle.Value)
|
||||
if (int.TryParse(asset.Container, out var value))
|
||||
{
|
||||
var dummyPath = Path.Combine(Path.GetDirectoryName(file), cab.fileName);
|
||||
using (var cabReader = new FileReader(dummyPath, cab.stream, Game))
|
||||
var last = unchecked((uint)value);
|
||||
var path = ResourceIndex.GetAssetPath(last);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
if (cabReader.FileType == FileType.AssetsFile)
|
||||
asset.Container = path;
|
||||
if (asset.Type == ClassIDType.MiHoYoBinData)
|
||||
{
|
||||
var assetsFile = new SerializedFile(cabReader, assetsManager, file);
|
||||
assetsManager.assetsFileList.Add(assetsFile);
|
||||
|
||||
assetsFile.m_Objects = assetsFile.m_Objects.Where(x => x.HasExportableType()).ToList();
|
||||
|
||||
IndexObject indexObject = null;
|
||||
var containers = new List<(PPtr<Object>, string)>(assetsFile.m_Objects.Count);
|
||||
var animators = new List<(PPtr<GameObject>, AssetEntry)>(assetsFile.m_Objects.Count);
|
||||
var objectAssetItemDic = new Dictionary<Object, AssetEntry>(assetsFile.m_Objects.Count);
|
||||
foreach (var objInfo in assetsFile.m_Objects)
|
||||
{
|
||||
var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objInfo);
|
||||
var obj = new Object(objectReader);
|
||||
var asset = new AssetEntry()
|
||||
{
|
||||
Source = file,
|
||||
PathID = objectReader.m_PathID,
|
||||
Type = objectReader.type,
|
||||
Container = ""
|
||||
};
|
||||
|
||||
var exportable = true;
|
||||
switch (objectReader.type)
|
||||
{
|
||||
case ClassIDType.AssetBundle:
|
||||
var assetBundle = new AssetBundle(objectReader);
|
||||
foreach (var m_Container in assetBundle.Container)
|
||||
{
|
||||
var preloadIndex = m_Container.Value.preloadIndex;
|
||||
var preloadSize = m_Container.Value.preloadSize;
|
||||
var preloadEnd = preloadIndex + preloadSize;
|
||||
for (int k = preloadIndex; k < preloadEnd; k++)
|
||||
{
|
||||
if (Game.Name == "GI" || Game.Name == "GI_CB2" || Game.Name == "GI_CB3")
|
||||
{
|
||||
if (long.TryParse(m_Container.Key, out var containerValue))
|
||||
{
|
||||
var last = unchecked((uint)containerValue);
|
||||
var path = ResourceIndex.GetBundlePath(last);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
containers.Add((assetBundle.PreloadTable[k], path));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
containers.Add((assetBundle.PreloadTable[k], m_Container.Key));
|
||||
}
|
||||
}
|
||||
obj = null;
|
||||
asset.Name = assetBundle.m_Name;
|
||||
exportable = AssetBundle.Exportable;
|
||||
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<GameObject>(objectReader);
|
||||
animators.Add((component, asset));
|
||||
asset.Name = "Animator";
|
||||
break;
|
||||
case ClassIDType.MiHoYoBinData:
|
||||
if (indexObject.Names.TryGetValue(objectReader.m_PathID, out var binName))
|
||||
{
|
||||
var path = ResourceIndex.GetContainerFromBinName(file, binName);
|
||||
asset.Container = path;
|
||||
asset.Name = !string.IsNullOrEmpty(path) ? Path.GetFileName(path) : binName;
|
||||
}
|
||||
exportable = IndexObject.Exportable;
|
||||
break;
|
||||
case ClassIDType.IndexObject:
|
||||
indexObject = new IndexObject(objectReader);
|
||||
obj = null;
|
||||
asset.Name = "IndexObject";
|
||||
exportable = IndexObject.Exportable;
|
||||
break;
|
||||
default:
|
||||
asset.Name = objectReader.ReadAlignedString();
|
||||
break;
|
||||
}
|
||||
if (obj != null)
|
||||
{
|
||||
objectAssetItemDic.Add(obj, asset);
|
||||
assetsFile.AddObject(obj);
|
||||
}
|
||||
var isMatchRegex = nameFilters.Length == 0 || nameFilters.Any(x => x.IsMatch(asset.Name) || asset.Type == ClassIDType.Animator);
|
||||
var isFilteredType = typeFilters.Length == 0 || typeFilters.Contains(asset.Type) || asset.Type == ClassIDType.Animator;
|
||||
if (isMatchRegex && isFilteredType && exportable)
|
||||
{
|
||||
assets.Add(asset);
|
||||
}
|
||||
}
|
||||
foreach (var pair in animators)
|
||||
{
|
||||
if (pair.Item1.TryGet(out var gameObject) && gameObject is GameObject && (nameFilters.Length == 0 || nameFilters.Any(x => x.IsMatch(gameObject.m_Name))) && (typeFilters.Length == 0 || typeFilters.Contains(pair.Item2.Type)))
|
||||
{
|
||||
pair.Item2.Name = gameObject.m_Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
assets.Remove(pair.Item2);
|
||||
}
|
||||
}
|
||||
foreach ((var pptr, var container) in containers)
|
||||
{
|
||||
if (pptr.TryGet(out var obj))
|
||||
{
|
||||
var item = objectAssetItemDic[obj];
|
||||
if (containerFilters.Length == 0 || containerFilters.Any(x => x.IsMatch(container)))
|
||||
{
|
||||
item.Container = container;
|
||||
}
|
||||
else
|
||||
{
|
||||
assets.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
assetsManager.assetsFileList.Clear();
|
||||
asset.Text = Path.GetFileNameWithoutExtension(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Info($"[{i + 1}/{files.Count}] Processed {Path.GetFileName(file)}");
|
||||
Progress.Report(i + 1, files.Count);
|
||||
Logger.Info("Updated !!");
|
||||
}
|
||||
|
||||
return assets;
|
||||
}
|
||||
|
||||
public static void BuildAssetData(ClassIDType[] typeFilters, Regex[] nameFilters, Regex[] containerFilters, ref int i)
|
||||
@@ -309,6 +233,7 @@ namespace AssetStudioCLI
|
||||
string productName = null;
|
||||
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
|
||||
var objectAssetItemDic = new Dictionary<Object, AssetItem>(objectCount);
|
||||
var mihoyoBinDataNames = new List<(PPtr<Object>, string)>();
|
||||
var containers = new List<(PPtr<Object>, string)>();
|
||||
foreach (var assetsFile in assetsManager.assetsFileList)
|
||||
{
|
||||
@@ -359,6 +284,7 @@ namespace AssetStudioCLI
|
||||
{
|
||||
assetItem.Text = gameObject.m_Name;
|
||||
}
|
||||
exportable = true;
|
||||
break;
|
||||
case MonoBehaviour m_MonoBehaviour:
|
||||
if (m_MonoBehaviour.m_Name == "" && m_MonoBehaviour.m_Script.TryGet(out var m_Script))
|
||||
@@ -374,35 +300,27 @@ namespace AssetStudioCLI
|
||||
productName = m_PlayerSettings.productName;
|
||||
break;
|
||||
case AssetBundle m_AssetBundle:
|
||||
foreach (var m_Container in m_AssetBundle.Container)
|
||||
foreach (var m_Container in m_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++)
|
||||
{
|
||||
if (Game.Name == "GI" || Game.Name == "GI_CB2" || Game.Name == "GI_CB3")
|
||||
{
|
||||
if (long.TryParse(m_Container.Key, out var containerValue))
|
||||
{
|
||||
var last = unchecked((uint)containerValue);
|
||||
var path = ResourceIndex.GetBundlePath(last);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
containers.Add((m_AssetBundle.PreloadTable[k], path));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
containers.Add((m_AssetBundle.PreloadTable[k], m_Container.Key));
|
||||
containers.Add((m_AssetBundle.m_PreloadTable[k], m_Container.Key));
|
||||
}
|
||||
}
|
||||
assetItem.Text = m_AssetBundle.m_Name;
|
||||
exportable = AssetBundle.Exportable;
|
||||
break;
|
||||
case IndexObject m_IndexObject:
|
||||
foreach (var index in m_IndexObject.AssetMap)
|
||||
{
|
||||
mihoyoBinDataNames.Add((index.Value.Object, index.Key));
|
||||
}
|
||||
assetItem.Text = "IndexObject";
|
||||
exportable = IndexObject.Exportable;
|
||||
break;
|
||||
case MiHoYoBinData m_MiHoYoBinData:
|
||||
exportable = MiHoYoBinData.Exportable;
|
||||
break;
|
||||
case ResourceManager m_ResourceManager:
|
||||
foreach (var m_Container in m_ResourceManager.m_Container)
|
||||
@@ -410,35 +328,6 @@ namespace AssetStudioCLI
|
||||
containers.Add((m_Container.Value, m_Container.Key));
|
||||
}
|
||||
break;
|
||||
case MiHoYoBinData m_MiHoYoBinData:
|
||||
if (m_MiHoYoBinData.assetsFile.ObjectsDic.TryGetValue(2, out var obj) && obj is IndexObject indexObject)
|
||||
{
|
||||
if (indexObject.Names.TryGetValue(m_MiHoYoBinData.m_PathID, out var binName))
|
||||
{
|
||||
string path = "";
|
||||
var game = GameManager.GetGame("GI");
|
||||
if (Path.GetExtension(assetsFile.originalPath) == game.Extension)
|
||||
{
|
||||
var blkName = Path.GetFileNameWithoutExtension(assetsFile.originalPath);
|
||||
var blk = Convert.ToUInt64(blkName);
|
||||
var lastHex = Convert.ToUInt32(binName, 16);
|
||||
var blkHash = (blk << 32) | lastHex;
|
||||
var index = ResourceIndex.GetAssetIndex(blkHash);
|
||||
var bundleInfo = ResourceIndex.GetBundleInfo(index);
|
||||
path = bundleInfo != null ? bundleInfo.Path : "";
|
||||
}
|
||||
else
|
||||
{
|
||||
var last = Convert.ToUInt32(binName, 16);
|
||||
path = ResourceIndex.GetBundlePath(last) ?? "";
|
||||
}
|
||||
assetItem.Container = path;
|
||||
assetItem.Text = !string.IsNullOrEmpty(path) ? Path.GetFileName(path) : binName;
|
||||
}
|
||||
}
|
||||
else assetItem.Text = string.Format("BinFile #{0}", assetItem.m_PathID);
|
||||
exportable = IndexObject.Exportable;
|
||||
break;
|
||||
case NamedObject m_NamedObject:
|
||||
assetItem.Text = m_NamedObject.m_Name;
|
||||
exportable = true;
|
||||
@@ -457,12 +346,25 @@ namespace AssetStudioCLI
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ((var pptr, var name) in mihoyoBinDataNames)
|
||||
{
|
||||
if (pptr.TryGet<MiHoYoBinData>(out var obj))
|
||||
{
|
||||
var assetItem = objectAssetItemDic[obj];
|
||||
if (int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var hash))
|
||||
{
|
||||
assetItem.Text = name;
|
||||
assetItem.Container = hash.ToString();
|
||||
}
|
||||
else assetItem.Text = $"BinFile #{assetItem.m_PathID}";
|
||||
}
|
||||
}
|
||||
foreach ((var pptr, var container) in containers)
|
||||
{
|
||||
if (pptr.TryGet(out var obj))
|
||||
{
|
||||
var item = objectAssetItemDic[obj];
|
||||
if (containerFilters.Length == 0 || containerFilters.Any(x => x.IsMatch(container)))
|
||||
if (containerFilters.IsNullOrEmpty() || containerFilters.Any(x => x.IsMatch(container)))
|
||||
{
|
||||
item.Container = container;
|
||||
}
|
||||
@@ -472,6 +374,10 @@ namespace AssetStudioCLI
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Game.Type.IsGISubGroup())
|
||||
{
|
||||
UpdateContainers();
|
||||
}
|
||||
containers.Clear();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user