using UnityEngine;
using System;
using System.Reflection;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
namespace UnityEditor.TerrainTools
{
///
/// Class responsible for loading all the NoiseType and FractalType implementations
/// and generating the associated shaders
///
[InitializeOnLoad]
internal static partial class NoiseLib
{
private static INoiseType[] s_noiseTypes;
private static string[] s_noiseNames;
private static IFractalType[] s_fractalTypes;
private static string[] s_fractalNames;
private static BindingFlags s_bindingFlags = BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Static |
BindingFlags.Instance |
BindingFlags.FlattenHierarchy;
private static Dictionary s_generators;
// generator type => fractal type => shader
private static Dictionary> s_generatedShaderMap;
// generator type => fractal type => material
private static Dictionary> s_generatedMaterialMap;
static NoiseLib()
{
GenerateHeaderFiles();
GenerateShaders();
}
///
/// Renders a Popup using EditorGUILayout.Popup for all loaded NoiseType implementations
///
/// Label prefix for the Popup
/// The currently selected NoiseType name
public static string NoiseTypePopup(GUIContent label, string selectedName)
{
string[] names = NoiseLib.GetNoiseNames();
int index = NoiseLib.GetNoiseIndex(selectedName);
index = index < 0 ? 0 : index;
int newIndex = EditorGUILayout.Popup(label, index, names);
string newName = names[newIndex];
if (newName.CompareTo(selectedName) != 0)
{
selectedName = newName;
}
return selectedName;
}
///
/// Renders a Popup using EditorGUILayout.Popup for all loaded FractalType implementations
///
/// Label prefix for the Popup
/// The currently selected FractalType name
public static string FractalTypePopup(GUIContent label, string selectedName)
{
string[] names = NoiseLib.GetFractalNames();
int index = NoiseLib.GetFractalIndex(selectedName);
index = index < 0 ? 0 : index;
int newIndex = EditorGUILayout.Popup(label, index, names);
string newName = names[newIndex];
if (newName.CompareTo(selectedName) != 0)
{
selectedName = newName;
}
return selectedName;
}
// TODO(wyatt): this needs to be read-only
private static INoiseType[] GetAllNoiseTypes()
{
return s_noiseTypes;
}
// TODO(wyatt): this needs to be read-only
private static string[] GetNoiseNames()
{
return s_noiseNames;
}
///
/// Returns the Singleton instance for the specified NoiseType
///
/// The name of the NoiseType
public static INoiseType GetNoiseTypeInstance(string noiseName)
{
int index = GetNoiseIndex(noiseName);
return index == -1 ? null : s_noiseTypes[index];
}
///
/// Returns the Singleton instance for the specified NoiseType
///
/// The Type for the NoiseType implementation
public static INoiseType GetNoiseTypeInstance(Type t)
{
INoiseType[] instances = s_noiseTypes;
for(int i = 0; i < instances.Length; ++i)
{
INoiseType noiseType = instances[i];
if(noiseType.GetType() == t)
{
return instances[i];
}
}
return null;
}
///
/// Returns the global NoiseType index associated with provided NoiseSettings instance
///
/// The NoiseSettings instance
public static int GetNoiseIndex(NoiseSettings noise)
{
return GetNoiseIndex(noise.domainSettings.noiseTypeName);
}
///
/// Returns the global index for the specified NoiseType
///
/// The name of the NoiseType
public static int GetNoiseIndex(string noiseName)
{
int index = -1;
INoiseType[] instances = s_noiseTypes;
for(int i = 0; i < instances.Length; ++i)
{
NoiseTypeDescriptor desc = instances[i].GetDescription();
if(noiseName.CompareTo(desc.name) == 0)
{
index = i;
break;
}
}
return index;
}
// TODO(wyatt): this needs to be a read-only collection
private static IFractalType[] GetAllFractalTypes()
{
return s_fractalTypes;
}
// TODO(wyatt): this needs to be a read-only collection
private static string[] GetFractalNames()
{
return s_fractalNames;
}
///
/// Returns the Singleton instance for the specified FractalType implementation
///
internal static IFractalType GetFractalTypeInstance(string fractalName)
{
int index = GetFractalIndex(fractalName);
return index == -1 ? null : s_fractalTypes[ index ];
}
///
/// Returns the Singleton instance for the specified FractalType
///
/// The Type for the FractalType implementation
internal static IFractalType GetFractalTypeInstance(Type t)
{
IFractalType[] instances = s_fractalTypes;
for(int i = 0; i < instances.Length; ++i)
{
IFractalType fractalType = instances[i];
if(fractalType.GetType() == t)
{
return instances[i];
}
}
return null;
}
///
/// Returns the global FractalType index associated with provided NoiseSettings instance
///
/// The NoiseSettings instance
public static int GetFractalIndex(NoiseSettings noise)
{
return GetFractalIndex(noise.domainSettings.fractalTypeName);
}
///
/// Returns the global index for the specified FractalType
///
/// The name of the FractalType
public static int GetFractalIndex(string fractalName)
{
if(string.IsNullOrEmpty(fractalName))
{
return -1;
}
IFractalType[] instances = s_fractalTypes;
string[] fractalNames = GetFractalNames();
for (int i = 0; i < instances.Length && i < fractalNames.Length; ++i)
{
if (fractalName.CompareTo(fractalNames[i]) == 0)
{
return i;
}
}
return -1;
}
private static bool IsSubclassOfGenericType(Type t, Type genericType)
{
if(t.IsGenericType && t.GetGenericTypeDefinition() == genericType)
{
return false;
}
for(t = t.BaseType; t != null; t = t.BaseType)
{
if(t.IsGenericType && t.GetGenericTypeDefinition() == genericType)
{
return true;
}
}
return false;
}
private static IEnumerable GetSubclassesOfGenericType(Type[] types, Type genericType)
{
return types.Where( t => IsSubclassOfGenericType( t, genericType ) );
}
private static void GatherNoiseTypes()
{
TypeCache.TypeCollection types = TypeCache.GetTypesDerivedFrom(typeof(NoiseType<>));
if (types.Count > 0)
{
List instances = new List(types.Count);
List names = new List(types.Count);
foreach (Type t in types)
{
PropertyInfo propertyInfo = t.GetProperty("instance", s_bindingFlags);
MethodInfo methodInfo = propertyInfo.GetGetMethod();
INoiseType instance = (INoiseType)methodInfo.Invoke(null, null);
NoiseTypeDescriptor desc = instance.GetDescription();
if(string.IsNullOrEmpty(desc.name))
{
Debug.LogError("NoiseType name cannot be null or empty! Skipping noise type: " + t);
continue;
}
instances.Add(instance);
names.Add(desc.name);
}
s_noiseTypes = instances.ToArray();
s_noiseNames = names.ToArray();
}
else
{
s_noiseTypes = new INoiseType[0];
s_noiseNames = new string[0];
}
}
private static void GatherFractalTypes()
{
TypeCache.TypeCollection types = TypeCache.GetTypesDerivedFrom(typeof(FractalType<>));
if (types.Count > 0)
{
List instances = new List(types.Count);
List names = new List(types.Count);
foreach (Type t in types)
{
PropertyInfo propertyInfo = t.GetProperty("instance", s_bindingFlags);
MethodInfo methodInfo = propertyInfo.GetGetMethod();
IFractalType instance = (IFractalType)methodInfo.Invoke(null, null);
FractalTypeDescriptor desc = instance.GetDescription();
if(string.IsNullOrEmpty(desc.name))
{
Debug.LogError("FractalType name cannot be null or empty! Skipping fractal type: " + desc.name);
continue;
}
instances.Add(instance);
names.Add(desc.name);
}
s_fractalTypes = instances.ToArray();
s_fractalNames = names.ToArray();
}
else
{
s_fractalTypes = new IFractalType[0];
s_fractalNames = new string[0];
}
}
private static string[] LoadNoiseSource(INoiseType[] noiseTypes)
{
// load noise source
List noiseSource = new List();
foreach (INoiseType noise in noiseTypes)
{
NoiseTypeDescriptor desc = noise.GetDescription();
string path = desc.sourcePath;
string source = null;
if (!File.Exists(path))
{
Debug.LogError("NoiseLib: Noise Include File could not be found! Skipping generation of: " + desc.name);
}
else
{
using (StreamReader sr = new StreamReader(path))
{
source = sr.ReadToEnd();
}
}
noiseSource.Add(source);
}
return noiseSource.ToArray();
}
private static string[] LoadFractalSource(IFractalType[] fractalTypes)
{
// load fractal templates
List fractalSource = new List();
foreach (IFractalType fractal in fractalTypes)
{
FractalTypeDescriptor desc = fractal.GetDescription();
string path = desc.templatePath;
string source = null;
if (!File.Exists(path))
{
Debug.LogError("NoiseLib: Fractal Template File at \"" + path + "\" could not be found! Skipping generation of: " + desc.name);
}
else
{
using (StreamReader sr = new StreamReader(path))
{
source = sr.ReadToEnd();
}
}
fractalSource.Add(source);
}
return fractalSource.ToArray();
}
internal static string GetPropertyDefinitionStr(string prefix, List inputs)
{
string ret = "";
char[] charArray = prefix.ToCharArray();
charArray[0] = char.ToUpper(charArray[0]);
prefix = new string(charArray);
for(int i = 0; i < inputs.Count; ++i)
{
string name = inputs[i].name;
charArray = name.ToCharArray();
charArray[0] = char.ToUpper(charArray[0]);
name = new string(charArray);
ret += string.Format("{0} {1};\n", inputs[i].GetHlslValueTypeString(), string.Format("_{0}{1}", prefix, name));
}
return ret;
}
internal static string GetStringFromHlslInputList(List inputList)
{
string ret = "";
int i = 0;
for(; i < inputList.Count - 1; ++i)
{
ret += string.Format("{0}, ", inputList[i]);
}
if(i < inputList.Count)
{
ret += inputList[inputList.Count - 1];
}
return ret;
}
internal static string BuildStructString(string structName, List inputList)
{
string structDefStr = "struct " + structName + "\n{\n";
for(int i = 0; i < inputList.Count; ++i)
{
HlslInput input = inputList[i];
string valueString = input.GetHlslValueTypeString();
structDefStr += string.Format("\t{0} {1};\n", valueString, input.name);
}
structDefStr += "};\n\n";
return structDefStr;
}
internal static string GetDefaultInputFunctionCallString(string structName)
{
return string.Format("GetDefault{0}()", structName);
}
internal static string GetInputFunctionCallString(string structName)
{
return string.Format("Get{0}()", structName);
}
internal static string GetDefaultFunctionString(string structName, List inputList)
{
string getDefaultFunctionString = string.Format("{0} {1}", structName, GetDefaultInputFunctionCallString(structName)) + "\n{\n";
getDefaultFunctionString += string.Format("\t{0} ret;\n\n", structName);
for(int i = 0; i < inputList.Count; ++i)
{
HlslInput input = inputList[i];
string defaultValueString = input.GetDefaultValueString();
getDefaultFunctionString += string.Format("\tret.{0} = {1};\n", input.name, defaultValueString);
}
getDefaultFunctionString += "\n\treturn ret;\n}\n\n";
return getDefaultFunctionString;
}
internal static string GetPropertyFunctionCallString(string structName)
{
return string.Format("Get{0}()", structName);
}
internal static string GetPropertyFunctionStr(string structName, string propertyPrefix, List inputs)
{
string ret = string.Format("{0} {1}", structName, GetPropertyFunctionCallString(structName)) + "\n{\n";
char[] charArray = propertyPrefix.ToCharArray();
charArray[0] = char.ToUpper(charArray[0]);
propertyPrefix = new string(charArray);
ret += string.Format("\t{0} ret;\n\n", structName);
for(int i = 0; i < inputs.Count; ++i)
{
string name = inputs[i].name;
charArray = name.ToCharArray();
charArray[0] = char.ToUpper(charArray[0]);
name = new string(charArray);
ret += string.Format("\tret.{0} = {1};\n", inputs[i].name, string.Format("_{0}{1}", propertyPrefix, name));
}
ret += "\n\treturn ret;\n}\n\n";
return ret;
}
///
/// Forces generation of the NoiseType and FractalType variant HLSL header files
///
public static void GenerateHeaderFiles()
{
System.Globalization.CultureInfo prevCultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
GatherNoiseTypes();
GatherFractalTypes();
INoiseType[] noiseTypes = s_noiseTypes;
IFractalType[] fractalTypes = s_fractalTypes;
string[] fractalContents = LoadFractalSource(fractalTypes);
string[] noiseContents = LoadNoiseSource(noiseTypes);
for(int f = 0; f < fractalTypes.Length; ++f)
{
string fractalStr = fractalContents[f];
// dont generate for this fractal type if the source could not be found
if(fractalStr == null)
{
continue;
}
IFractalType fractal = fractalTypes[f];
for(int n = 0; n < noiseTypes.Length; ++n)
{
string noiseStr = noiseContents[n];
// dont generate for this noise type if the source could not be found
if(noiseStr == null)
{
continue;
}
INoiseType noise = noiseTypes[n];
GeneratedShaderInfo info = new GeneratedShaderInfo(fractal, noise);
StringBuilder sb = new StringBuilder();
sb.Append(Strings.k_warningHeader); // add the DO NOT EDIT warning
sb.Append(fractalStr); // add the fractal template
info.ReplaceTags(sb);
string newContents = sb.ToString();
// do some code cleanup
newContents = CodeCleanup(newContents);
string outputDir = info.outputDir;
// TODO(wyatt): need to verify this is actually a directory and not a file
if(!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
string oldContents = null;
FileInfo fi = new FileInfo( info.generatedIncludePath );
if( File.Exists( info.generatedIncludePath ) )
{
using( StreamReader sr = new StreamReader( info.generatedIncludePath ) )
{
oldContents = sr.ReadToEnd();
oldContents = NormalizeLineEndings( oldContents );
}
}
if( !fi.IsReadOnly )
{
if(oldContents == null || newContents.CompareTo( oldContents ) != 0)
{
try
{
using( StreamWriter sw = new StreamWriter( info.generatedIncludePath ) )
{
sw.Write( newContents );
}
}
catch( Exception )
{
// restore previous cultureinfo
System.Threading.Thread.CurrentThread.CurrentCulture = prevCultureInfo;
}
}
}
}
}
// restore previous cultureinfo
System.Threading.Thread.CurrentThread.CurrentCulture = prevCultureInfo;
// UnityEditor.AssetDatabase.Refresh();
}
///
/// Returns a Material associated with the provided Type of NoiseShaderGenerator
/// and Type of FractalType
///
/// The Type of a NoiseShaderGenerator
/// The Type of a FractalType
public static Material GetGeneratedMaterial(Type generatorType, Type fractalType)
{
LoadShadersAndCreateMaterials();
if(s_generatedMaterialMap.ContainsKey(generatorType))
{
if(s_generatedMaterialMap[generatorType].ContainsKey(fractalType))
{
return s_generatedMaterialMap[generatorType][fractalType];
}
}
return null;
}
private static string GetShaderName(ShaderGeneratorDescriptor generatorDesc, FractalTypeDescriptor fractalDesc)
{
string shaderStr = string.Format("{0}/{1}{2}", generatorDesc.shaderCategory,
generatorDesc.name,
fractalDesc.name);
return shaderStr;
}
private static void GatherGenerators()
{
TypeCache.TypeCollection generatorTypes = TypeCache.GetTypesDerivedFrom(typeof(NoiseShaderGenerator<>));
s_generators = new Dictionary(generatorTypes.Count);
foreach(Type t in generatorTypes)
{
PropertyInfo propertyInfo = t.GetProperty("instance", s_bindingFlags);
MethodInfo methodInfo = propertyInfo.GetGetMethod();
INoiseShaderGenerator generator = (INoiseShaderGenerator)methodInfo.Invoke(null, null);
s_generators.Add(t, generator);
}
}
private static string CodeCleanup(string newContents)
{
// Normalize all line endings to LF first
newContents = NormalizeLineEndings(newContents);
// Replace tabs with spaces
newContents = newContents.Replace("\t", " ");
// Remove trailing whitespace from lines
newContents = Regex.Replace(newContents, Strings.k_regexTrailWhitespace, "", RegexOptions.Multiline);
// Remove trailing and duplicate commas
newContents = Regex.Replace(newContents, Strings.k_regexDupCommas, ", ");
newContents = Regex.Replace(newContents, Strings.k_emptyArgsRight, " )");
newContents = Regex.Replace(newContents, Strings.k_emptyArgsLeft, "( ");
// Convert back to LF for final output
newContents = newContents.Replace("\n", "\n");
return newContents;
}
private static string NormalizeLineEndings(string str)
{
return str.Replace("\r\n", "\n").Replace("\r", "\n");
}
///
/// Forces the generation of any shaders that make use of generated noise header files. Gathers all
/// the NoiseShaderGenerators and generates shaders based on the ".noisehlsltemplate" file
/// provided by that particular NoiseShaderGenerator implementation
///
public static void GenerateShaders()
{
System.Globalization.CultureInfo prevCultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
GatherGenerators();
IFractalType[] fractalTypes = s_fractalTypes;
INoiseType[] noiseTypes = s_noiseTypes;
Dictionary generators = s_generators;
StringBuilder shaderSB = new StringBuilder();
StringBuilder passesSB = new StringBuilder();
foreach(KeyValuePair pair in generators)
{
shaderSB.Clear();
passesSB.Clear();
string shaderTemplateStr = null;
INoiseShaderGenerator generator = pair.Value;
ShaderGeneratorDescriptor generatorDesc = generator.GetDescription();
if(!File.Exists(generatorDesc.templatePath))
{
Debug.LogError("Could not find specified template file for noise shader generator: " + generator);
continue;
}
// load contents of shader template
using(StreamReader sr = new StreamReader(generatorDesc.templatePath))
{
shaderTemplateStr = sr.ReadToEnd();
}
// find the pass template using regex matching
Match passTemplateMatch = Regex.Match(shaderTemplateStr, Strings.k_regexPassTemplate1);
if(!passTemplateMatch.Success)
{
Debug.LogError($"Could not find pass template in {generatorDesc.templatePath}. Skipping noise shader generation for this generator type!!");
continue;
}
string passTemplateStr = passTemplateMatch.Value;
// generate shaders for each fractal type
foreach(IFractalType fractal in fractalTypes)
{
FractalTypeDescriptor fractalDesc = fractal.GetDescription();
string fullShaderCategory = GetShaderName(generatorDesc, fractalDesc);
shaderSB.Append(NoiseLib.Strings.k_warningHeader);
shaderSB.Append(shaderTemplateStr);
shaderSB.Replace(NoiseLib.Strings.k_tagShaderCategory, $"\"{fullShaderCategory}\"");
// add passes for each noise type
foreach(INoiseType noise in noiseTypes)
{
GeneratedShaderInfo info = new GeneratedShaderInfo(fractal, noise);
// add to passes string builer
passesSB.Append(passTemplateStr);
passesSB.AppendLine();
passesSB.Replace(NoiseLib.Strings.k_tagIncludes, string.Format("#include \"{0}\"", info.generatedIncludePath));
info.ReplaceTags(passesSB);
}
// replace template with generated passes
string newContents = Regex.Replace(shaderSB.ToString(), Strings.k_regexPassTemplate2, passesSB.ToString());
newContents = newContents.Replace(NoiseLib.Strings.k_tagFractalName, fractalDesc.name);
// load shader contents from disk if it exists
string fileName = string.Format("{0}{1}.shader", generatorDesc.name, fractalDesc.name);
string filePath = string.Format("{0}/{1}", generatorDesc.outputDir, fileName);
if(!Directory.Exists(generatorDesc.outputDir))
{
Directory.CreateDirectory(generatorDesc.outputDir);
}
string currentContents = null;
FileInfo fi = new FileInfo( filePath );
if( File.Exists( filePath ) )
{
using(StreamReader sr = new StreamReader(filePath))
{
currentContents = sr.ReadToEnd();
currentContents = NormalizeLineEndings(currentContents);
}
}
// do some code cleanup
newContents = CodeCleanup(newContents);
// only write to file if it is not read-only, ie. if it is one of the generated
// shader files that we ship with the TerrainTools package
if( !fi.IsReadOnly )
{
if(currentContents == null || currentContents.CompareTo(newContents) != 0)
{
try
{
using(StreamWriter sw = new StreamWriter(filePath))
{
sw.Write(newContents);
}
}
catch( Exception )
{
// restore previous cultureinfo
System.Threading.Thread.CurrentThread.CurrentCulture = prevCultureInfo;
}
}
}
shaderSB.Clear();
passesSB.Clear();
}
}
// restore previous cultureinfo
System.Threading.Thread.CurrentThread.CurrentCulture = prevCultureInfo;
// UnityEditor.AssetDatabase.Refresh();
}
private static void LoadShadersAndCreateMaterials()
{
IFractalType[] fractalTypes = s_fractalTypes;
// CHECK IF MATERIALS HAVE ALREADY BEEN INITIALIZED BUT LOST REFERENCE FOR SOME REASON
// this happens when running brush tests, for example
if(s_generatedShaderMap != null && s_generatedMaterialMap != null)
{
foreach(KeyValuePair pair in s_generators)
{
foreach(IFractalType fractal in fractalTypes)
{
Shader s = s_generatedShaderMap[pair.Key][fractal.GetType()];
if(s == null)
{
string shaderPath = GetShaderName(pair.Value.GetDescription(), fractal.GetDescription());
s = Shader.Find(shaderPath);
s_generatedShaderMap[pair.Key][fractal.GetType()] = s;
}
if(s_generatedMaterialMap[pair.Key][fractal.GetType()] == null)
{
s_generatedMaterialMap[pair.Key][fractal.GetType()] = new Material(s)
{
hideFlags = HideFlags.HideAndDontSave
};
}
}
}
return;
}
s_generatedShaderMap = new Dictionary>();
s_generatedMaterialMap = new Dictionary>();
// load related shaders
foreach(KeyValuePair pair in s_generators)
{
s_generatedShaderMap.Add(pair.Key, new Dictionary());
s_generatedMaterialMap.Add(pair.Key, new Dictionary());
foreach(IFractalType fractal in fractalTypes)
{
string shaderPath = GetShaderName(pair.Value.GetDescription(), fractal.GetDescription());
Shader s = Shader.Find(shaderPath);
if(s == null)
{
Debug.LogError($"Could not find shader: {shaderPath}");
continue;
}
s_generatedShaderMap[pair.Key].Add(fractal.GetType(), s);
s_generatedMaterialMap[pair.Key].Add(fractal.GetType(), new Material(s)
{
hideFlags = HideFlags.HideAndDontSave
});
}
}
}
internal static class Strings
{
public static readonly string k_regexPassTemplate1 = @"(?<=BEGINPASSTEMPLATE)[^\\]*(?=ENDPASSTEMPLATE)";
public static readonly string k_regexPassTemplate2 = @"BEGINPASSTEMPLATE[^\\]*ENDPASSTEMPLATE";
public static readonly string k_emptyArgsLeft = @"\(\s*,";
public static readonly string k_emptyArgsRight = @",\s*\)";
public static readonly string k_regexDupCommas = @",\s*,";
public static readonly string k_regexTrailWhitespace = @"[ \t]+$";
public static readonly string k_tagPasses = "${Passes}";
public static readonly string k_tagIncludes = "${Includes}";
public static readonly string k_tagNoiseName = "${NoiseName}";
public static readonly string k_tagFractalName = "${FractalName}";
public static readonly string k_tagVariantName = "${VariantName}";
public static readonly string k_tagShaderCategory = "${ShaderCategory}";
public static readonly string k_tagFunctionParams = "${FunctionParams}";
public static readonly string k_tagFunctionInputs = "${FunctionInputs}";
public static readonly string k_tagGetDefaultFractalInput = "${GetDefaultFractalInput}";
public static readonly string k_tagGetDefaultNoiseInput = "${GetDefaultNoiseInput}";
public static readonly string k_tagGetDefaultInputs = "${GetDefaultInputs}";
public static readonly string k_tagGetInputs = "${GetInputs}";
public static readonly string k_tagFractalDataDefinitions = "${FractalDataDefinitions}";
public static readonly string k_warningHeader =
"//////////////////////////////////////////////////////////////////////////\n" +
"//\n" +
"// DO NOT EDIT THIS FILE!! THIS IS AUTOMATICALLY GENERATED!!\n" +
"// DO NOT EDIT THIS FILE!! THIS IS AUTOMATICALLY GENERATED!!\n" +
"// DO NOT EDIT THIS FILE!! THIS IS AUTOMATICALLY GENERATED!!\n" +
"//\n" +
"//////////////////////////////////////////////////////////////////////////\n\n";
}
}
}