/// <summary> /// Initialize the config system with the given types /// </summary> public static bool ReadConfigFiles() { // Find all the configurable types List <Type> ConfigTypes = FindConfigurableTypes(); // Find all the input files FileReference[] InputFiles = FindInputFiles().Select(x => x.Location).ToArray(); // Get the path to the cache file FileReference CacheFile = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Intermediate", "Build", "XmlConfigCache.bin"); FileReference SchemaFile = GetSchemaLocation(); // Try to read the existing cache from disk XmlConfigData CachedValues; if (IsCacheUpToDate(CacheFile, InputFiles) && FileReference.Exists(SchemaFile)) { if (XmlConfigData.TryRead(CacheFile, ConfigTypes, out CachedValues) && Enumerable.SequenceEqual(InputFiles, CachedValues.InputFiles)) { Values = CachedValues; } } // If that failed, regenerate it if (Values == null) { // Find all the configurable fields from the given types Dictionary <string, Dictionary <string, FieldInfo> > CategoryToFields = new Dictionary <string, Dictionary <string, FieldInfo> >(); FindConfigurableFields(ConfigTypes, CategoryToFields); // Create a schema for the config files XmlSchema Schema = CreateSchema(CategoryToFields); WriteSchema(Schema, SchemaFile); // Read all the XML files and validate them against the schema Dictionary <Type, Dictionary <FieldInfo, object> > TypeToValues = new Dictionary <Type, Dictionary <FieldInfo, object> >(); foreach (FileReference InputFile in InputFiles) { if (!TryReadFile(InputFile, CategoryToFields, TypeToValues, Schema)) { Log.TraceError("Failed to properly read XML file : {0}", InputFile.FullName); return(false); } } // Create the new cache Values = new XmlConfigData(InputFiles, TypeToValues.ToDictionary(x => x.Key, x => x.Value.ToArray())); Values.Write(CacheFile); } // Apply all the static field values foreach (KeyValuePair <Type, KeyValuePair <FieldInfo, object>[]> TypeValuesPair in Values.TypeToValues) { foreach (KeyValuePair <FieldInfo, object> FieldValuePair in TypeValuesPair.Value) { if (FieldValuePair.Key.IsStatic) { object Value = InstanceValue(FieldValuePair.Value, FieldValuePair.Key.FieldType); FieldValuePair.Key.SetValue(null, Value); } } } return(true); }
/// <summary> /// Initialize the config system with the given types /// </summary> /// <param name="OverrideCacheFile">Force use of the cached XML config without checking if it's valid (useful for remote builds)</param> public static void ReadConfigFiles(FileReference OverrideCacheFile) { // Find all the configurable types List <Type> ConfigTypes = FindConfigurableTypes(); // Update the cache if necessary if (OverrideCacheFile != null) { // Set the cache file to the overriden value CacheFile = OverrideCacheFile; // Never rebuild the cache; just try to load it. if (!XmlConfigData.TryRead(CacheFile, ConfigTypes, out Values)) { throw new BuildException("Unable to load XML config cache ({0})", CacheFile); } } else { // Get the default cache file CacheFile = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Intermediate", "Build", "XmlConfigCache.bin"); if (UnrealBuildTool.IsEngineInstalled()) { DirectoryReference UserSettingsDir = Utils.GetUserSettingDirectory(); if (UserSettingsDir != null) { CacheFile = FileReference.Combine(UserSettingsDir, "UnrealEngine", String.Format("XmlConfigCache-{0}.bin", UnrealBuildTool.RootDirectory.FullName.Replace(":", "").Replace(Path.DirectorySeparatorChar, '+'))); } } // Find all the input files FileReference[] InputFiles = FindInputFiles().Select(x => x.Location).ToArray(); // Get the path to the schema FileReference SchemaFile = GetSchemaLocation(); // Try to read the existing cache from disk XmlConfigData CachedValues; if (IsCacheUpToDate(CacheFile, InputFiles) && FileReference.Exists(SchemaFile)) { if (XmlConfigData.TryRead(CacheFile, ConfigTypes, out CachedValues) && Enumerable.SequenceEqual(InputFiles, CachedValues.InputFiles)) { Values = CachedValues; } } // If that failed, regenerate it if (Values == null) { // Find all the configurable fields from the given types Dictionary <string, Dictionary <string, FieldInfo> > CategoryToFields = new Dictionary <string, Dictionary <string, FieldInfo> >(); FindConfigurableFields(ConfigTypes, CategoryToFields); // Create a schema for the config files XmlSchema Schema = CreateSchema(CategoryToFields); if (!UnrealBuildTool.IsEngineInstalled()) { WriteSchema(Schema, SchemaFile); } // Read all the XML files and validate them against the schema Dictionary <Type, Dictionary <FieldInfo, object> > TypeToValues = new Dictionary <Type, Dictionary <FieldInfo, object> >(); foreach (FileReference InputFile in InputFiles) { if (!TryReadFile(InputFile, CategoryToFields, TypeToValues, Schema)) { throw new BuildException("Failed to properly read XML file : {0}", InputFile.FullName); } } // Make sure the cache directory exists DirectoryReference.CreateDirectory(CacheFile.Directory); // Create the new cache Values = new XmlConfigData(InputFiles, TypeToValues.ToDictionary(x => x.Key, x => x.Value.ToArray())); Values.Write(CacheFile); } } // Apply all the static field values foreach (KeyValuePair <Type, KeyValuePair <FieldInfo, object>[]> TypeValuesPair in Values.TypeToValues) { foreach (KeyValuePair <FieldInfo, object> FieldValuePair in TypeValuesPair.Value) { if (FieldValuePair.Key.IsStatic) { object Value = InstanceValue(FieldValuePair.Value, FieldValuePair.Key.FieldType); FieldValuePair.Key.SetValue(null, Value); } } } }
/// <summary> /// Attempts to read a previous block of config values from disk /// </summary> /// <param name="Location">The file to read from</param> /// <param name="Types">Array of valid types. Used to resolve serialized type names to concrete types.</param> /// <param name="Data">On success, receives the parsed data</param> /// <returns>True if the data was read and is valid</returns> public static bool TryRead(FileReference Location, IEnumerable <Type> Types, out XmlConfigData Data) { // Check the file exists first if (!FileReference.Exists(Location)) { Data = null; return(false); } // Read the cache from disk using (BinaryReader Reader = new BinaryReader(File.Open(Location.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))) { // Check the serialization version matches if (Reader.ReadInt32() != SerializationVersion) { Data = null; return(false); } // Read the input files FileReference[] InputFiles = Reader.ReadArray(() => Reader.ReadFileReference()); // Read the types int NumTypes = Reader.ReadInt32(); Dictionary <Type, KeyValuePair <FieldInfo, object>[]> TypeToValues = new Dictionary <Type, KeyValuePair <FieldInfo, object>[]>(NumTypes); for (int TypeIdx = 0; TypeIdx < NumTypes; TypeIdx++) { // Read the type name string TypeName = Reader.ReadString(); // Try to find it in the list of configurable types Type Type = Types.FirstOrDefault(x => x.Name == TypeName); if (Type == null) { Data = null; return(false); } // Read all the values KeyValuePair <FieldInfo, object>[] Values = new KeyValuePair <FieldInfo, object> [Reader.ReadInt32()]; for (int ValueIdx = 0; ValueIdx < Values.Length; ValueIdx++) { string FieldName = Reader.ReadString(); // Find the matching field on the output type FieldInfo Field = Type.GetField(FieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); if (Field == null || Field.GetCustomAttribute <XmlConfigFileAttribute>() == null) { Data = null; return(false); } // Try to parse the value and add it to the output array object Value = Reader.ReadObject(Field.FieldType); Values[ValueIdx] = new KeyValuePair <FieldInfo, object>(Field, Value); } // Add it to the type map TypeToValues.Add(Type, Values); } // Return the parsed data Data = new XmlConfigData(InputFiles.ToArray(), TypeToValues); return(true); } }