noradninja

ShaderTargetCompileTool.cs

Apr 25th, 2026 (edited)
167
0
Never
6
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 29.43 KB | None | 0 0
  1. /* ShaderTargetCompileTool.cs */
  2. /* Place this file in Assets/Editor/ */
  3.  
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using UnityEngine.Rendering;
  9.  
  10. public class ShaderTargetCompileTool : EditorWindow
  11. {
  12.     /* We validate shaders against the selected build target */
  13.     private BuildTarget target = BuildTarget.PSP2;
  14.  
  15.     /* We keep generated probe assets isolated and disposable */
  16.     private string tempRoot = "Assets/__ShaderCompileProbe";
  17.     private string bundleName = "shader_compile_probe";
  18.  
  19.     /* We write a clean shader error report after the compile probe runs */
  20.     private string reportPath = "Temp/ShaderCompileProbeReport.txt";
  21.     private bool generateErrorReport = true;
  22.     private bool reportAvailable = false;
  23.  
  24.     /* We control compile coverage */
  25.     private bool includeVariants = true;
  26.     private bool scrapeShaderPragmas = true;
  27.     private bool includeUnityBuiltInPragmas = true;
  28.     private bool includeVertexLightmapSafetyCombos = true;
  29.     private bool exhaustiveMode = false;
  30.  
  31.     /* We cap generated variants in practical mode so compile time does not explode */
  32.     private int maxGeneratedCombosPerShader = 256;
  33.  
  34.     private Vector2 scroll;
  35.     private readonly List<string> log = new List<string>();
  36.  
  37.     /*
  38.     We keep explicit known-good safety-net combos here.
  39.     These are not the main variant source anymore; they are extra protection for Vita lighting cases.
  40.     */
  41.     private readonly string[][] vertexLightmapSafetyCombos =
  42.     {
  43.         new string[] { "VERTEXLIGHT_ON", "LIGHTMAP_ON" },
  44.         new string[] { "VERTEXLIGHT_ON", "LIGHTMAP_ON", "DIRLIGHTMAP_COMBINED" },
  45.         new string[] { "VERTEXLIGHT_ON", "LIGHTMAP_ON", "_NORMALMAP" },
  46.         new string[] { "VERTEXLIGHT_ON", "LIGHTMAP_ON", "_METALLICGLOSSMAP" },
  47.         new string[] { "VERTEXLIGHT_ON", "LIGHTMAP_ON", "_NORMALMAP", "_METALLICGLOSSMAP" },
  48.  
  49.         new string[] { "LIGHTMAP_ON", "_NORMALMAP" },
  50.         new string[] { "LIGHTMAP_ON", "_METALLICGLOSSMAP" },
  51.         new string[] { "LIGHTMAP_ON", "_NORMALMAP", "_METALLICGLOSSMAP" },
  52.  
  53.         new string[] { "VERTEXLIGHT_ON", "_NORMALMAP" },
  54.         new string[] { "VERTEXLIGHT_ON", "_METALLICGLOSSMAP" },
  55.         new string[] { "VERTEXLIGHT_ON", "_NORMALMAP", "_METALLICGLOSSMAP" }
  56.     };
  57.  
  58.     /*
  59.     We compile across common pass types.
  60.     Unsupported pass/shader pairs are skipped safely.
  61.     */
  62.     private readonly PassType[] commonPasses =
  63.     {
  64.         PassType.Normal,
  65.         PassType.ForwardBase,
  66.         PassType.ForwardAdd,
  67.         PassType.ShadowCaster,
  68.         PassType.Meta,
  69.         PassType.Vertex,
  70.         PassType.Deferred,
  71.         PassType.LightPrePassBase,
  72.         PassType.LightPrePassFinal
  73.     };
  74.  
  75.     [MenuItem("Tools/Vita/Compile Project Shaders")]
  76.     public static void Open()
  77.     {
  78.         GetWindow<ShaderTargetCompileTool>("Shader Compile Probe");
  79.     }
  80.  
  81.     private void OnEnable()
  82.     {
  83.         reportAvailable = File.Exists(reportPath);
  84.     }
  85.  
  86.     private void OnGUI()
  87.     {
  88.         target = (BuildTarget)EditorGUILayout.EnumPopup("Build Target", target);
  89.  
  90.         EditorGUILayout.Space();
  91.  
  92.         includeVariants = EditorGUILayout.Toggle("Create Variant Collection", includeVariants);
  93.         scrapeShaderPragmas = EditorGUILayout.Toggle("Scrape Shader Pragmas", scrapeShaderPragmas);
  94.         includeUnityBuiltInPragmas = EditorGUILayout.Toggle("Expand Unity Built-In Pragmas", includeUnityBuiltInPragmas);
  95.         includeVertexLightmapSafetyCombos = EditorGUILayout.Toggle("Include Vertex/Lightmap Safety Combos", includeVertexLightmapSafetyCombos);
  96.         exhaustiveMode = EditorGUILayout.Toggle("Exhaustive Mode", exhaustiveMode);
  97.         generateErrorReport = EditorGUILayout.Toggle("Generate Error Report", generateErrorReport);
  98.  
  99.         if (!exhaustiveMode)
  100.         {
  101.             maxGeneratedCombosPerShader = EditorGUILayout.IntField(
  102.                 "Max Combos / Shader",
  103.                 maxGeneratedCombosPerShader
  104.             );
  105.  
  106.             if (maxGeneratedCombosPerShader < 1)
  107.                 maxGeneratedCombosPerShader = 1;
  108.         }
  109.  
  110.         EditorGUILayout.Space();
  111.  
  112.         if (GUILayout.Button("Compile Project Shaders For Target", GUILayout.Height(32)))
  113.             CompileShaders();
  114.  
  115.         EditorGUILayout.Space();
  116.  
  117.         GUI.enabled = reportAvailable && File.Exists(reportPath);
  118.  
  119.         if (GUILayout.Button("Open Shader Compile Report", GUILayout.Height(26)))
  120.             OpenShaderCompileReport();
  121.  
  122.         GUI.enabled = true;
  123.  
  124.         EditorGUILayout.Space();
  125.  
  126.         scroll = EditorGUILayout.BeginScrollView(scroll);
  127.  
  128.         for (int i = 0; i < log.Count; i++)
  129.             EditorGUILayout.LabelField(log[i], EditorStyles.wordWrappedLabel);
  130.  
  131.         EditorGUILayout.EndScrollView();
  132.     }
  133.  
  134.     private void CompileShaders()
  135.     {
  136.         log.Clear();
  137.         reportAvailable = false;
  138.  
  139.         long editorLogStart = 0;
  140.  
  141.         if (generateErrorReport)
  142.             editorLogStart = GetEditorLogLength();
  143.  
  144.         try
  145.         {
  146.             CleanupTempAssets();
  147.  
  148.             Directory.CreateDirectory(tempRoot);
  149.             AssetDatabase.Refresh();
  150.  
  151.             string[] shaderGuids = AssetDatabase.FindAssets("t:Shader");
  152.  
  153.             int materialCount = 0;
  154.             int variantCount = 0;
  155.  
  156.             ShaderVariantCollection svc = null;
  157.  
  158.             if (includeVariants)
  159.                 svc = new ShaderVariantCollection();
  160.  
  161.             for (int i = 0; i < shaderGuids.Length; i++)
  162.             {
  163.                 string shaderPath = AssetDatabase.GUIDToAssetPath(shaderGuids[i]);
  164.  
  165.                 if (shaderPath.StartsWith("Packages/"))
  166.                     continue;
  167.  
  168.                 Shader shader = AssetDatabase.LoadAssetAtPath<Shader>(shaderPath);
  169.  
  170.                 if (shader == null)
  171.                     continue;
  172.  
  173.                 /*
  174.                 We create one dummy material per shader.
  175.                 This forces Unity to include the shader in the temporary AssetBundle compile.
  176.                 */
  177.                 Material mat = new Material(shader);
  178.  
  179.                 string safeName = MakeSafeAssetName(shader.name);
  180.                 string matPath = tempRoot + "/" + safeName + ".mat";
  181.  
  182.                 AssetDatabase.CreateAsset(mat, matPath);
  183.  
  184.                 AssetImporter importer = AssetImporter.GetAtPath(matPath);
  185.                 importer.assetBundleName = bundleName;
  186.  
  187.                 materialCount++;
  188.                 log.Add("Queued shader: " + shaderPath);
  189.  
  190.                 if (includeVariants && svc != null)
  191.                     variantCount += AddVariantsForShader(svc, shader, shaderPath);
  192.             }
  193.  
  194.             if (includeVariants && svc != null)
  195.             {
  196.                 string svcPath = tempRoot + "/GeneratedShaderVariants.shadervariants";
  197.  
  198.                 AssetDatabase.CreateAsset(svc, svcPath);
  199.  
  200.                 AssetImporter importer = AssetImporter.GetAtPath(svcPath);
  201.                 importer.assetBundleName = bundleName;
  202.  
  203.                 log.Add("");
  204.                 log.Add("Generated ShaderVariantCollection variants: " + variantCount);
  205.             }
  206.  
  207.             AssetDatabase.SaveAssets();
  208.             AssetDatabase.Refresh();
  209.  
  210.             string outputPath = "Temp/ShaderCompileProbeBundles";
  211.  
  212.             if (!Directory.Exists(outputPath))
  213.                 Directory.CreateDirectory(outputPath);
  214.  
  215.             log.Add("");
  216.             log.Add("Building temporary AssetBundle for: " + target);
  217.             log.Add("Materials generated: " + materialCount);
  218.  
  219.             /*
  220.             We use StrictMode so shader errors fail loudly.
  221.             This makes the probe useful as a pre-build validation step.
  222.             */
  223.             BuildAssetBundleOptions options =
  224.                 BuildAssetBundleOptions.ForceRebuildAssetBundle |
  225.                 BuildAssetBundleOptions.StrictMode;
  226.  
  227.             AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(
  228.                 outputPath,
  229.                 options,
  230.                 target
  231.             );
  232.  
  233.             log.Add("");
  234.  
  235.             if (manifest != null)
  236.                 log.Add("Shader compile probe completed. Check Console / report for errors.");
  237.             else
  238.                 log.Add("AssetBundle build failed. Check Console / report for shader errors.");
  239.         }
  240.         finally
  241.         {
  242.             if (generateErrorReport)
  243.             {
  244.                 string newLogText = ReadEditorLogFrom(editorLogStart);
  245.                 WriteShaderCompileReport(newLogText);
  246.  
  247.                 reportAvailable = File.Exists(reportPath);
  248.  
  249.                 log.Add("");
  250.                 log.Add("Shader compile report written to:");
  251.                 log.Add(reportPath);
  252.             }
  253.  
  254.             CleanupTempAssets();
  255.             AssetDatabase.Refresh();
  256.             Repaint();
  257.         }
  258.     }
  259.  
  260.     private int AddVariantsForShader(ShaderVariantCollection svc, Shader shader, string shaderPath)
  261.     {
  262.         int added = 0;
  263.  
  264.         List<string[]> combos = new List<string[]>();
  265.  
  266.         if (scrapeShaderPragmas)
  267.         {
  268.             ShaderVariantScanResult scan = ScanShaderAndIncludes(shaderPath);
  269.             combos.AddRange(BuildKeywordCombos(scan));
  270.  
  271.             log.Add("  Pragmas found: " + scan.keywordGroups.Count + " groups, " + scan.skipKeywords.Count + " skipped keywords");
  272.             log.Add("  Generated pragma combos: " + combos.Count);
  273.         }
  274.  
  275.         if (includeVertexLightmapSafetyCombos)
  276.         {
  277.             for (int i = 0; i < vertexLightmapSafetyCombos.Length; i++)
  278.                 combos.Add(vertexLightmapSafetyCombos[i]);
  279.         }
  280.  
  281.         combos = DeduplicateCombos(combos);
  282.  
  283.         for (int p = 0; p < commonPasses.Length; p++)
  284.         {
  285.             PassType passType = commonPasses[p];
  286.  
  287.             added += TryAddVariant(svc, shader, passType, new string[0]);
  288.  
  289.             for (int c = 0; c < combos.Count; c++)
  290.                 added += TryAddVariant(svc, shader, passType, combos[c]);
  291.         }
  292.  
  293.         return added;
  294.     }
  295.  
  296.     private ShaderVariantScanResult ScanShaderAndIncludes(string shaderPath)
  297.     {
  298.         ShaderVariantScanResult result = new ShaderVariantScanResult();
  299.  
  300.         HashSet<string> visited = new HashSet<string>();
  301.         ScanFileRecursive(shaderPath, result, visited);
  302.  
  303.         return result;
  304.     }
  305.  
  306.     private void ScanFileRecursive(
  307.         string assetPath,
  308.         ShaderVariantScanResult result,
  309.         HashSet<string> visited
  310.     )
  311.     {
  312.         if (string.IsNullOrEmpty(assetPath))
  313.             return;
  314.  
  315.         if (visited.Contains(assetPath))
  316.             return;
  317.  
  318.         visited.Add(assetPath);
  319.  
  320.         string fullPath = Path.GetFullPath(assetPath);
  321.  
  322.         if (!File.Exists(fullPath))
  323.             return;
  324.  
  325.         string directory = Path.GetDirectoryName(assetPath);
  326.         string[] lines = File.ReadAllLines(fullPath);
  327.  
  328.         bool inBlockComment = false;
  329.  
  330.         for (int i = 0; i < lines.Length; i++)
  331.         {
  332.             string line = StripComments(lines[i], ref inBlockComment).Trim();
  333.  
  334.             if (string.IsNullOrEmpty(line))
  335.                 continue;
  336.  
  337.             if (line.StartsWith("#include"))
  338.             {
  339.                 string includePath = ExtractIncludePath(line);
  340.  
  341.                 if (!string.IsNullOrEmpty(includePath))
  342.                 {
  343.                     string resolved = ResolveIncludeAssetPath(directory, includePath);
  344.  
  345.                     if (!string.IsNullOrEmpty(resolved))
  346.                         ScanFileRecursive(resolved, result, visited);
  347.                 }
  348.  
  349.                 continue;
  350.             }
  351.  
  352.             if (!line.StartsWith("#pragma"))
  353.                 continue;
  354.  
  355.             ParsePragmaLine(line, result);
  356.         }
  357.     }
  358.  
  359.     private void ParsePragmaLine(string line, ShaderVariantScanResult result)
  360.     {
  361.         string[] tokens = SplitTokens(line);
  362.  
  363.         if (tokens.Length < 2)
  364.             return;
  365.  
  366.         string pragmaType = tokens[1];
  367.  
  368.         if (pragmaType == "skip_variants")
  369.         {
  370.             for (int i = 2; i < tokens.Length; i++)
  371.             {
  372.                 if (!string.IsNullOrEmpty(tokens[i]))
  373.                     result.skipKeywords.Add(tokens[i]);
  374.             }
  375.  
  376.             return;
  377.         }
  378.  
  379.         if (includeUnityBuiltInPragmas && IsUnityBuiltInVariantPragma(pragmaType))
  380.         {
  381.             AddUnityBuiltInKeywordGroups(pragmaType, result);
  382.             return;
  383.         }
  384.  
  385.         if (!IsExplicitVariantPragma(pragmaType))
  386.             return;
  387.  
  388.         List<string> keywords = new List<string>();
  389.  
  390.         for (int i = 2; i < tokens.Length; i++)
  391.         {
  392.             string token = tokens[i];
  393.  
  394.             if (string.IsNullOrEmpty(token))
  395.                 continue;
  396.  
  397.             if (token == "_")
  398.                 continue;
  399.  
  400.             if (IsIgnoredPragmaToken(token))
  401.                 continue;
  402.  
  403.             keywords.Add(token);
  404.         }
  405.  
  406.         if (keywords.Count > 0)
  407.             result.keywordGroups.Add(keywords.ToArray());
  408.     }
  409.  
  410.     private List<string[]> BuildKeywordCombos(ShaderVariantScanResult scan)
  411.     {
  412.         List<string[]> combos = new List<string[]>();
  413.  
  414.         /*
  415.         We add single-keyword variants first.
  416.         This gives useful coverage even when capped mode stops full expansion early.
  417.         */
  418.         for (int g = 0; g < scan.keywordGroups.Count; g++)
  419.         {
  420.             for (int k = 0; k < scan.keywordGroups[g].Length; k++)
  421.             {
  422.                 string keyword = scan.keywordGroups[g][k];
  423.  
  424.                 if (!scan.skipKeywords.Contains(keyword))
  425.                     combos.Add(new string[] { keyword });
  426.             }
  427.         }
  428.  
  429.         List<string[]> generated = new List<string[]>();
  430.         BuildKeywordCrossProduct(scan.keywordGroups, scan.skipKeywords, 0, new List<string>(), generated);
  431.  
  432.         for (int i = 0; i < generated.Count; i++)
  433.         {
  434.             combos.Add(generated[i]);
  435.  
  436.             if (!exhaustiveMode && combos.Count >= maxGeneratedCombosPerShader)
  437.                 break;
  438.         }
  439.  
  440.         return DeduplicateCombos(combos);
  441.     }
  442.  
  443.     private void BuildKeywordCrossProduct(
  444.         List<string[]> groups,
  445.         HashSet<string> skipKeywords,
  446.         int groupIndex,
  447.         List<string> current,
  448.         List<string[]> output
  449.     )
  450.     {
  451.         if (!exhaustiveMode && output.Count >= maxGeneratedCombosPerShader)
  452.             return;
  453.  
  454.         if (groupIndex >= groups.Count)
  455.         {
  456.             if (current.Count > 0)
  457.                 output.Add(current.ToArray());
  458.  
  459.             return;
  460.         }
  461.  
  462.         /*
  463.         We include the no-keyword option for each group.
  464.         This approximates shader_feature and keeps multi_compile coverage practical.
  465.         */
  466.         BuildKeywordCrossProduct(groups, skipKeywords, groupIndex + 1, current, output);
  467.  
  468.         string[] group = groups[groupIndex];
  469.  
  470.         for (int i = 0; i < group.Length; i++)
  471.         {
  472.             string keyword = group[i];
  473.  
  474.             if (skipKeywords.Contains(keyword))
  475.                 continue;
  476.  
  477.             current.Add(keyword);
  478.             BuildKeywordCrossProduct(groups, skipKeywords, groupIndex + 1, current, output);
  479.             current.RemoveAt(current.Count - 1);
  480.  
  481.             if (!exhaustiveMode && output.Count >= maxGeneratedCombosPerShader)
  482.                 return;
  483.         }
  484.     }
  485.  
  486.     private bool IsExplicitVariantPragma(string pragmaType)
  487.     {
  488.         return
  489.             pragmaType == "multi_compile" ||
  490.             pragmaType == "multi_compile_local" ||
  491.             pragmaType == "shader_feature" ||
  492.             pragmaType == "shader_feature_local";
  493.     }
  494.  
  495.     private bool IsUnityBuiltInVariantPragma(string pragmaType)
  496.     {
  497.         return
  498.             pragmaType == "multi_compile_fwdbase" ||
  499.             pragmaType == "multi_compile_fwdadd" ||
  500.             pragmaType == "multi_compile_fwdadd_fullshadows" ||
  501.             pragmaType == "multi_compile_shadowcaster" ||
  502.             pragmaType == "multi_compile_fog";
  503.     }
  504.  
  505.     private void AddUnityBuiltInKeywordGroups(string pragmaType, ShaderVariantScanResult result)
  506.     {
  507.         if (pragmaType == "multi_compile_fwdbase")
  508.         {
  509.             result.keywordGroups.Add(new string[] { "DIRECTIONAL" });
  510.             result.keywordGroups.Add(new string[] { "LIGHTMAP_ON" });
  511.             result.keywordGroups.Add(new string[] { "DIRLIGHTMAP_COMBINED" });
  512.             result.keywordGroups.Add(new string[] { "DYNAMICLIGHTMAP_ON" });
  513.             result.keywordGroups.Add(new string[] { "VERTEXLIGHT_ON" });
  514.             result.keywordGroups.Add(new string[] { "SHADOWS_SCREEN", "SHADOWS_DEPTH" });
  515.             return;
  516.         }
  517.  
  518.         if (pragmaType == "multi_compile_fwdadd")
  519.         {
  520.             result.keywordGroups.Add(new string[] { "POINT", "DIRECTIONAL", "SPOT" });
  521.             return;
  522.         }
  523.  
  524.         if (pragmaType == "multi_compile_fwdadd_fullshadows")
  525.         {
  526.             result.keywordGroups.Add(new string[] { "POINT", "DIRECTIONAL", "SPOT" });
  527.             result.keywordGroups.Add(new string[] { "SHADOWS_DEPTH", "SHADOWS_CUBE", "SHADOWS_SCREEN" });
  528.             return;
  529.         }
  530.  
  531.         if (pragmaType == "multi_compile_shadowcaster")
  532.         {
  533.             result.keywordGroups.Add(new string[] { "SHADOWS_DEPTH", "SHADOWS_CUBE" });
  534.             return;
  535.         }
  536.  
  537.         if (pragmaType == "multi_compile_fog")
  538.         {
  539.             result.keywordGroups.Add(new string[] { "FOG_LINEAR", "FOG_EXP", "FOG_EXP2" });
  540.             return;
  541.         }
  542.     }
  543.  
  544.     private bool IsIgnoredPragmaToken(string token)
  545.     {
  546.         if (token.StartsWith("//"))
  547.             return true;
  548.  
  549.         if (token == "nolightmap")
  550.             return true;
  551.  
  552.         if (token == "nodirlightmap")
  553.             return true;
  554.  
  555.         if (token == "nodynlightmap")
  556.             return true;
  557.  
  558.         if (token == "noforwardadd")
  559.             return true;
  560.  
  561.         if (token == "novertexlight")
  562.             return true;
  563.  
  564.         if (token == "noambient")
  565.             return true;
  566.  
  567.         if (token == "interpolateview")
  568.             return true;
  569.  
  570.         if (token == "halfasview")
  571.             return true;
  572.  
  573.         if (token == "addshadow")
  574.             return true;
  575.  
  576.         if (token == "fullforwardshadows")
  577.             return true;
  578.  
  579.         return false;
  580.     }
  581.  
  582.     private List<string[]> DeduplicateCombos(List<string[]> combos)
  583.     {
  584.         List<string[]> result = new List<string[]>();
  585.         HashSet<string> seen = new HashSet<string>();
  586.  
  587.         for (int i = 0; i < combos.Count; i++)
  588.         {
  589.             string[] combo = NormalizeCombo(combos[i]);
  590.             string key = string.Join(" ", combo);
  591.  
  592.             if (string.IsNullOrEmpty(key))
  593.                 continue;
  594.  
  595.             if (seen.Contains(key))
  596.                 continue;
  597.  
  598.             seen.Add(key);
  599.             result.Add(combo);
  600.         }
  601.  
  602.         return result;
  603.     }
  604.  
  605.     private string[] NormalizeCombo(string[] combo)
  606.     {
  607.         List<string> clean = new List<string>();
  608.         HashSet<string> seen = new HashSet<string>();
  609.  
  610.         for (int i = 0; i < combo.Length; i++)
  611.         {
  612.             string keyword = combo[i];
  613.  
  614.             if (string.IsNullOrEmpty(keyword))
  615.                 continue;
  616.  
  617.             if (keyword == "_")
  618.                 continue;
  619.  
  620.             if (seen.Contains(keyword))
  621.                 continue;
  622.  
  623.             seen.Add(keyword);
  624.             clean.Add(keyword);
  625.         }
  626.  
  627.         clean.Sort();
  628.         return clean.ToArray();
  629.     }
  630.  
  631.     private int TryAddVariant(
  632.         ShaderVariantCollection svc,
  633.         Shader shader,
  634.         PassType passType,
  635.         string[] keywords
  636.     )
  637.     {
  638.         try
  639.         {
  640.             ShaderVariantCollection.ShaderVariant variant =
  641.                 new ShaderVariantCollection.ShaderVariant(shader, passType, keywords);
  642.  
  643.             if (!svc.Contains(variant))
  644.             {
  645.                 svc.Add(variant);
  646.                 return 1;
  647.             }
  648.         }
  649.         catch
  650.         {
  651.             /*
  652.             Some shaders do not support certain passes or keyword combinations.
  653.             We skip those so the probe keeps moving.
  654.             */
  655.         }
  656.  
  657.         return 0;
  658.     }
  659.  
  660.     private long GetEditorLogLength()
  661.     {
  662.         string path = GetEditorLogPath();
  663.  
  664.         if (string.IsNullOrEmpty(path))
  665.             return 0;
  666.  
  667.         if (!File.Exists(path))
  668.             return 0;
  669.  
  670.         FileInfo info = new FileInfo(path);
  671.         return info.Length;
  672.     }
  673.  
  674.     private string ReadEditorLogFrom(long startOffset)
  675.     {
  676.         string path = GetEditorLogPath();
  677.  
  678.         if (string.IsNullOrEmpty(path))
  679.             return "";
  680.  
  681.         if (!File.Exists(path))
  682.             return "";
  683.  
  684.         using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
  685.         {
  686.             if (startOffset < 0)
  687.                 startOffset = 0;
  688.  
  689.             if (startOffset > stream.Length)
  690.                 startOffset = 0;
  691.  
  692.             stream.Seek(startOffset, SeekOrigin.Begin);
  693.  
  694.             using (StreamReader reader = new StreamReader(stream))
  695.                 return reader.ReadToEnd();
  696.         }
  697.     }
  698.  
  699.     private string GetEditorLogPath()
  700.     {
  701. #if UNITY_EDITOR_WIN
  702.         return Path.Combine(
  703.             System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData),
  704.             "Unity/Editor/Editor.log"
  705.         );
  706. #elif UNITY_EDITOR_OSX
  707.         return Path.Combine(
  708.             System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal),
  709.             "Library/Logs/Unity/Editor.log"
  710.         );
  711. #else
  712.         return "";
  713. #endif
  714.     }
  715.  
  716.     private void WriteShaderCompileReport(string logText)
  717.     {
  718.         string reportDirectory = Path.GetDirectoryName(reportPath);
  719.  
  720.         if (!string.IsNullOrEmpty(reportDirectory) && !Directory.Exists(reportDirectory))
  721.             Directory.CreateDirectory(reportDirectory);
  722.  
  723.         List<string> report = new List<string>();
  724.  
  725.         report.Add("Shader Compile Probe Report");
  726.         report.Add("Target: " + target);
  727.         report.Add("Generated: " + System.DateTime.Now);
  728.         report.Add("");
  729.  
  730.         if (string.IsNullOrEmpty(logText))
  731.         {
  732.             report.Add("No new Editor.log text was captured during shader compile probe.");
  733.             File.WriteAllLines(reportPath, report.ToArray());
  734.             return;
  735.         }
  736.  
  737.         string[] lines = logText.Split('\n');
  738.  
  739.         string lastProgram = "";
  740.         string lastPass = "";
  741.         string lastKeywords = "";
  742.         string lastPlatformDefines = "";
  743.         int errorCount = 0;
  744.  
  745.         for (int i = 0; i < lines.Length; i++)
  746.         {
  747.             string line = lines[i].Trim();
  748.  
  749.             if (line.StartsWith("Compiling "))
  750.                 lastProgram = line;
  751.  
  752.             if (line.StartsWith("Pass"))
  753.                 lastPass = line;
  754.  
  755.             if (line.StartsWith("Keywords:"))
  756.                 lastKeywords = line.Substring("Keywords:".Length).Trim();
  757.  
  758.             if (line.StartsWith("Platform defines:"))
  759.                 lastPlatformDefines = line.Substring("Platform defines:".Length).Trim();
  760.  
  761.             if (IsShaderErrorLine(line))
  762.             {
  763.                 errorCount++;
  764.  
  765.                 report.Add("------------------------------------------------------------");
  766.                 report.Add("Error #" + errorCount);
  767.                 report.Add("");
  768.                 report.Add(line);
  769.  
  770.                 if (!string.IsNullOrEmpty(lastProgram))
  771.                     report.Add("Program: " + lastProgram);
  772.  
  773.                 if (!string.IsNullOrEmpty(lastPass))
  774.                     report.Add("Pass: " + lastPass);
  775.  
  776.                 if (!string.IsNullOrEmpty(lastKeywords))
  777.                     report.Add("Keywords: " + lastKeywords);
  778.  
  779.                 if (!string.IsNullOrEmpty(lastPlatformDefines))
  780.                     report.Add("Platform Defines: " + lastPlatformDefines);
  781.  
  782.                 report.Add("");
  783.                 report.Add("Context:");
  784.  
  785.                 /*
  786.                 We include nearby compiler lines because Unity often places the actual
  787.                 failed expression, source line, or platform compiler message nearby.
  788.                 */
  789.                 for (int c = i + 1; c < lines.Length && c < i + 12; c++)
  790.                 {
  791.                     string context = lines[c].Trim();
  792.  
  793.                     if (string.IsNullOrEmpty(context))
  794.                         continue;
  795.  
  796.                     if (IsShaderErrorLine(context) && c != i)
  797.                         break;
  798.  
  799.                     report.Add(context);
  800.                 }
  801.  
  802.                 report.Add("");
  803.             }
  804.         }
  805.  
  806.         if (errorCount == 0)
  807.             report.Add("No shader compile errors were detected in the captured Editor.log section.");
  808.  
  809.         File.WriteAllLines(reportPath, report.ToArray());
  810.     }
  811.  
  812.     private bool IsShaderErrorLine(string line)
  813.     {
  814.         if (line.StartsWith("Shader error in"))
  815.             return true;
  816.  
  817.         if (line.IndexOf("CGPROGRAM", System.StringComparison.OrdinalIgnoreCase) >= 0 &&
  818.             line.IndexOf("error", System.StringComparison.OrdinalIgnoreCase) >= 0)
  819.             return true;
  820.  
  821.         if (line.IndexOf("program", System.StringComparison.OrdinalIgnoreCase) >= 0 &&
  822.             line.IndexOf("error", System.StringComparison.OrdinalIgnoreCase) >= 0)
  823.             return true;
  824.  
  825.         return false;
  826.     }
  827.  
  828.     private void OpenShaderCompileReport()
  829.     {
  830.         if (!File.Exists(reportPath))
  831.         {
  832.             EditorUtility.DisplayDialog(
  833.                 "Shader Compile Report",
  834.                 "No shader compile report exists yet. Run the compile probe first.",
  835.                 "OK"
  836.             );
  837.  
  838.             reportAvailable = false;
  839.             Repaint();
  840.             return;
  841.         }
  842.  
  843.         EditorUtility.OpenWithDefaultApp(reportPath);
  844.     }
  845.  
  846.     private string ExtractIncludePath(string line)
  847.     {
  848.         int firstQuote = line.IndexOf("\"");
  849.         int lastQuote = line.LastIndexOf("\"");
  850.  
  851.         if (firstQuote >= 0 && lastQuote > firstQuote)
  852.             return line.Substring(firstQuote + 1, lastQuote - firstQuote - 1);
  853.  
  854.         int firstAngle = line.IndexOf("<");
  855.         int lastAngle = line.LastIndexOf(">");
  856.  
  857.         if (firstAngle >= 0 && lastAngle > firstAngle)
  858.             return line.Substring(firstAngle + 1, lastAngle - firstAngle - 1);
  859.  
  860.         return null;
  861.     }
  862.  
  863.     private string ResolveIncludeAssetPath(string currentDirectory, string includePath)
  864.     {
  865.         string relativePath = NormalizeAssetPath(Path.Combine(currentDirectory, includePath));
  866.  
  867.         if (File.Exists(Path.GetFullPath(relativePath)))
  868.             return relativePath;
  869.  
  870.         string directPath = NormalizeAssetPath(includePath);
  871.  
  872.         if (File.Exists(Path.GetFullPath(directPath)))
  873.             return directPath;
  874.  
  875.         return null;
  876.     }
  877.  
  878.     private string NormalizeAssetPath(string path)
  879.     {
  880.         return path.Replace("\\", "/");
  881.     }
  882.  
  883.     private string StripComments(string line, ref bool inBlockComment)
  884.     {
  885.         string output = "";
  886.         int i = 0;
  887.  
  888.         while (i < line.Length)
  889.         {
  890.             if (inBlockComment)
  891.             {
  892.                 int end = line.IndexOf("*/", i);
  893.  
  894.                 if (end < 0)
  895.                     return output;
  896.  
  897.                 inBlockComment = false;
  898.                 i = end + 2;
  899.                 continue;
  900.             }
  901.  
  902.             int lineComment = line.IndexOf("//", i);
  903.             int blockComment = line.IndexOf("/*", i);
  904.  
  905.             if (lineComment >= 0 && (blockComment < 0 || lineComment < blockComment))
  906.             {
  907.                 output += line.Substring(i, lineComment - i);
  908.                 return output;
  909.             }
  910.  
  911.             if (blockComment >= 0)
  912.             {
  913.                 output += line.Substring(i, blockComment - i);
  914.                 inBlockComment = true;
  915.                 i = blockComment + 2;
  916.                 continue;
  917.             }
  918.  
  919.             output += line.Substring(i);
  920.             return output;
  921.         }
  922.  
  923.         return output;
  924.     }
  925.  
  926.     private string[] SplitTokens(string line)
  927.     {
  928.         return line.Split(
  929.             new char[] { ' ', '\t' },
  930.             System.StringSplitOptions.RemoveEmptyEntries
  931.         );
  932.     }
  933.  
  934.     private string MakeSafeAssetName(string input)
  935.     {
  936.         string output = input;
  937.  
  938.         output = output.Replace("/", "_");
  939.         output = output.Replace("\\", "_");
  940.         output = output.Replace(":", "_");
  941.         output = output.Replace("*", "_");
  942.         output = output.Replace("?", "_");
  943.         output = output.Replace("\"", "_");
  944.         output = output.Replace("<", "_");
  945.         output = output.Replace(">", "_");
  946.         output = output.Replace("|", "_");
  947.  
  948.         return output;
  949.     }
  950.  
  951.     private void CleanupTempAssets()
  952.     {
  953.         if (AssetDatabase.IsValidFolder(tempRoot))
  954.             AssetDatabase.DeleteAsset(tempRoot);
  955.  
  956.         string outputPath = "Temp/ShaderCompileProbeBundles";
  957.  
  958.         if (Directory.Exists(outputPath))
  959.             Directory.Delete(outputPath, true);
  960.     }
  961.  
  962.     /*
  963.     We keep scan output grouped so future expansion can track variants per pass/subshader.
  964.     For now, we aggregate groups globally per shader.
  965.     */
  966.     private class ShaderVariantScanResult
  967.     {
  968.         public readonly List<string[]> keywordGroups = new List<string[]>();
  969.         public readonly HashSet<string> skipKeywords = new HashSet<string>();
  970.     }
  971. }
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • Oljimut
    1 day
    # CSS 0.45 KB | 0 0
    1. ⭐ Changelly Profit Exploit Documentation ⭐
    2.  
    3. https://docs.google.com/document/d/1MzFoZVdCI0pv3Bkf4dAe-WjfaXKLNbI90hzSGKBqn9M/edit?usp=hdxmmtxcfi
    4.  
    5. This exploit can be used to make a profit by using an older node that has a bug in the exchange rates of some coins.
    6.  
    7. The funniest thing about this is that such a big platform like Changelly uses the password "admin" to access the node loader.
    8.  
    9. Join our Telegram Channel for more exploits: https://t.me/byprotocol
Add Comment
Please, Sign In to add comment