From 8597241a0850cbbfed8b0f102e3e5464c817c581 Mon Sep 17 00:00:00 2001 From: PachiMako <299646743+PachiMako@users.noreply.github.com> Date: Fri, 10 Jul 2026 18:45:12 +0200 Subject: [PATCH] Revive --extract-shaders for dxbc bytecode extraction teShaderCode, teShaderInstance and teShaderGroup all changed structure since at least the patch of Aug 10 '23 (Manifest 3320652641754470004). Changes: - Update offsets of used fields in teShaderGroup and teShaderCode headers - Improves error handling and logging for --extract-shaders - Adds errors in codepaths that are not longer functional such as resolving file names in SaveShaderGroup and resolving previously extracted shaders using their now stale hash Verified working on overwatch build prometheus-2_23_0_0-150480 with valid dxbc for all entries of teShaderCode with the following distributions across shader types: PIXEL (2): 112210 VERTEX (1): 21777 COMPUTE (0): 147 GEOMETRY (4): 24 VERTEX, UNKNOWN_C (129): 13 GEOMETRY, UNKNOWN_B (20): 1 --- DataTool/FindLogic/Combo.cs | 38 +++++++--- .../Extract/Debug/ExtractDebugShaders.cs | 8 +- TankLib/teShaderCode.cs | 27 ++----- TankLib/teShaderGroup.cs | 76 +++++-------------- 4 files changed, 58 insertions(+), 91 deletions(-) diff --git a/DataTool/FindLogic/Combo.cs b/DataTool/FindLogic/Combo.cs index 8364d460e..2b424965f 100644 --- a/DataTool/FindLogic/Combo.cs +++ b/DataTool/FindLogic/Combo.cs @@ -671,19 +671,35 @@ public static ComboInfo Find(ComboInfo info, ulong guid, DictionaryTank ShaderCode, file type 087 public class teShaderCode { /// ShaderCode Header - [StructLayout(LayoutKind.Sequential, Pack = 4)] + [StructLayout(LayoutKind.Explicit)] public struct ShaderCodeHeader { + /// Shader type flags + [FieldOffset(8)] public Enums.teSHADER_TYPE ShaderType; // 36 -> 8 /// Offset to data - public long DataOffset; // 0 - - /// Offset to unknown data - public long OffsetB; // 8 - - public uint UnknownFF; // 16 - - /// Unknown data count - public uint StreamOutDescCount; // 20 - + [FieldOffset(112)] public long DataOffset; // 0 -> 112 /// Size of compressed DXBC data - public int CompressedSize; // 24 - + [FieldOffset(132)] public int CompressedSize; // 24 -> 132 /// Size of decompressed DXBC data - public int UncompressedSize; // 28 - - public uint Unknown; // 32 - - /// Shader type - public Enums.teSHADER_TYPE ShaderType; // 36 - - // etc + [FieldOffset(136)] public int UncompressedSize; // 28 -> 136 } /// Header Data diff --git a/TankLib/teShaderGroup.cs b/TankLib/teShaderGroup.cs index ec480633f..ec367b0d6 100644 --- a/TankLib/teShaderGroup.cs +++ b/TankLib/teShaderGroup.cs @@ -1,36 +1,18 @@ using System.IO; using System.Runtime.InteropServices; +using TankLib.Helpers; namespace TankLib { /// Tank ShaderGroup, file type 085 public class teShaderGroup { - [StructLayout(LayoutKind.Sequential, Pack = 4)] + [StructLayout(LayoutKind.Explicit)] public struct GroupHeader { + /// Number of entries for both ShadersOffset and InstancesOffset + [FieldOffset(100)] public int NumShaders; // 64 -> 76 -> 100 + /// ShaderCode array offset + [FieldOffset(104)] public long ShadersOffset; // na -> 104 /// ShaderInstance array offset - public long InstanceOffset; // 0 - - public long HashOffset; // 8 - public long FlagsOffset; // 16 - - public long NewIdk; // n/a -> 24 - - public long OffsetD; // 24 -> 32 - public long OffsetE; // 32 -> 40 - - /// teShaderSource that this group was generated from - /// 088 GUID - public teResourceGUID SourceGUID; // 40 -> 48 - - /// A virtual reference. Usage unknown - /// 00F GUID - public teResourceGUID CacheGUID; // 48 -> 56 - - public ulong Flags; // 56 -> 64 - - public int NumIdk; // n/a -> 72 - public int NumShaders; // 64 -> 76 - - // ... + [FieldOffset(120)] public long InstancesOffset; // 0 -> 100 } [StructLayout(LayoutKind.Sequential, Pack = 4)] @@ -54,17 +36,15 @@ public struct ShaderUnk { /// Header Data public GroupHeader Header; - /// ShaderInstances + /// teShaderCode GUIDs (087) + public teResourceGUID[] Shaders; + + /// teShaderInstance info GUIDs (040) public teResourceGUID[] Instances; /// Flags that are used to select the correct ShaderInstance pair based on parameters public ulong[] InstanceFlags; - /// - /// ShaderInstance "hashes". Used to identify instances in 088 groups. - /// - public uint[] Hashes; - public ShaderQuality[] ShaderQualities; public ShaderUnk[] ShaderUnks; @@ -89,43 +69,27 @@ public teShaderGroup(BinaryReader reader) { private void Read(BinaryReader reader) { Header = reader.Read(); - if (Header.InstanceOffset > 0) { - reader.BaseStream.Position = Header.InstanceOffset; - Instances = reader.ReadArray(Header.NumShaders); - } - - if (Header.HashOffset > 0) { - reader.BaseStream.Position = Header.HashOffset; - Hashes = reader.ReadArray(Header.NumShaders); + if (Header.ShadersOffset > 0) { + reader.BaseStream.Position = Header.ShadersOffset; + Shaders = reader.ReadArray(Header.NumShaders); } - if (Header.FlagsOffset > 0) { - reader.BaseStream.Position = Header.FlagsOffset; - InstanceFlags = reader.ReadArray(Header.NumShaders); - } - - { - reader.BaseStream.Position = 72; - ShaderQualities = reader.ReadArray(5); - - reader.BaseStream.Position = 104; - ShaderUnks = reader.ReadArray(5); + if (Header.InstancesOffset > 0) { + reader.BaseStream.Position = Header.InstancesOffset; + Instances = reader.ReadArray(Header.NumShaders); } } /// + /// DEPRECATED: teShaderGroup used to store a hash to match with a specific teShaderInstance. This mapping has since been broken as + /// teShaderGroup now holds guids to both teShaderInstance and teShaderCode mapping them with a simple positional index instead. /// Get a ShaderInstance GUID from a "hash" /// /// Mostly used on 088 groups /// "Hash" associated with a specific ShaderInstance /// public teResourceGUID GetShaderByHash(uint hash) { - if (Hashes == null) return (teResourceGUID) 0; - for (int i = 0; i < Header.NumShaders; i++) { - if (Hashes[i] == hash) { - return Instances[i]; - } - } + Logger.Error("teShaderGroup", $"GetShaderByHash doesn't work any longer for {hash:X16}, see documentation on GetShaderByHash."); return (teResourceGUID) 0; } }