using System.Collections.Generic; using UnityEditor.Build; using UnityEditor.Build.Reporting; using UnityEngine; namespace UnityEditor.XR.OpenXR.Features { /// /// The Boot Config builder exposes a centralized call-site for populating BootConfig options. /// public class BootConfigBuilder { private struct SettingEntry { public bool IsDirty; public string Setting; } private readonly Dictionary _bootConfigSettings; /// /// Internal constructor. Should only ever get called inside this assembly. More to the point, it should only ever /// get called inside /// internal BootConfigBuilder() { _bootConfigSettings = new Dictionary(); } /// /// Populate the boot config settings from the current EditorUserBuildSettings based on the BuildReport. /// If or have been called before this call, we do not overwrite /// the value set, as we assume that these were meant to be the new, updated values. /// /// The BuildReport load the bootconfig from. internal void ReadBootConfig(BuildReport report) { var bootConfig = new BootConfig(report); bootConfig.ReadBootConfig(); foreach (var setting in bootConfig.Settings) { // only update the boot config if the key doesn't currently live in _bootConfigSettings // We may have updated _bootConfigSettings before we've called `ReadBootConfig`. If that is the case, // this value overrides what's in the boot config. if (!_bootConfigSettings.TryGetValue(setting.Key, out var entry)) _bootConfigSettings[setting.Key] = new SettingEntry { IsDirty = false, Setting = setting.Value }; } } /// /// To ensure we don't have any lingering values carried over into the next build, we clear out the current /// boot config as part of the post build step. /// Any setting that we have added via a or will be cleaned up. /// Any setting that was already in the boot config will not be removed. /// /// internal void ClearAndWriteBootConfig(BuildReport report) { var bootConfig = new BootConfig(report); bootConfig.ReadBootConfig(); foreach (var entry in _bootConfigSettings) { if (entry.Value.IsDirty) bootConfig.ClearEntryForKeyAndValue(entry.Key, entry.Value.Setting); } bootConfig.WriteBootConfig(); _bootConfigSettings.Clear(); } /// /// Write the current boot config. /// Since we can override the , methods, /// we cannot guarantee this method will get called, nor the order in which this method can be called. If you override these methods, /// unless you call the base methods last, you'll want to invoke this method manually. /// /// Build report that we want to write internal void WriteBootConfig(BuildReport report) { // don't bother writing out if there's no boot config settings, or there isn't an OpenXR loader active. if (_bootConfigSettings.Count <= 0) return; var bootConfig = new BootConfig(report); bootConfig.ReadBootConfig(); foreach (var entry in _bootConfigSettings) { // We only want to clean up the entries that we've added in this build processor. // Any other entries, we want to leave as is. if (entry.Value.IsDirty) bootConfig.SetValueForKey(entry.Key, entry.Value.Setting); } bootConfig.WriteBootConfig(); } /// /// Method for setting a specific boot config option, given the key and the string value to store. /// /// Key of the value to be stored /// String value to write to the key /// True if we are able to set the config value, otherwise returns false public bool SetBootConfigValue(string key, string value) { if (string.IsNullOrEmpty(key)) { Debug.LogError("Cannot write a boot config value with an empty key."); return false; } _bootConfigSettings[key] = new SettingEntry { IsDirty = true, Setting = value }; return true; } /// /// Method for setting a specific BOOLEAN config option. This method ensures a consistent method for writing a boolean value /// /// Key of the value to be stored /// Boolean value to set /// If the `key` existing in the boot config and it's "1", return true. Otherwise return false. public bool SetBootConfigBoolean(string key, bool value) { if (string.IsNullOrEmpty(key)) { Debug.LogError("Cannot write a boot config with an empty key"); return false; } _bootConfigSettings[key] = new SettingEntry { IsDirty = true, Setting = value ? "1" : "0" }; return true; } /// /// Get a config value from the boot config, given a specific key. /// /// Key we want to locate in the boot config /// Where we store the result. /// true if we find the key in the bootconfig, otherwise we return false public bool TryGetBootConfigValue(string key, out string value) { if (string.IsNullOrEmpty(key)) { Debug.LogError("Cannot write a boot config with an empty key"); value = null; return false; } bool result = _bootConfigSettings.TryGetValue(key, out var entry); value = result ? entry.Setting : null; return result; } /// /// Return a boolean based on the value stored at `key` /// /// key to look for in the boot config /// Where we store the result. /// true if we find the key in the bootconfig, otherwise we return false public bool TryGetBootConfigBoolean(string key, out bool value) { if (string.IsNullOrEmpty(key)) { Debug.LogError("Cannot perform a look up with a null or empty string key"); value = false; return false; } bool result = _bootConfigSettings.TryGetValue(key, out var entry); value = result && entry.Setting.Equals("1"); return result; } /// /// Try and remove an entry from the boot config. /// /// The key to attempt to remove /// true if we were able to remove the boot config entry, otherwise false public bool TryRemoveBootConfigEntry(string key) { if (string.IsNullOrEmpty(key) || !_bootConfigSettings.ContainsKey(key)) { return false; } return _bootConfigSettings.Remove(key); } } }