- WIP Asset Browser.

- fix for some modes without textures [`SR`]
- AssetMap new mode (Minimal/Full).
This commit is contained in:
Razmoth
2023-05-07 19:56:55 +04:00
parent 8e0f97ce2d
commit 2376a8669e
23 changed files with 498 additions and 180 deletions

View File

@@ -1,11 +0,0 @@
namespace AssetStudio
{
public class AssetEntry
{
public string Name { get; set; }
public string Container { get; set; }
public string Source { get; set; }
public long PathID { get; set; }
public ClassIDType Type { get; set; }
}
}

38
AssetStudio/AssetIndex.cs Normal file
View File

@@ -0,0 +1,38 @@
using System.Collections.Generic;
namespace AssetStudio
{
public record AssetIndex
{
public Dictionary<string, string> Types { get; set; }
public record SubAssetInfo
{
public string Name { get; set; }
public byte PathHashPre { get; set; }
public uint PathHashLast { get; set; }
}
public Dictionary<int, List<SubAssetInfo>> SubAssets { get; set; }
public Dictionary<int, List<int>> Dependencies { get; set; }
public List<uint> PreloadBlocks { get; set; }
public List<uint> PreloadShaderBlocks { get; set; }
public record BlockInfo
{
public byte Language { get; set; }
public uint Id { get; set; }
public uint Offset { get; set; }
}
public Dictionary<int, BlockInfo> Assets { get; set; }
public List<uint> SortList { get; set; }
public AssetIndex()
{
Types = new Dictionary<string, string>();
SubAssets = new Dictionary<int, List<SubAssetInfo>>();
Dependencies = new Dictionary<int, List<int>>();
PreloadBlocks = new List<uint>();
PreloadShaderBlocks = new List<uint>();
Assets = new Dictionary<int, BlockInfo>();
SortList = new List<uint>();
}
}
}

35
AssetStudio/AssetMap.cs Normal file
View File

@@ -0,0 +1,35 @@
using MessagePack;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace AssetStudio
{
[MessagePackObject]
public record AssetMap
{
[Key(0)]
public GameType GameType { get; set; }
[Key(1)]
public AssetEntry[] AssetEntries { get; set; }
}
[MessagePackObject]
public record AssetEntry
{
[Key(0)]
public string Name { get; set; }
[Key(1)]
public string Container { get; set; }
[Key(2)]
public string Source { get; set; }
[Key(3)]
public long PathID { get; set; }
[Key(4)]
public ClassIDType Type { get; set; }
public bool Matches(Regex regex) => regex.IsMatch(Name)
|| regex.IsMatch(Container)
|| regex.IsMatch(Source)
|| regex.IsMatch(PathID.ToString())
|| regex.IsMatch(Type.ToString());
}
}

View File

@@ -11,6 +11,7 @@
<ItemGroup>
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.4-beta" />
<PackageReference Include="MessagePack" Version="2.5.108" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>

View File

@@ -9,6 +9,7 @@ using Newtonsoft.Json;
using System.Text.RegularExpressions;
using System.Xml;
using System.Text;
using MessagePack;
namespace AssetStudio
{
@@ -16,6 +17,7 @@ namespace AssetStudio
{
public const string MapName = "Maps";
public static bool Minimal = true;
public static CancellationTokenSource tokenSource = new CancellationTokenSource();
private static string BaseFolder = "";
@@ -34,7 +36,7 @@ namespace AssetStudio
{
Directory.CreateDirectory(MapName);
var files = Directory.GetFiles(MapName, "*.bin", SearchOption.TopDirectoryOnly);
return files.Select(x => Path.GetFileNameWithoutExtension(x)).ToArray();
return files.Select(Path.GetFileNameWithoutExtension).ToArray();
}
public static void Clear()
@@ -132,12 +134,11 @@ namespace AssetStudio
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
Dependencies = assetsFile.m_Externals.Select(x => x.fileName).ToArray()
};
if (CABMap.ContainsKey(assetsFile.fileName))
@@ -192,17 +193,16 @@ namespace AssetStudio
var path = reader.ReadString();
var offset = reader.ReadInt64();
var depCount = reader.ReadInt32();
var dependencies = new List<string>();
var dependencies = new string[depCount];
for (int j = 0; j < depCount; j++)
{
var dependancy = reader.ReadString();
dependencies.Add(dependancy);
dependencies[j] = reader.ReadString();
}
var entry = new Entry()
{
Path = path,
Offset = offset,
Dependencies = dependencies.ToArray()
Dependencies = dependencies
};
CABMap.Add(cab, entry);
}
@@ -230,7 +230,7 @@ namespace AssetStudio
UpdateContainers(assets, game);
ExportAssetsMap(assets.ToArray(), mapName, savePath, exportListType, resetEvent);
ExportAssetsMap(assets.ToArray(), game, mapName, savePath, exportListType, resetEvent);
}
catch(Exception e)
{
@@ -283,13 +283,13 @@ namespace AssetStudio
}
obj = null;
asset.Name = assetBundle.m_Name;
exportable = false;
exportable = !Minimal;
break;
case ClassIDType.GameObject:
var gameObject = new GameObject(objectReader);
obj = gameObject;
asset.Name = gameObject.m_Name;
exportable = false;
exportable = !Minimal;
break;
case ClassIDType.Shader when Shader.Parsable:
asset.Name = objectReader.ReadAlignedString();
@@ -306,7 +306,6 @@ namespace AssetStudio
case ClassIDType.MiHoYoBinData:
var MiHoYoBinData = new MiHoYoBinData(objectReader);
obj = MiHoYoBinData;
exportable = true;
break;
case ClassIDType.IndexObject:
var indexObject = new IndexObject(objectReader);
@@ -316,6 +315,7 @@ namespace AssetStudio
mihoyoBinDataNames.Add((index.Value.Object, index.Key));
}
asset.Name = "IndexObject";
exportable = !Minimal;
break;
case ClassIDType.Font:
case ClassIDType.Material:
@@ -329,7 +329,8 @@ namespace AssetStudio
asset.Name = objectReader.ReadAlignedString();
break;
default:
exportable = false;
asset.Name = objectReader.type.ToString();
exportable = !Minimal;
break;
}
}
@@ -428,7 +429,7 @@ namespace AssetStudio
}
}
private static void ExportAssetsMap(AssetEntry[] toExportAssets, string name, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null)
private static void ExportAssetsMap(AssetEntry[] toExportAssets, Game game, string name, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null)
{
ThreadPool.QueueUserWorkItem(state =>
{
@@ -436,13 +437,12 @@ namespace AssetStudio
Progress.Reset();
string filename;
string filename = Path.Combine(savePath, $"{name}{exportListType.GetExtension()}");
switch (exportListType)
{
case ExportListType.XML:
filename = Path.Combine(savePath, $"{name}.xml");
var settings = new XmlWriterSettings() { Indent = true };
using (XmlWriter writer = XmlWriter.Create(filename, settings))
var xmlSettings = new XmlWriterSettings() { Indent = true };
using (XmlWriter writer = XmlWriter.Create(filename, xmlSettings))
{
writer.WriteStartDocument();
writer.WriteStartElement("Assets");
@@ -466,7 +466,6 @@ namespace AssetStudio
}
break;
case ExportListType.JSON:
filename = Path.Combine(savePath, $"{name}.json");
using (StreamWriter file = File.CreateText(filename))
{
var serializer = new JsonSerializer() { Formatting = Newtonsoft.Json.Formatting.Indented };
@@ -474,10 +473,20 @@ namespace AssetStudio
serializer.Serialize(file, toExportAssets);
}
break;
case ExportListType.MessagePack:
using (var file = File.Create(filename))
{
var assetMap = new AssetMap
{
GameType = game.Type,
AssetEntries = toExportAssets
};
MessagePackSerializer.Serialize(file, assetMap, MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray));
}
break;
}
Logger.Info($"Finished exporting asset list with {toExportAssets.Length} items.");
Logger.Info($"AssetMap build successfully !!");
Logger.Info($"Finished buidling AssetMap with {toExportAssets.Length} assets.");
resetEvent?.Set();
});
@@ -501,7 +510,7 @@ namespace AssetStudio
DumpCABMap(mapName);
Logger.Info($"Map build successfully !! {collision} collisions found");
ExportAssetsMap(assets.ToArray(), mapName, savePath, exportListType, resetEvent);
ExportAssetsMap(assets.ToArray(), game, mapName, savePath, exportListType, resetEvent);
}
}
}

View File

@@ -3,7 +3,8 @@
public enum ExportListType
{
XML,
JSON
JSON,
MessagePack
}
public static class ExportListTypeExtensions
@@ -12,6 +13,7 @@
{
ExportListType.XML => ".xml",
ExportListType.JSON => ".json",
ExportListType.MessagePack => ".map",
_ => throw new System.NotImplementedException(),
};
}

View File

@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace AssetStudio
@@ -86,38 +85,4 @@ namespace AssetStudio
return string.Empty;
}
}
public record AssetIndex
{
public Dictionary<string, string> Types { get; set; }
public record SubAssetInfo
{
public string Name { get; set; }
public byte PathHashPre { get; set; }
public uint PathHashLast { get; set; }
}
public Dictionary<int, List<SubAssetInfo>> SubAssets { get; set; }
public Dictionary<int, List<int>> Dependencies { get; set; }
public List<uint> PreloadBlocks { get; set; }
public List<uint> PreloadShaderBlocks { get; set; }
public record BlockInfo
{
public byte Language { get; set; }
public uint Id { get; set; }
public uint Offset { get; set; }
}
public Dictionary<int, BlockInfo> Assets { get; set; }
public List<uint> SortList { get; set; }
public AssetIndex()
{
Types = new Dictionary<string, string>();
SubAssets = new Dictionary<int, List<SubAssetInfo>>();
Dependencies = new Dictionary<int, List<int>>();
PreloadBlocks = new List<uint>();
PreloadShaderBlocks = new List<uint>();
Assets = new Dictionary<int, BlockInfo>();
SortList = new List<uint>();
}
}
}

View File

@@ -0,0 +1,37 @@
using MessagePack;
using System;
using System.IO;
namespace AssetStudio
{
public static class ResourceMap
{
private static AssetMap Instance;
public static AssetEntry[] GetEntries() => Instance.AssetEntries;
public static void FromFile(string path)
{
if (!string.IsNullOrEmpty(path))
{
Logger.Info(string.Format("Parsing...."));
try
{
using var stream = File.OpenRead(path);
Instance = MessagePackSerializer.Deserialize<AssetMap>(stream, MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray));
}
catch (Exception e)
{
Logger.Error("AssetMap was not loaded");
Console.WriteLine(e.ToString());
return;
}
Logger.Info("Loaded !!");
}
}
public static void Clear()
{
Instance.GameType = GameType.Normal;
Instance.AssetEntries = null;
}
}
}