From 5fc9db676eb2e59bbda44bf2f8c88f59f413eed2 Mon Sep 17 00:00:00 2001 From: "Nathan V." Date: Fri, 11 Jul 2025 02:21:07 -0400 Subject: [PATCH 01/10] begin ResourceImport implementation --- .../Resources/InstanceList/InstanceList.cs | 8 +++--- Volatility/Resources/Model/Model.cs | 9 +++---- Volatility/Resources/ResourceImport.cs | 7 +++++ Volatility/Types.cs | 26 ++----------------- 4 files changed, 16 insertions(+), 34 deletions(-) create mode 100644 Volatility/Resources/ResourceImport.cs diff --git a/Volatility/Resources/InstanceList/InstanceList.cs b/Volatility/Resources/InstanceList/InstanceList.cs index 1f00e5a..cefc642 100644 --- a/Volatility/Resources/InstanceList/InstanceList.cs +++ b/Volatility/Resources/InstanceList/InstanceList.cs @@ -67,10 +67,10 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann // Padding1 = _padding1, Padding2 = _padding2, MaxVisibleDistanceSquared = _maxVisibleDistanceSquared, Transform = _transform, - ResourceId = new ResourceID + ResourceId = new ResourceImport { - ID = reader.ReadBytes(4), - Endian = reader.GetEndianness() + ReferenceID = reader.ReadUInt32(), + ExternalImport = false }, }); } @@ -80,7 +80,7 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann public struct Instance { [EditorLabel("Resource ID"), EditorCategory("InstanceList/Instances"), EditorTooltip("The reference to the resource placed by this instance.")] - public ResourceID ResourceId; + public ResourceImport ResourceId; [EditorLabel("Transform"), EditorCategory("InstanceList/Instances"), EditorTooltip("The location, rotation, and scale of this instance.")] public Transform Transform; diff --git a/Volatility/Resources/Model/Model.cs b/Volatility/Resources/Model/Model.cs index 175bf0c..8f1157b 100644 --- a/Volatility/Resources/Model/Model.cs +++ b/Volatility/Resources/Model/Model.cs @@ -60,8 +60,7 @@ public override void WriteToStream(EndianAwareBinaryWriter writer, Endian endian // Resource ID References for (int i = 0; i < models; i++) { - writer.Write(ModelDatas[i].ResourceReference.Endian == Endian.BE ? new byte[4] : ModelDatas[i].ResourceReference.ID); - writer.Write(ModelDatas[i].ResourceReference.Endian == Endian.LE ? new byte[4] : ModelDatas[i].ResourceReference.ID); + writer.Write(ModelDatas[i].ResourceReference.ReferenceID); writer.Write(renderablesPtr + (i * 0x4)); writer.Write((uint)0x0); // Unknown. Always 0 in BPR, not always 0 on X360 } @@ -120,9 +119,7 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann (reader.GetEndianness() == Endian.BE ? 0x4 : 0x0), SeekOrigin.Begin ); - modelData.ResourceReference.ID = reader.ReadBytes(4); - modelData.ResourceReference.Endian = reader.GetEndianness(); - + modelData.ResourceReference.ReferenceID = reader.ReadUInt32(); ModelDatas.Add(modelData); } } @@ -134,7 +131,7 @@ public Model(string path, Endian endianness = Endian.Agnostic) : base(path, endi public struct ModelData { [EditorCategory("Model Data"), EditorLabel("Resource Reference")] - public ResourceID ResourceReference; + public ResourceImport ResourceReference; [EditorCategory("Model Data"), EditorLabel("Model State")] public State State; diff --git a/Volatility/Resources/ResourceImport.cs b/Volatility/Resources/ResourceImport.cs new file mode 100644 index 0000000..0265ca0 --- /dev/null +++ b/Volatility/Resources/ResourceImport.cs @@ -0,0 +1,7 @@ +namespace Volatility.Resources; + +public struct ResourceImport +{ + public ResourceID ReferenceID; + public bool ExternalImport; +} diff --git a/Volatility/Types.cs b/Volatility/Types.cs index 62126e1..1b813d6 100644 --- a/Volatility/Types.cs +++ b/Volatility/Types.cs @@ -8,6 +8,7 @@ global using Vector4 = System.Numerics.Vector4; global using Quaternion = System.Numerics.Quaternion; global using Matrix44Affine = System.Numerics.Matrix4x4; +global using ResourceID = System.UInt64; // Volatilty Types global using ColorRGB = System.Numerics.Vector3; @@ -19,27 +20,4 @@ public struct Transform public Vector3 Location; public Quaternion Rotation; public Vector3 Scale; -} - -// Experimenting with a new way to store ResourceIDs. -public struct ResourceID -{ - [Newtonsoft.Json.JsonIgnore] - public byte[] ID; - - public string HexID - { - get => BitConverter.ToString(ID).Replace("-", "").ToLower(); - set => ID = Enumerable.Range(0, value.Length / 2) - .Select(x => Convert.ToByte(value.Substring(x * 2, 2), 16)) - .ToArray(); - } - - public Endian Endian; - - public ResourceID() - { - ID = new byte[4]; - Endian = default; - } -} +} \ No newline at end of file From 979dea33ee6ac2bd4a99742b553bd1577766478c Mon Sep 17 00:00:00 2001 From: "Nathan V." Date: Fri, 11 Jul 2025 03:33:32 -0400 Subject: [PATCH 02/10] implement ReadExternalImport for unpacked resources --- Volatility/Resources/ResourceImport.cs | 58 +++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/Volatility/Resources/ResourceImport.cs b/Volatility/Resources/ResourceImport.cs index 0265ca0..80f136f 100644 --- a/Volatility/Resources/ResourceImport.cs +++ b/Volatility/Resources/ResourceImport.cs @@ -1,7 +1,61 @@ -namespace Volatility.Resources; +using YamlDotNet.Serialization; + +namespace Volatility.Resources; public struct ResourceImport { public ResourceID ReferenceID; public bool ExternalImport; -} + + public static bool ReadExternalImport(byte index, EndianAwareBinaryReader reader, long importBlockOffset, out ResourceImport resourceImport) + { + resourceImport.ExternalImport = true; + + // In-resource imports block + if (reader.BaseStream.Length >= importBlockOffset + (0x10 * index) + 0x10) + { + long originalPosition = reader.BaseStream.Position; + + reader.BaseStream.Seek(importBlockOffset + (0x10 * index), SeekOrigin.Begin); + + resourceImport.ReferenceID = reader.ReadUInt64(); + + reader.BaseStream.Seek(originalPosition, SeekOrigin.Begin); + + return true; + } + // YAP imports yaml + else if (reader.BaseStream is FileStream fs) + { + string baseName = Path.GetFileNameWithoutExtension(fs.Name); + + string directory = Path.GetDirectoryName(fs.Name); + + resourceImport.ReferenceID = GetYAMLImportValueAt(Path.Combine(directory, baseName + "_imports.yaml"), index); + + return true; + } + + resourceImport = default; + return false; + } + + + public static ResourceID GetYAMLImportValueAt(string yamlPath, byte index) + { + var yaml = File.ReadAllText(yamlPath); + var deser = new DeserializerBuilder().Build(); + + var list = deser + .Deserialize>>(yaml) + ?? throw new InvalidDataException("Expected a YAML sequence of mappings."); + + if (index < 0 || index >= list.Count) + throw new ArgumentOutOfRangeException(nameof(index), $"Valid range 0–{list.Count - 1}"); + + var kv = list[index].Values.GetEnumerator(); + kv.MoveNext(); + return Convert.ToUInt32(kv.Current, 16); + } +}; + From 35678fc561d419b897d19fddcd71ca9a5d1a8548 Mon Sep 17 00:00:00 2001 From: "Nathan V." Date: Fri, 11 Jul 2025 03:53:07 -0400 Subject: [PATCH 03/10] Use ResourceImport for EnvironmentKeyframe ColorCube reference --- .../Resources/EnvironmentKeyframe/EnvironmentKeyframe.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Volatility/Resources/EnvironmentKeyframe/EnvironmentKeyframe.cs b/Volatility/Resources/EnvironmentKeyframe/EnvironmentKeyframe.cs index dc32b6f..cda1145 100644 --- a/Volatility/Resources/EnvironmentKeyframe/EnvironmentKeyframe.cs +++ b/Volatility/Resources/EnvironmentKeyframe/EnvironmentKeyframe.cs @@ -156,10 +156,13 @@ public VignetteData(ResourceBinaryReader reader) public struct TintData { - public ulong ColorCubePtr; // TODO: Update with new ResourceID system + public ResourceImport ColorCubeReference; public TintData(ResourceBinaryReader reader, Arch arch) { - ColorCubePtr = (arch == Arch.x64 ? reader.ReadUInt64() : reader.ReadUInt32()); + ColorCubeReference.ReferenceID = (arch == Arch.x64 ? reader.ReadUInt64() : reader.ReadUInt32()); + + if (ResourceImport.ReadExternalImport(0, reader, 0x240, out ResourceImport ExternalReference)) + ColorCubeReference = ExternalReference; } } From fe09874989e78da7fcd9e5d1699a0ef1fc6df1f8 Mon Sep 17 00:00:00 2001 From: "Nathan V." Date: Fri, 11 Jul 2025 03:52:09 -0400 Subject: [PATCH 04/10] ResourceID refactor, update unpacker detection --- Volatility/CLI/Commands/AutotestCommand.cs | 10 ++--- Volatility/Resources/Resource.cs | 42 ++++++++++----------- Volatility/Utilities/ResourceIDUtilities.cs | 27 ++++++++++++- 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/Volatility/CLI/Commands/AutotestCommand.cs b/Volatility/CLI/Commands/AutotestCommand.cs index 4ca0d92..ba4334d 100644 --- a/Volatility/CLI/Commands/AutotestCommand.cs +++ b/Volatility/CLI/Commands/AutotestCommand.cs @@ -48,7 +48,7 @@ public async Task Execute() TexturePC textureHeaderPC = new TexturePC { AssetName = "autotest_header_PC", - ResourceID = GetResourceIDFromName("autotest_header_PC", Endian.LE), + ResourceID = GetResourceIDFromName("autotest_header_PC"), Format = D3DFORMAT.D3DFMT_DXT1, Width = 1024, Height = 512, @@ -62,7 +62,7 @@ public async Task Execute() TextureBPR textureHeaderBPR = new TextureBPR { AssetName = "autotest_header_BPR", - ResourceID = GetResourceIDFromName("autotest_header_BPR", Endian.LE), + ResourceID = GetResourceIDFromName("autotest_header_BPR"), Format = DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM, Width = 1024, Height = 512, @@ -77,7 +77,7 @@ public async Task Execute() textureHeaderBPR.SetResourceArch(Arch.x64); textureHeaderBPR.AssetName = "autotest_header_BPRx64"; - textureHeaderBPR.ResourceID = GetResourceIDFromName(textureHeaderBPR.AssetName, Endian.LE); + textureHeaderBPR.ResourceID = GetResourceIDFromName(textureHeaderBPR.AssetName); // Write 64 bit test BPR header TestHeaderRW("autotest_header_BPRx64.dat", textureHeaderBPR); @@ -86,7 +86,7 @@ public async Task Execute() TexturePS3 textureHeaderPS3 = new TexturePS3 { AssetName = "autotest_header_PS3", - ResourceID = GetResourceIDFromName("autotest_header_PS3", Endian.BE), + ResourceID = GetResourceIDFromName("autotest_header_PS3"), Format = CELL_GCM_COLOR_FORMAT.CELL_GCM_TEXTURE_COMPRESSED_DXT45, Width = 1024, Height = 512, @@ -100,7 +100,7 @@ public async Task Execute() TextureX360 textureHeaderX360 = new TextureX360 { AssetName = "autotest_header_X360", - ResourceID = GetResourceIDFromName("autotest_header_X360", Endian.BE), + ResourceID = GetResourceIDFromName("autotest_header_X360"), Format = new GPUTEXTURE_FETCH_CONSTANT { Tiled = true, diff --git a/Volatility/Resources/Resource.cs b/Volatility/Resources/Resource.cs index b46e645..c412cc5 100644 --- a/Volatility/Resources/Resource.cs +++ b/Volatility/Resources/Resource.cs @@ -5,7 +5,7 @@ namespace Volatility.Resources; public abstract class Resource { [EditorCategory("Resource Info"), EditorLabel("Resource ID"), EditorTooltip("The CRC32 ID used to identify the resource in the game engine.")] - public string ResourceID = ""; + public ResourceID ResourceID = 0x00000000; [EditorCategory("Resource Info"), EditorLabel("Asset Name"), EditorTooltip("The asset's name in the resource depot.")] public string AssetName = "invalid"; @@ -68,30 +68,33 @@ public Resource(string path, Endian endianness = Endian.Agnostic) Unpacker = GetUnpackerFromFileName(Path.GetFileName(ImportedFileName)); if (Unpacker != Unpacker.Raw) { + int idx = name.LastIndexOf('_'); name = Unpacker switch { Unpacker.DGI => name.Replace("_", ""), - Unpacker.Bnd2Manager => name.Substring(0, name.LastIndexOf('_')), - Unpacker.YAP => name.Substring(0, name.LastIndexOf('_')) + Unpacker.Bnd2Manager or Unpacker.YAP when idx > 0 => name[..idx], + Unpacker.Bnd2Manager or Unpacker.YAP => name, + _ => name }; } if (ValidateResourceID(name)) { // We store ResourceIDs how BE platforms do to be consistent with the original console releases. // This makes it easy to cross reference assets between all platforms. - ResourceID = (importEndianness == Endian.LE && Unpacker != Unpacker.YAP) + ResourceID = Convert.ToUInt64((importEndianness == Endian.LE && Unpacker != Unpacker.YAP) ? FlipResourceIDEndian(name) - : name; + : name + , 16); string newName = GetNameByResourceID(ResourceID, GetResourceType().ToString()); AssetName = !string.IsNullOrEmpty(newName) ? newName - : ResourceID; + : ResourceID.ToString("X8"); } else { // TODO: Add new entry to ResourceDB - ResourceID = GetResourceIDFromName(name, importEndianness); + ResourceID = Convert.ToUInt64(GetResourceIDFromName(name, importEndianness), 16); AssetName = name; } @@ -105,23 +108,18 @@ public Resource(string path, Endian endianness = Endian.Agnostic) private static Unpacker GetUnpackerFromFileName(string filename) { - if (filename.EndsWith("_1.bin")) // bnd2-manager + var name = Path.GetFileName(filename); + return name switch { - return Unpacker.Bnd2Manager; - } - else if (filename.EndsWith("_primary.dat")) // YAP - { - return Unpacker.YAP; - } - else if (filename.EndsWith(".dat")) // DGI - { - return Unpacker.DGI; - } - return Unpacker.Raw; - - // Volatility doesn't have a bundle unpacker yet... + var n when n.EndsWith("_1.bin", StringComparison.OrdinalIgnoreCase) => Unpacker.Bnd2Manager, + var n when n.EndsWith("_primary.dat", StringComparison.OrdinalIgnoreCase) => Unpacker.YAP, + var n when n.EndsWith(".dat", StringComparison.OrdinalIgnoreCase) + && n.Count(c => c == '_') == 3 => Unpacker.DGI, + var n when n.EndsWith(".dat", StringComparison.OrdinalIgnoreCase) => Unpacker.YAP, + _ => Unpacker.Raw, + }; } - + public virtual void PushAll() { } public virtual void PullAll() { } } diff --git a/Volatility/Utilities/ResourceIDUtilities.cs b/Volatility/Utilities/ResourceIDUtilities.cs index bd02142..69e9c89 100644 --- a/Volatility/Utilities/ResourceIDUtilities.cs +++ b/Volatility/Utilities/ResourceIDUtilities.cs @@ -1,4 +1,5 @@ -using System.IO.Hashing; +using System.Buffers.Binary; +using System.IO.Hashing; using System.Text; using Newtonsoft.Json; @@ -107,6 +108,25 @@ public static string GetNameByResourceID(string id, string type) return ""; } + public static string GetNameByResourceID(ResourceID id, string type) + { + string path = Path.Combine + ( + Directory.GetCurrentDirectory(), + "data", + "ResourceDB", + $"{type}.json" + ); + + if (File.Exists(path)) + { + Dictionary? data = JsonConvert.DeserializeObject>(File.ReadAllText(path)); + + return data.TryGetValue($"{id.ToString():X8}", out string? value) ? value : ""; + } + + return ""; + } public static string GetResourceIDFromName(string name, Endian endian = Endian.LE) { @@ -119,4 +139,9 @@ public static string GetResourceIDFromName(string name, Endian endian = Endian.L return BitConverter.ToString(hash).Replace("-", "_").ToUpper(); } + + public static ResourceID GetResourceIDFromName(string name) + { + return BinaryPrimitives.ReadUInt32BigEndian(Crc32.Hash(Encoding.UTF8.GetBytes(name.ToLower()))); + } } From 9ed257492a7ab9d7c22d08b50920c9ad75696a4d Mon Sep 17 00:00:00 2001 From: "Nathan V." Date: Fri, 11 Jul 2025 04:06:16 -0400 Subject: [PATCH 05/10] Update ResourceImport.cs --- Volatility/Resources/ResourceImport.cs | 31 +++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/Volatility/Resources/ResourceImport.cs b/Volatility/Resources/ResourceImport.cs index 80f136f..12bcf16 100644 --- a/Volatility/Resources/ResourceImport.cs +++ b/Volatility/Resources/ResourceImport.cs @@ -4,21 +4,42 @@ namespace Volatility.Resources; public struct ResourceImport { + // The idea here is that if the name is populated but + // the ID is empty, the name will be calculated into an ID + // on export. If both a name and ID exist, use the ID, as + // this will keep consistency for imported assets. If you + // want to use the calculated name, clear the ReferenceID field. + public string Name; public ResourceID ReferenceID; public bool ExternalImport; - public static bool ReadExternalImport(byte index, EndianAwareBinaryReader reader, long importBlockOffset, out ResourceImport resourceImport) + public ResourceImport() { } + + public ResourceImport(ResourceID id, bool externalImport = false, bool useCalculatedName = false) + { + ReferenceID = id; + ExternalImport = externalImport; + // TODO: find name in ResourceDB, pending ResourceDB v3 + // if (useCalculatedName || resource was found in db) + // ReferenceID = 0x0; + } + + public ResourceImport(string name, bool externalImport = false) { - resourceImport.ExternalImport = true; + Name = name; + ExternalImport = externalImport; + } + public static bool ReadExternalImport(byte index, EndianAwareBinaryReader reader, long importBlockOffset, out ResourceImport resourceImport) + { // In-resource imports block if (reader.BaseStream.Length >= importBlockOffset + (0x10 * index) + 0x10) { long originalPosition = reader.BaseStream.Position; reader.BaseStream.Seek(importBlockOffset + (0x10 * index), SeekOrigin.Begin); - - resourceImport.ReferenceID = reader.ReadUInt64(); + + resourceImport = new ResourceImport(reader.ReadUInt64(), externalImport: true); reader.BaseStream.Seek(originalPosition, SeekOrigin.Begin); @@ -31,7 +52,7 @@ public static bool ReadExternalImport(byte index, EndianAwareBinaryReader reader string directory = Path.GetDirectoryName(fs.Name); - resourceImport.ReferenceID = GetYAMLImportValueAt(Path.Combine(directory, baseName + "_imports.yaml"), index); + resourceImport = new ResourceImport(GetYAMLImportValueAt(Path.Combine(directory, baseName + "_imports.yaml"), index), externalImport: true); return true; } From 3eb694736c4c54896bbb8d16f2401021b1f0f35d Mon Sep 17 00:00:00 2001 From: "Nathan V." Date: Fri, 11 Jul 2025 04:13:20 -0400 Subject: [PATCH 06/10] make ResourceID an explicit type --- Volatility/Resources/Resource.cs | 2 +- Volatility/Types.cs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Volatility/Resources/Resource.cs b/Volatility/Resources/Resource.cs index c412cc5..2eb5784 100644 --- a/Volatility/Resources/Resource.cs +++ b/Volatility/Resources/Resource.cs @@ -89,7 +89,7 @@ public Resource(string path, Endian endianness = Endian.Agnostic) string newName = GetNameByResourceID(ResourceID, GetResourceType().ToString()); AssetName = !string.IsNullOrEmpty(newName) ? newName - : ResourceID.ToString("X8"); + : ResourceID.ToString(); } else { diff --git a/Volatility/Types.cs b/Volatility/Types.cs index 1b813d6..05ec211 100644 --- a/Volatility/Types.cs +++ b/Volatility/Types.cs @@ -8,7 +8,6 @@ global using Vector4 = System.Numerics.Vector4; global using Quaternion = System.Numerics.Quaternion; global using Matrix44Affine = System.Numerics.Matrix4x4; -global using ResourceID = System.UInt64; // Volatilty Types global using ColorRGB = System.Numerics.Vector3; @@ -20,4 +19,13 @@ public struct Transform public Vector3 Location; public Quaternion Rotation; public Vector3 Scale; +} + +public readonly struct ResourceID +{ + public readonly ulong Value; + public ResourceID(ulong v) => Value = v; + public static implicit operator ulong(ResourceID r) => r.Value; + public static implicit operator ResourceID(ulong v) => new ResourceID(v); + public override string ToString() => $"{Value:X8}"; } \ No newline at end of file From 4ea3f2348051e7225c1342b320a8c7f2b931f420 Mon Sep 17 00:00:00 2001 From: "Nathan V." Date: Fri, 11 Jul 2025 04:18:06 -0400 Subject: [PATCH 07/10] implement ResourceID yaml type converter --- .../CLI/Commands/ImportResourceCommand.cs | 5 +-- .../YAML/ResourceIDYamlTypeConverter.cs | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 Volatility/Utilities/YAML/ResourceIDYamlTypeConverter.cs diff --git a/Volatility/CLI/Commands/ImportResourceCommand.cs b/Volatility/CLI/Commands/ImportResourceCommand.cs index 2f103d6..58a04be 100644 --- a/Volatility/CLI/Commands/ImportResourceCommand.cs +++ b/Volatility/CLI/Commands/ImportResourceCommand.cs @@ -72,8 +72,9 @@ public async Task Execute() var serializer = new SerializerBuilder() .DisableAliases() .WithTypeInspector(inner => new IncludeFieldsTypeInspector(inner)) - .WithTypeConverter(new ResourceYamlTypeConverter()) - .WithTypeConverter(new StringEnumYamlTypeConverter()) + .WithTypeConverter(new ResourceYamlTypeConverter()) + .WithTypeConverter(new ResourceIDYamlTypeConverter()) + .WithTypeConverter(new StringEnumYamlTypeConverter()) .Build(); var serializedString = new string(""); diff --git a/Volatility/Utilities/YAML/ResourceIDYamlTypeConverter.cs b/Volatility/Utilities/YAML/ResourceIDYamlTypeConverter.cs new file mode 100644 index 0000000..18edd7e --- /dev/null +++ b/Volatility/Utilities/YAML/ResourceIDYamlTypeConverter.cs @@ -0,0 +1,33 @@ +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Volatility.Utilities; + +public class ResourceIDYamlTypeConverter : IYamlTypeConverter +{ + public bool Accepts(Type type) => type == typeof(ResourceID); + + public object ReadYaml(IParser parser, Type type, ObjectDeserializer nestedObjectDeserializer) + { + var scalar = parser.Consume().Value; + var hex = scalar.StartsWith("0x", StringComparison.OrdinalIgnoreCase) + ? scalar.Substring(2) + : scalar; + var ul = Convert.ToUInt64(hex, 16); + return new ResourceID(ul); + } + + public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer nestedObjectSerializer) + { + var text = value.ToString(); + emitter.Emit(new Scalar( + anchor: null, + tag: null, + value: text, + style: ScalarStyle.Plain, + isPlainImplicit: true, + isQuotedImplicit: false + )); + } +} \ No newline at end of file From 670ba6cdd13f0aa46ca2295817e41121c984cab2 Mon Sep 17 00:00:00 2001 From: "Nathan V." Date: Fri, 11 Jul 2025 11:34:10 -0400 Subject: [PATCH 08/10] read external imports in Model --- Volatility/Resources/Model/Model.cs | 12 ++++++++++-- Volatility/Resources/ResourceImport.cs | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Volatility/Resources/Model/Model.cs b/Volatility/Resources/Model/Model.cs index 8f1157b..036f06e 100644 --- a/Volatility/Resources/Model/Model.cs +++ b/Volatility/Resources/Model/Model.cs @@ -96,9 +96,16 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann Flags = reader.ReadByte(); + var maxLength = new[] + { + lodDistancesPtr + numRenderables * sizeof(uint), + renderablesPtr + numRenderables * sizeof(uint), + renderableStatesPtr + numRenderables + }.Max(); + // This currently does a lot of seeking. // It may improve performance if we separate this. - for (uint i = 0; i < numRenderables; i++) + for (int i = 0; i < numRenderables; i++) { ModelData modelData = new ModelData(); @@ -119,7 +126,8 @@ public override void ParseFromStream(ResourceBinaryReader reader, Endian endiann (reader.GetEndianness() == Endian.BE ? 0x4 : 0x0), SeekOrigin.Begin ); - modelData.ResourceReference.ReferenceID = reader.ReadUInt32(); + ResourceImport.ReadExternalImport(i, reader, maxLength, out modelData.ResourceReference); + ModelDatas.Add(modelData); } } diff --git a/Volatility/Resources/ResourceImport.cs b/Volatility/Resources/ResourceImport.cs index 12bcf16..da98aa8 100644 --- a/Volatility/Resources/ResourceImport.cs +++ b/Volatility/Resources/ResourceImport.cs @@ -30,7 +30,7 @@ public ResourceImport(string name, bool externalImport = false) ExternalImport = externalImport; } - public static bool ReadExternalImport(byte index, EndianAwareBinaryReader reader, long importBlockOffset, out ResourceImport resourceImport) + public static bool ReadExternalImport(int index, EndianAwareBinaryReader reader, long importBlockOffset, out ResourceImport resourceImport) { // In-resource imports block if (reader.BaseStream.Length >= importBlockOffset + (0x10 * index) + 0x10) @@ -62,7 +62,7 @@ public static bool ReadExternalImport(byte index, EndianAwareBinaryReader reader } - public static ResourceID GetYAMLImportValueAt(string yamlPath, byte index) + public static ResourceID GetYAMLImportValueAt(string yamlPath, int index) { var yaml = File.ReadAllText(yamlPath); var deser = new DeserializerBuilder().Build(); From a726019c74a7d033582ea6034ea1bf9b05c92025 Mon Sep 17 00:00:00 2001 From: "Nathan V." Date: Fri, 11 Jul 2025 12:44:01 -0400 Subject: [PATCH 09/10] improve resource name resolution & find name for imports --- Volatility/Resources/Resource.cs | 2 +- Volatility/Resources/ResourceImport.cs | 16 ++++++++++---- Volatility/Utilities/ResourceIDUtilities.cs | 24 +++++++++++++-------- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Volatility/Resources/Resource.cs b/Volatility/Resources/Resource.cs index 2eb5784..5c2842b 100644 --- a/Volatility/Resources/Resource.cs +++ b/Volatility/Resources/Resource.cs @@ -86,7 +86,7 @@ public Resource(string path, Endian endianness = Endian.Agnostic) : name , 16); - string newName = GetNameByResourceID(ResourceID, GetResourceType().ToString()); + string newName = GetNameByResourceID(ResourceID); AssetName = !string.IsNullOrEmpty(newName) ? newName : ResourceID.ToString(); diff --git a/Volatility/Resources/ResourceImport.cs b/Volatility/Resources/ResourceImport.cs index da98aa8..fec5adf 100644 --- a/Volatility/Resources/ResourceImport.cs +++ b/Volatility/Resources/ResourceImport.cs @@ -1,5 +1,7 @@ using YamlDotNet.Serialization; +using static Volatility.Utilities.ResourceIDUtilities; + namespace Volatility.Resources; public struct ResourceImport @@ -13,15 +15,21 @@ public struct ResourceImport public ResourceID ReferenceID; public bool ExternalImport; - public ResourceImport() { } + public ResourceImport() + { + Name = string.Empty; + } public ResourceImport(ResourceID id, bool externalImport = false, bool useCalculatedName = false) { ReferenceID = id; ExternalImport = externalImport; - // TODO: find name in ResourceDB, pending ResourceDB v3 - // if (useCalculatedName || resource was found in db) - // ReferenceID = 0x0; + Name = GetNameByResourceID(id); + + if (Name.Length > 0 && useCalculatedName) + { + ReferenceID = 0x0; + } } public ResourceImport(string name, bool externalImport = false) diff --git a/Volatility/Utilities/ResourceIDUtilities.cs b/Volatility/Utilities/ResourceIDUtilities.cs index 69e9c89..f867432 100644 --- a/Volatility/Utilities/ResourceIDUtilities.cs +++ b/Volatility/Utilities/ResourceIDUtilities.cs @@ -89,14 +89,14 @@ public static string FlipResourceIDEndian(string ResourceID) return string.Concat(FlipResourceIDEndian(ResourceNameToResourceID(ResourceID))); } - public static string GetNameByResourceID(string id, string type) + public static string GetNameByResourceID(string id) { string path = Path.Combine ( - Directory.GetCurrentDirectory(), - "data", - "ResourceDB", - $"{type}.json" + Directory.GetCurrentDirectory(), + "data", + "ResourceDB", + "ResourceDB.json" ); if (File.Exists(path)) @@ -108,21 +108,27 @@ public static string GetNameByResourceID(string id, string type) return ""; } - public static string GetNameByResourceID(ResourceID id, string type) + + public static string GetNameByResourceID(ResourceID id) { string path = Path.Combine ( Directory.GetCurrentDirectory(), "data", "ResourceDB", - $"{type}.json" + "ResourceDB.json" ); if (File.Exists(path)) { - Dictionary? data = JsonConvert.DeserializeObject>(File.ReadAllText(path)); + Dictionary? data = new Dictionary(StringComparer.OrdinalIgnoreCase); + + using var reader = File.OpenText(path); + using var json = new JsonTextReader(reader); + new JsonSerializer() + .Populate(json, data); - return data.TryGetValue($"{id.ToString():X8}", out string? value) ? value : ""; + return data.TryGetValue(id.ToString(), out string? value) ? value : ""; } return ""; From 4efcf67c701a2d49b1ada077d8b06db1c3ede197 Mon Sep 17 00:00:00 2001 From: "Nathan V." Date: Fri, 11 Jul 2025 13:20:43 -0400 Subject: [PATCH 10/10] deserialize ResourceIDs from YAML --- Volatility/Utilities/YAML/ResourceYamlDeserializer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Volatility/Utilities/YAML/ResourceYamlDeserializer.cs b/Volatility/Utilities/YAML/ResourceYamlDeserializer.cs index b35fd58..4c8a58b 100644 --- a/Volatility/Utilities/YAML/ResourceYamlDeserializer.cs +++ b/Volatility/Utilities/YAML/ResourceYamlDeserializer.cs @@ -43,6 +43,7 @@ public static object DeserializeResource(Type resourceClass, string yaml) var finalDeserializer = new DeserializerBuilder() .IgnoreUnmatchedProperties() + .WithTypeConverter(new ResourceIDYamlTypeConverter()) .Build(); using (var reader = new StringReader(mergedYaml)) {