// !! AI ARTIFACT !! // This code was originally generated by Claude 3.5 Sonnet. // I wanted to write this tooling like I want a fucking hole in the head so I // kindly asked Claude to write it for me. It's shitty and poorly designed, but // it works well enough for my purposes. // It has been slightly tweaked by me, and validated on *this* codebase. It is // provided with no warranty. using UnityEngine; using UnityEditor; using System.IO; using System.Text.RegularExpressions; using System.Collections.Generic; using System.Linq; public class ShaderInlinerV2 : EditorWindow { private string inputShaderPath; private string outputShaderPath; private int maxOutputLines = 10000000; // 10 million lines limit private int currentLineCount = 0; [MenuItem("Tools/yum_food/Shader Inliner (v2)")] public static void ShowWindow() { GetWindow("Shader Inliner"); } private void OnGUI() { GUILayout.Label("Shader Inliner", EditorStyles.boldLabel); inputShaderPath = EditorGUILayout.TextField("Input Shader Path", inputShaderPath); if (GUILayout.Button("Select Input Shader")) { inputShaderPath = EditorUtility.OpenFilePanel("Select Shader", "", "shader"); } maxOutputLines = EditorGUILayout.IntField("Max Output Lines", maxOutputLines); if (GUILayout.Button("Inline Shader")) { if (string.IsNullOrEmpty(inputShaderPath)) { EditorUtility.DisplayDialog("Error", "Please select an input shader.", "OK"); return; } InlineShader(); } } private void InlineShader() { currentLineCount = 0; string shaderContent = File.ReadAllText(inputShaderPath); string inlinedShader = ProcessShader(shaderContent, Path.GetDirectoryName(inputShaderPath)); string fileName = Path.GetFileNameWithoutExtension(inputShaderPath); outputShaderPath = Path.Combine(Path.GetDirectoryName(inputShaderPath), $"{fileName}_inlined.shader"); File.WriteAllText(outputShaderPath, inlinedShader); AssetDatabase.Refresh(); EditorUtility.DisplayDialog("Success", $"Inlined shader saved to:\n{outputShaderPath}\nTotal lines: {currentLineCount}", "OK"); } private string ProcessShader(string content, string basePath) { // Update shader name content = Regex.Replace(content, @"Shader\s+""(.+?)""", match => { string shaderName = match.Groups[1].Value; return $"Shader \"{shaderName}_inlined\""; }); // Count initial lines currentLineCount += content.Split('\n').Length; if (currentLineCount > maxOutputLines) { Debug.LogError($"Maximum line count exceeded: {currentLineCount}"); return content; } // Process all includes, regardless of whether they're in CGPROGRAM blocks content = ProcessIncludes(content, basePath); // Check for mismatched preprocessor macros in the entire shader CheckMismatchedMacros(content); return content; } private string ProcessIncludes(string content, string basePath) { string pattern = @"#include\s+""(.+?)"""; return Regex.Replace(content, pattern, match => { string includePath = match.Groups[1].Value; string fullPath = Path.Combine(basePath, includePath); if (File.Exists(fullPath)) { string includeContent = File.ReadAllText(fullPath); // Count the lines in the included file int includeLines = includeContent.Split('\n').Length; currentLineCount += includeLines; // Check if we've exceeded the line limit if (currentLineCount > maxOutputLines) { Debug.LogError($"Maximum line count exceeded ({currentLineCount} lines) while including: {includePath}"); return $"// ERROR: Maximum line count exceeded while including: {includePath}"; } // Process includes recursively return ProcessIncludes(includeContent, Path.GetDirectoryName(fullPath)); } else { Debug.LogWarning($"Include file not found: {fullPath}"); return match.Value; } }); } private void CheckMismatchedMacros(string content) { var stack = new Stack(); var lines = content.Split('\n'); var macroPattern = @"^\s*#(if|ifdef|ifndef|elif|else|endif|if\s+defined)"; for (int i = 0; i < lines.Length; i++) { var line = lines[i].Trim(); var match = Regex.Match(line, macroPattern); if (match.Success) { var directive = match.Groups[1].Value; switch (directive) { case "if": case "ifdef": case "ifndef": case "if defined": stack.Push(directive); break; case "elif": if (stack.Count == 0 || (stack.Peek() != "if" && stack.Peek() != "elif")) { Debug.LogError($"Mismatched #elif at line {i + 1}"); } else { stack.Pop(); stack.Push("elif"); } break; case "else": if (stack.Count == 0 || (stack.Peek() != "if" && stack.Peek() != "elif")) { Debug.LogError($"Mismatched #else at line {i + 1}"); } else { stack.Pop(); stack.Push("else"); } break; case "endif": if (stack.Count == 0) { Debug.LogError($"Mismatched #endif at line {i + 1}"); } else { stack.Pop(); } break; } } } if (stack.Count > 0) { Debug.LogError($"Unclosed preprocessor directives: {string.Join(", ", stack)}"); } } }