From cebccbf8e9d676f191a3855c6a573dc232b441d5 Mon Sep 17 00:00:00 2001 From: parzivail Date: Mon, 2 Aug 2021 20:44:45 -0400 Subject: [PATCH] add mesh prop searching commands --- DumpTool/DumpMeshPropsCommand.cs | 4 +- DumpTool/FindAllMeshPropsCommand.cs | 65 ++++++++++++++++++ DumpTool/FindAllMeshPropsGlobalCommand.cs | 38 +++++++++++ DumpTool/Program.cs | 4 +- RainbowForge/BinaryReaderExt.cs | 2 +- RainbowForge/Dump/DumpHelper.cs | 83 +++++++++++++++++++++++ 6 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 DumpTool/FindAllMeshPropsCommand.cs create mode 100644 DumpTool/FindAllMeshPropsGlobalCommand.cs diff --git a/DumpTool/DumpMeshPropsCommand.cs b/DumpTool/DumpMeshPropsCommand.cs index e27f752..f893d14 100644 --- a/DumpTool/DumpMeshPropsCommand.cs +++ b/DumpTool/DumpMeshPropsCommand.cs @@ -57,11 +57,11 @@ public static void ProcessFlatArchive(ILiteDatabase db, Forge forge, Entry entry return; } - foreach (var meshProp in arc.Entries.Where(archiveEntry => MagicHelper.Equals(Magic.MeshProperties, archiveEntry.Meta.Magic))) + foreach (var meshProp in arc.Entries) { var unresolvedExterns = new List(); - var outputDir = Path.Combine(rootOutputDir, $"model_flatarchive_id{entry.Uid}", $"meshprop_{meshProp.Meta.Uid}"); + var outputDir = Path.Combine(rootOutputDir, $"model_flatarchive_id{entry.Uid}", $"{(Magic) meshProp.Meta.Magic}_{meshProp.Meta.Uid}"); Directory.CreateDirectory(outputDir); DumpHelper.DumpNonContainerChildren(outputDir, assetStream, arc, meshProp, unresolvedExterns); diff --git a/DumpTool/FindAllMeshPropsCommand.cs b/DumpTool/FindAllMeshPropsCommand.cs new file mode 100644 index 0000000..11dc128 --- /dev/null +++ b/DumpTool/FindAllMeshPropsCommand.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using CommandLine; +using RainbowForge; +using RainbowForge.Archive; +using RainbowForge.Dump; +using RainbowForge.Forge; +using RainbowForge.Forge.Container; + +namespace DumpTool +{ + [Verb("findallmeshprops", HelpText = "Find all MeshProperties containers which reference the given UID in all flat archives in the given forge file")] + public class FindAllMeshPropsCommand + { + [Value(0, HelpText = "The forge file to reference")] + public string ForgeFilename { get; set; } + + [Value(1, HelpText = "The UID to search for")] + public ulong Uid { get; set; } + + public static void Run(FindAllMeshPropsCommand args) + { + try + { + var forge = Program.GetForge(args.ForgeFilename); + foreach (var entry in forge.Entries) + if (SearchFlatArchive(forge, entry, args.Uid)) + Console.WriteLine(entry.Uid); + } + catch (Exception e) + { + Console.Error.WriteLine($"Error while dumping: {e}"); + } + } + + public static bool SearchFlatArchive(Forge forge, Entry entry, ulong uid) + { + var container = forge.GetContainer(entry.Uid); + if (container is not ForgeAsset forgeAsset || MagicHelper.GetFiletype(entry.Name.FileType) != AssetType.FlatArchive) + return false; + + var assetStream = forgeAsset.GetDataStream(forge); + var arc = FlatArchive.Read(assetStream); + + foreach (var meshProp in arc.Entries) + { + var unresolvedExterns = new List(); + + try + { + DumpHelper.SearchNonContainerChildren(assetStream, arc, meshProp, unresolvedExterns); + } + catch (NotSupportedException) + { + continue; + } + + if (unresolvedExterns.Contains(uid)) + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/DumpTool/FindAllMeshPropsGlobalCommand.cs b/DumpTool/FindAllMeshPropsGlobalCommand.cs new file mode 100644 index 0000000..68a480b --- /dev/null +++ b/DumpTool/FindAllMeshPropsGlobalCommand.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using CommandLine; + +namespace DumpTool +{ + [Verb("findallmeshpropsglobal", HelpText = "Find all MeshProperties containers which reference the given UID in all flat archives in the given forge file")] + public class FindAllMeshPropsGlobalCommand + { + [Value(0, HelpText = "The directory of forge files to search")] + public string ForgeDirectory { get; set; } + + [Value(1, HelpText = "The UID to search for")] + public ulong Uid { get; set; } + + public static void Run(FindAllMeshPropsGlobalCommand args) + { + Program.AssertDirectoryExists(args.ForgeDirectory); + + foreach (var file in Directory.GetFiles(args.ForgeDirectory, "*.forge")) + try + { + var forge = Program.GetForge(file); + for (var i = 0; i < forge.Entries.Length; i++) + { + var entry = forge.Entries[i]; + + if (FindAllMeshPropsCommand.SearchFlatArchive(forge, entry, args.Uid)) + Console.WriteLine($"{Path.GetFileName(file)}: {entry.Uid}"); + } + } + catch (Exception e) + { + Console.Error.WriteLine($"Error while dumping: {e}"); + } + } + } +} \ No newline at end of file diff --git a/DumpTool/Program.cs b/DumpTool/Program.cs index d2533a5..672e889 100644 --- a/DumpTool/Program.cs +++ b/DumpTool/Program.cs @@ -33,7 +33,7 @@ public static Forge GetForge(string filename) private static void Main(string[] args) { Parser.Default.ParseArguments(args) + DumpAllCommand, DumpAllMeshPropsCommand, FindAllMeshPropsCommand, FindAllMeshPropsGlobalCommand>(args) .WithParsed(ListCommand.Run) .WithParsed(FindCommand.Run) .WithParsed(InspectCommand.Run) @@ -41,6 +41,8 @@ private static void Main(string[] args) .WithParsed(DumpAllCommand.Run) .WithParsed(DumpMeshPropsCommand.Run) .WithParsed(DumpAllMeshPropsCommand.Run) + .WithParsed(FindAllMeshPropsCommand.Run) + .WithParsed(FindAllMeshPropsGlobalCommand.Run) .WithParsed(IndexCommand.Run); } } diff --git a/RainbowForge/BinaryReaderExt.cs b/RainbowForge/BinaryReaderExt.cs index 38d9824..812e0bf 100644 --- a/RainbowForge/BinaryReaderExt.cs +++ b/RainbowForge/BinaryReaderExt.cs @@ -16,7 +16,7 @@ public static Vector3 ReadVector3(this BinaryReader r) public static Vector3 ReadUInt64AsPos(this BinaryReader r) { - const float bias = 0x777F; + const float bias = 0x7FFF; var x = r.ReadUInt16(); var y = r.ReadUInt16(); diff --git a/RainbowForge/Dump/DumpHelper.cs b/RainbowForge/Dump/DumpHelper.cs index c02b303..bb797ee 100644 --- a/RainbowForge/Dump/DumpHelper.cs +++ b/RainbowForge/Dump/DumpHelper.cs @@ -242,5 +242,88 @@ void TryRecurseChildren(string dir, ulong uid) } } } + + public static void SearchNonContainerChildren(BinaryReader assetStream, FlatArchive arc, FlatArchiveEntry entry, List referencedExterns) + { + if (!referencedExterns.Contains(entry.Meta.Uid)) + referencedExterns.Add(entry.Meta.Uid); + + void TryRecurseChildren(ulong uid) + { + if (uid == 0) + return; + + if (!referencedExterns.Contains(uid)) + referencedExterns.Add(uid); + + var arcEntry = arc.Entries.FirstOrDefault(archiveEntry => archiveEntry.Meta.Uid == uid); + if (arcEntry != null) + SearchNonContainerChildren(assetStream, arc, arcEntry, referencedExterns); + } + + assetStream.BaseStream.Seek(entry.PayloadOffset, SeekOrigin.Begin); + switch ((Magic) entry.Meta.Magic) + { + case Magic.MaterialContainer: + { + var mat = MaterialContainer.Read(assetStream); + foreach (var mipContainerReference in mat.BaseMipContainers) + TryRecurseChildren(mipContainerReference.MipContainerUid); + foreach (var mipContainerReference in mat.SecondaryMipContainers) + TryRecurseChildren(mipContainerReference.MipContainerUid); + foreach (var mipContainerReference in mat.TertiaryMipContainers) + TryRecurseChildren(mipContainerReference.MipContainerUid); + + break; + } + case Magic.MipContainer: + { + var mipContainer = MipContainer.Read(assetStream); + TryRecurseChildren(mipContainer.MipUid); + break; + } + case Magic.MeshProperties: + { + var meshProps = MeshProperties.Read(assetStream); + TryRecurseChildren(meshProps.MeshUid); + + foreach (var materialContainer in meshProps.MaterialContainers) + TryRecurseChildren(materialContainer); + break; + } + case Magic.MipSet: + { + var mipSet = MipSet.Read(assetStream); + foreach (var uid in mipSet.TexUidMipSet1.Where(arg => arg != 0 && !referencedExterns.Contains(arg))) + referencedExterns.Add(uid); + foreach (var uid in mipSet.TexUidMipSet2.Where(arg => arg != 0 && !referencedExterns.Contains(arg))) + referencedExterns.Add(uid); + break; + } + // The root entry is a UidLinkContainer + case Magic.FlatArchive1: + case Magic.FlatArchive2: + case Magic.FlatArchive3: + { + var linkContainer = UidLinkContainer.Read(assetStream, entry.Meta.Var1); + foreach (var linkEntry in linkContainer.UidLinkEntries) + { + if (linkEntry.UidLinkNode1 != null) + { + var uid = linkEntry.UidLinkNode1.LinkedUid; + TryRecurseChildren(uid); + } + + if (linkEntry.UidLinkNode2 != null) + { + var uid = linkEntry.UidLinkNode2.LinkedUid; + TryRecurseChildren(uid); + } + } + + break; + } + } + } } } \ No newline at end of file