Skip to content

Commit 8597241

Browse files
committed
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
1 parent 3f2e672 commit 8597241

4 files changed

Lines changed: 58 additions & 91 deletions

File tree

DataTool/FindLogic/Combo.cs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -671,19 +671,35 @@ public static ComboInfo Find(ComboInfo info, ulong guid, Dictionary<ulong, ulong
671671
materialInfo.m_materialIDs.Add(context.MaterialID);
672672
materialInfo.m_shaderSourceGUID = GetReplacement(material.Header.ShaderSource, replacements);
673673
materialInfo.m_shaderGroupGUID = GetReplacement(material.Header.ShaderGroup, replacements);
674-
try {
675-
if (Program.Flags.ExtractShaders) {
676-
teShaderGroup shaderGroup = new teShaderGroup(OpenFile(materialInfo.m_shaderGroupGUID));
677-
foreach (teResourceGUID shaderGuid in shaderGroup.Instances) {
678-
ulong shaderInstanceGuid = GetReplacement(shaderGuid, replacements);
679-
teShaderInstance shaderInstance = new teShaderInstance(OpenFile(shaderInstanceGuid));
680-
ulong shaderCodeGuid = GetReplacement(shaderInstance.Header.ShaderCode, replacements);
681-
teShaderCode shaderCode = new teShaderCode(OpenFile(shaderCodeGuid));
682-
materialInfo.m_shaders.Add((shaderInstanceGuid, shaderCodeGuid, shaderCode.ByteCode));
674+
675+
if (Program.Flags.ExtractShaders) {
676+
// Local function to prevent nesting on error handling, could be hoisted into a static function
677+
void ExtractShaders() {
678+
using Stream shaderGroupStream = OpenFile(materialInfo.m_shaderGroupGUID);
679+
if (shaderGroupStream == null) {
680+
Logger.Error("Combo", $"ExtractShaders: can't open shader group {materialInfo.m_shaderGroupGUID:X16}");
681+
return;
682+
}
683+
684+
teShaderGroup shaderGroup = new teShaderGroup(shaderGroupStream);
685+
if (shaderGroup.Shaders == null || shaderGroup.Instances == null) {
686+
Logger.Error("Combo", $"ExtractShaders: group {materialInfo.m_shaderGroupGUID:X16} has no shader/instance arrays");
687+
return;
688+
}
689+
690+
for (int i = 0; i < shaderGroup.Shaders.Length; i++) {
691+
ulong shaderCodeGuid = GetReplacement(shaderGroup.Shaders[i], replacements);
692+
using Stream shaderCodeStream = OpenFile(shaderCodeGuid);
693+
if (shaderCodeStream == null) {
694+
Logger.Error("Combo", $"ExtractShaders: can't open shader code {shaderCodeGuid:X16}");
695+
continue;
696+
}
697+
698+
teShaderCode shaderCode = new teShaderCode(shaderCodeStream);
699+
materialInfo.m_shaders.Add((GetReplacement(shaderGroup.Instances[i], replacements), shaderCodeGuid, shaderCode.ByteCode));
683700
}
684701
}
685-
} catch {
686-
// lol xd
702+
ExtractShaders();
687703
}
688704

689705
if (context.ModelLook == 0 && context.Model != 0) {

DataTool/ToolLogic/Extract/Debug/ExtractDebugShaders.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using TankLib.STU.Types;
1010
using static DataTool.Program;
1111
using static DataTool.Helper.STUHelper;
12+
using TankLib.Helpers;
1213

1314
namespace DataTool.ToolLogic.Extract.Debug;
1415

@@ -286,9 +287,10 @@ public static void SaveShaderGroup(teShaderGroup shaderGroup, string path) {
286287
int i = 0;
287288
foreach (ulong shaderGroupInstance in shaderGroup.Instances) {
288289
string name = null;
289-
if (shaderGroup.Hashes != null && shaderGroup.Hashes[i] != 0) {
290-
name = shaderGroup.Hashes[i].ToString("X8");
291-
}
290+
Logger.Error("SaveShaderGroup", $"teShaderGroup no longer associates a hash with teShaderInstance, see documentation on GetShaderByHash.");
291+
//if (shaderGroup.Hashes != null && shaderGroup.Hashes[i] != 0) {
292+
// name = shaderGroup.Hashes[i].ToString("X8");
293+
//}
292294

293295
SaveShaderInstance(path, shaderGroupInstance, name);
294296
i++;

TankLib/teShaderCode.cs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,16 @@ namespace TankLib {
77
/// <summary>Tank ShaderCode, file type 087</summary>
88
public class teShaderCode {
99
/// <summary>ShaderCode Header </summary>
10-
[StructLayout(LayoutKind.Sequential, Pack = 4)]
10+
[StructLayout(LayoutKind.Explicit)]
1111
public struct ShaderCodeHeader {
12+
/// <summary>Shader type flags</summary>
13+
[FieldOffset(8)] public Enums.teSHADER_TYPE ShaderType; // 36 -> 8
1214
/// <summary>Offset to data</summary>
13-
public long DataOffset; // 0
14-
15-
/// <summary>Offset to unknown data</summary>
16-
public long OffsetB; // 8
17-
18-
public uint UnknownFF; // 16
19-
20-
/// <summary>Unknown data count</summary>
21-
public uint StreamOutDescCount; // 20
22-
15+
[FieldOffset(112)] public long DataOffset; // 0 -> 112
2316
/// <summary>Size of compressed DXBC data</summary>
24-
public int CompressedSize; // 24
25-
17+
[FieldOffset(132)] public int CompressedSize; // 24 -> 132
2618
/// <summary>Size of decompressed DXBC data</summary>
27-
public int UncompressedSize; // 28
28-
29-
public uint Unknown; // 32
30-
31-
/// <summary>Shader type</summary>
32-
public Enums.teSHADER_TYPE ShaderType; // 36
33-
34-
// etc
19+
[FieldOffset(136)] public int UncompressedSize; // 28 -> 136
3520
}
3621

3722
/// <summary>Header Data</summary>

TankLib/teShaderGroup.cs

Lines changed: 20 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,18 @@
11
using System.IO;
22
using System.Runtime.InteropServices;
3+
using TankLib.Helpers;
34

45
namespace TankLib {
56
/// <summary>Tank ShaderGroup, file type 085</summary>
67
public class teShaderGroup {
7-
[StructLayout(LayoutKind.Sequential, Pack = 4)]
8+
[StructLayout(LayoutKind.Explicit)]
89
public struct GroupHeader {
10+
/// <summary>Number of entries for both ShadersOffset and InstancesOffset</summary>
11+
[FieldOffset(100)] public int NumShaders; // 64 -> 76 -> 100
12+
/// <summary>ShaderCode array offset</summary>
13+
[FieldOffset(104)] public long ShadersOffset; // na -> 104
914
/// <summary>ShaderInstance array offset</summary>
10-
public long InstanceOffset; // 0
11-
12-
public long HashOffset; // 8
13-
public long FlagsOffset; // 16
14-
15-
public long NewIdk; // n/a -> 24
16-
17-
public long OffsetD; // 24 -> 32
18-
public long OffsetE; // 32 -> 40
19-
20-
/// <summary>teShaderSource that this group was generated from</summary>
21-
/// <remarks>088 GUID</remarks>
22-
public teResourceGUID SourceGUID; // 40 -> 48
23-
24-
/// <summary>A virtual reference. Usage unknown</summary>
25-
/// <remarks>00F GUID</remarks>
26-
public teResourceGUID CacheGUID; // 48 -> 56
27-
28-
public ulong Flags; // 56 -> 64
29-
30-
public int NumIdk; // n/a -> 72
31-
public int NumShaders; // 64 -> 76
32-
33-
// ...
15+
[FieldOffset(120)] public long InstancesOffset; // 0 -> 100
3416
}
3517

3618
[StructLayout(LayoutKind.Sequential, Pack = 4)]
@@ -54,17 +36,15 @@ public struct ShaderUnk {
5436
/// <summary>Header Data</summary>
5537
public GroupHeader Header;
5638

57-
/// <summary>ShaderInstances</summary>
39+
/// <summary>teShaderCode GUIDs (087)</summary>
40+
public teResourceGUID[] Shaders;
41+
42+
/// <summary>teShaderInstance info GUIDs (040)</summary>
5843
public teResourceGUID[] Instances;
5944

6045
/// <summary>Flags that are used to select the correct ShaderInstance pair based on parameters</summary>
6146
public ulong[] InstanceFlags;
6247

63-
/// <summary>
64-
/// ShaderInstance "hashes". Used to identify instances in 088 groups.
65-
/// </summary>
66-
public uint[] Hashes;
67-
6848
public ShaderQuality[] ShaderQualities;
6949
public ShaderUnk[] ShaderUnks;
7050

@@ -89,43 +69,27 @@ public teShaderGroup(BinaryReader reader) {
8969
private void Read(BinaryReader reader) {
9070
Header = reader.Read<GroupHeader>();
9171

92-
if (Header.InstanceOffset > 0) {
93-
reader.BaseStream.Position = Header.InstanceOffset;
94-
Instances = reader.ReadArray<teResourceGUID>(Header.NumShaders);
95-
}
96-
97-
if (Header.HashOffset > 0) {
98-
reader.BaseStream.Position = Header.HashOffset;
99-
Hashes = reader.ReadArray<uint>(Header.NumShaders);
72+
if (Header.ShadersOffset > 0) {
73+
reader.BaseStream.Position = Header.ShadersOffset;
74+
Shaders = reader.ReadArray<teResourceGUID>(Header.NumShaders);
10075
}
10176

102-
if (Header.FlagsOffset > 0) {
103-
reader.BaseStream.Position = Header.FlagsOffset;
104-
InstanceFlags = reader.ReadArray<ulong>(Header.NumShaders);
105-
}
106-
107-
{
108-
reader.BaseStream.Position = 72;
109-
ShaderQualities = reader.ReadArray<ShaderQuality>(5);
110-
111-
reader.BaseStream.Position = 104;
112-
ShaderUnks = reader.ReadArray<ShaderUnk>(5);
77+
if (Header.InstancesOffset > 0) {
78+
reader.BaseStream.Position = Header.InstancesOffset;
79+
Instances = reader.ReadArray<teResourceGUID>(Header.NumShaders);
11380
}
11481
}
11582

11683
/// <summary>
84+
/// DEPRECATED: teShaderGroup used to store a hash to match with a specific teShaderInstance. This mapping has since been broken as
85+
/// teShaderGroup now holds guids to both teShaderInstance and teShaderCode mapping them with a simple positional index instead.
11786
/// Get a ShaderInstance GUID from a "hash"
11887
/// </summary>
11988
/// <note>Mostly used on 088 groups</note>
12089
/// <param name="hash">"Hash" associated with a specific ShaderInstance</param>
12190
/// <returns></returns>
12291
public teResourceGUID GetShaderByHash(uint hash) {
123-
if (Hashes == null) return (teResourceGUID) 0;
124-
for (int i = 0; i < Header.NumShaders; i++) {
125-
if (Hashes[i] == hash) {
126-
return Instances[i];
127-
}
128-
}
92+
Logger.Error("teShaderGroup", $"GetShaderByHash doesn't work any longer for {hash:X16}, see documentation on GetShaderByHash.");
12993
return (teResourceGUID) 0;
13094
}
13195
}

0 commit comments

Comments
 (0)