/// <summary> /// /// </summary> /// <param name="InDirectory"></param> /// <param name="InTargetPlatform"></param> /// <param name="OutRSAKeys"></param> /// <param name="OutAESKey"></param> public static void ParseEncryptionIni(DirectoryReference InDirectory, UnrealTargetPlatform InTargetPlatform, out String[] OutRSAKeys, out String OutAESKey) { OutAESKey = String.Empty; OutRSAKeys = null; ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Encryption, InDirectory, InTargetPlatform); bool bSigningEnabled; Ini.GetBool("Core.Encryption", "SignPak", out bSigningEnabled); if (bSigningEnabled) { OutRSAKeys = new string[3]; Ini.GetString("Core.Encryption", "rsa.privateexp", out OutRSAKeys[0]); Ini.GetString("Core.Encryption", "rsa.modulus", out OutRSAKeys[1]); Ini.GetString("Core.Encryption", "rsa.publicexp", out OutRSAKeys[2]); if (String.IsNullOrEmpty(OutRSAKeys[0]) || String.IsNullOrEmpty(OutRSAKeys[1]) || String.IsNullOrEmpty(OutRSAKeys[2])) { OutRSAKeys = null; } } bool bEncryptionEnabled; Ini.GetBool("Core.Encryption", "EncryptPak", out bEncryptionEnabled); if (bEncryptionEnabled) { Ini.GetString("Core.Encryption", "aes.key", out OutAESKey); } }
public static List <string> GetShippingArchitectures(ConfigHierarchy Ini) { List <string> ProjectArches = new List <string>(); bool bBuild = true; if (Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bShipForArmV7", out bBuild) && bBuild) { ProjectArches.Add("armv7"); } if (Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bShipForArm64", out bBuild) && bBuild) { ProjectArches.Add("arm64"); } if (Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bShipForArmV7S", out bBuild) && bBuild) { ProjectArches.Add("armv7s"); } // force armv7 if something went wrong if (ProjectArches.Count == 0) { ProjectArches.Add("armv7"); ProjectArches.Add("arm64"); } return(ProjectArches); }
public HTML5ToolChain(FileReference InProjectFile) : base(CppPlatform.HTML5) { if (!HTML5SDKInfo.IsSDKInstalled()) { throw new BuildException("HTML5 SDK is not installed; cannot use toolchain."); } // ini configs // - normal ConfigCache w/ UnrealBuildTool.ProjectFile takes all game config ini files // (including project config ini files) // - but, during packaging, if -remoteini is used -- need to use UnrealBuildTool.GetRemoteIniPath() // (note: ConfigCache can take null ProjectFile) string EngineIniPath = UnrealBuildTool.GetRemoteIniPath(); DirectoryReference ProjectDir = !String.IsNullOrEmpty(EngineIniPath) ? new DirectoryReference(EngineIniPath) : DirectoryReference.FromFile(InProjectFile); ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, ProjectDir, UnrealTargetPlatform.HTML5); // Ini.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "EnableSIMD", out enableSIMD); Ini.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "EnableMultithreading", out enableMultithreading); Ini.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "OffscreenCanvas", out bMultithreading_UseOffscreenCanvas); Ini.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "LLVMWasmBackend", out useLLVMwasmBackend); Ini.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "EnableTracing", out bEnableTracing); if (useLLVMwasmBackend) { libExt = ".a"; // experimental - LLVMWasmBackend } // TODO: remove this "fix" when emscripten supports WASM with SIMD enableSIMD = false; Log.TraceInformation("HTML5ToolChain: EnableSIMD = " + enableSIMD); Log.TraceInformation("HTML5ToolChain: EnableMultithreading " + enableMultithreading); Log.TraceInformation("HTML5ToolChain: OffscreenCanvas " + bMultithreading_UseOffscreenCanvas); Log.TraceInformation("HTML5ToolChain: LLVMWasmBackend " + useLLVMwasmBackend); Log.TraceInformation("HTML5ToolChain: EnableTracing = " + bEnableTracing); PrintOnce = new VerbosePrint(PrintOnceOn); // reset Log.TraceInformation("Setting Emscripten SDK: located in " + HTML5SDKInfo.EMSCRIPTEN_ROOT); string TempDir = HTML5SDKInfo.SetupEmscriptenTemp(); HTML5SDKInfo.SetUpEmscriptenConfigFile(); if (Environment.GetEnvironmentVariable("EMSDK") == null) // If EMSDK is present, Emscripten is already configured by the developer { // If not using preset emsdk, configure our generated .emscripten config, instead of autogenerating one in the user's home directory. Environment.SetEnvironmentVariable("EM_CONFIG", HTML5SDKInfo.DOT_EMSCRIPTEN); Environment.SetEnvironmentVariable("EM_CACHE", HTML5SDKInfo.EMSCRIPTEN_CACHE); Environment.SetEnvironmentVariable("EMCC_TEMP_DIR", TempDir); } Log.TraceInformation("*** Emscripten Config File: " + Environment.GetEnvironmentVariable("EM_CONFIG")); }
private string GetElfName(FileReference InNMakeOutputPath) { ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, InNMakeOutputPath.Directory.ParentDirectory.ParentDirectory, UnrealTargetPlatform.Lumin); bool bUseMobileRendering = false; Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bUseMobileRendering", out bUseMobileRendering); bool bUseVulkan = false; Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bUseVulkan", out bUseVulkan); string OutputFileName = string.Format("{0}-arm64-{1}{2}", InNMakeOutputPath.GetFileNameWithoutExtension(), !(bUseMobileRendering || bUseVulkan) ? "lumingl4" : "lumin", InNMakeOutputPath.GetExtension()); return(OutputFileName); }
public HTML5ToolChain(FileReference InProjectFile) : base(CppPlatform.HTML5, WindowsCompiler.VisualStudio2015, false, false) { if (!HTML5SDKInfo.IsSDKInstalled()) { throw new BuildException("HTML5 SDK is not installed; cannot use toolchain."); } // ini configs // - normal ConfigCache w/ UnrealBuildTool.ProjectFile takes all game config ini files // (including project config ini files) // - but, during packaging, if -remoteini is used -- need to use UnrealBuildTool.GetRemoteIniPath() // (note: ConfigCache can take null ProjectFile) string EngineIniPath = UnrealBuildTool.GetRemoteIniPath(); DirectoryReference ProjectDir = !String.IsNullOrEmpty(EngineIniPath) ? new DirectoryReference(EngineIniPath) : DirectoryReference.FromFile(InProjectFile); ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, ProjectDir, UnrealTargetPlatform.HTML5); // these will be going away... bool targetingAsmjs = true; // inverted check bool targetWebGL1 = false; // inverted check if (Ini.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "TargetAsmjs", out targetingAsmjs)) { targetingWasm = !targetingAsmjs; } if (Ini.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "TargetWebGL1", out targetWebGL1)) { targetWebGL2 = !targetWebGL1; } Ini.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "EnableSIMD", out enableSIMD); Ini.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "EnableMultithreading", out enableMultithreading); Ini.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "EnableTracing", out bEnableTracing); Log.TraceInformation("HTML5ToolChain: TargetWasm = " + targetingWasm); Log.TraceInformation("HTML5ToolChain: TargetWebGL2 = " + targetWebGL2); Log.TraceInformation("HTML5ToolChain: EnableSIMD = " + enableSIMD); Log.TraceInformation("HTML5ToolChain: EnableMultithreading " + enableMultithreading); Log.TraceInformation("HTML5ToolChain: EnableTracing = " + bEnableTracing); // TODO: remove this "fix" when emscripten supports (SIMD & pthreads) + WASM if (targetingWasm) { enableSIMD = false; // TODO: double check Engine/Source/Runtime/Core/Private/HTML5/HTML5PlatformProcess.cpp::SupportsMultithreading() enableMultithreading = false; } PrintOnce = new VerbosePrint(PrintOnceOn); // reset }
protected XElement GetIdentity(out string IdentityName) { // Read the PackageName from config var PackageName = Regex.Replace(GetConfigString("PackageName", "ProjectName", "DefaultUE4Project"), "[^-.A-Za-z0-9]", ""); if (string.IsNullOrWhiteSpace(PackageName)) { Log.TraceError("Invalid package name {0}. Package names must only contain letters, numbers, dash, and period and must be at least one character long.", PackageName); Log.TraceError("Consider using the setting [{0}]:PackageName to provide a specific value.", IniSection_PlatformTargetSettings); } var PublisherName = GetConfigString("PublisherName", "CompanyDistinguishedName", "CN=NoPublisher"); var VersionNumber = GetConfigString("PackageVersion", "ProjectVersion", "1.0.0.0"); VersionNumber = ValidatePackageVersion(VersionNumber); // If specified in the project settings attempt to retrieve the current build number and increment the version number by that amount, accounting for overflows bool bIncludeEngineVersionInPackageVersion; if (EngineIni.GetBool(IniSection_PlatformTargetSettings, "bIncludeEngineVersionInPackageVersion", out bIncludeEngineVersionInPackageVersion) && bIncludeEngineVersionInPackageVersion) { VersionNumber = IncludeBuildVersionInPackageVersion(VersionNumber); } IdentityName = PackageName; return(new XElement(GetName("Identity", Schema2010NS), new XAttribute("Name", PackageName), new XAttribute("Publisher", PublisherName), new XAttribute("Version", VersionNumber))); }
private bool GetRemoveDebugInfo() { ConfigHierarchy Ini = GetConfigCacheIni(ConfigHierarchyType.Engine); bool Value = false; Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bRemoveDebugInfo", out Value); return(Value); }
public bool UseVulkan() { ConfigHierarchy Ini = GetConfigCacheIni(ConfigHierarchyType.Engine); bool Value = false; Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bUseVulkan", out Value); return(Value); }
protected static bool DoProjectSettingsMatchDefault(UnrealTargetPlatform Platform, DirectoryReference ProjectDirectoryName, string Section, string[] BoolKeys, string[] IntKeys, string[] StringKeys) { ConfigHierarchy ProjIni = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, ProjectDirectoryName, Platform); ConfigHierarchy DefaultIni = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, (DirectoryReference)null, Platform); // look at all bool values if (BoolKeys != null) { foreach (string Key in BoolKeys) { bool Default = false, Project = false; DefaultIni.GetBool(Section, Key, out Default); ProjIni.GetBool(Section, Key, out Project); if (Default != Project) { Log.TraceInformationOnce(Key + " is not set to default. (" + Default + " vs. " + Project + ")"); return(false); } } } // look at all int values if (IntKeys != null) { foreach (string Key in IntKeys) { int Default = 0, Project = 0; DefaultIni.GetInt32(Section, Key, out Default); ProjIni.GetInt32(Section, Key, out Project); if (Default != Project) { Log.TraceInformationOnce(Key + " is not set to default. (" + Default + " vs. " + Project + ")"); return(false); } } } // look for all string values if (StringKeys != null) { foreach (string Key in StringKeys) { string Default = "", Project = ""; DefaultIni.GetString(Section, Key, out Default); ProjIni.GetString(Section, Key, out Project); if (Default != Project) { Log.TraceInformationOnce(Key + " is not set to default. (" + Default + " vs. " + Project + ")"); return(false); } } } // if we get here, we match all important settings return(true); }
public bool UseVulkan() { // @todo Lumin: Switch to Lumin once we have sub-platform ini ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(ProjectFile), UnrealTargetPlatform.Android); // go by string bool bUseVulkan = true; Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bUseVulkan", out bUseVulkan); return(bUseVulkan); }
public bool UseVulkan() { DirectoryReference DirRef = (!string.IsNullOrEmpty(UnrealBuildTool.GetRemoteIniPath()) ? new DirectoryReference(UnrealBuildTool.GetRemoteIniPath()) : (ProjectFile != null ? ProjectFile.Directory : null)); ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirRef, UnrealTargetPlatform.Lumin); // go by string bool bUseVulkan = true; Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bUseVulkan", out bUseVulkan); return(bUseVulkan); }
private manifestApplicationComponentType GetApplicationType() { ConfigHierarchy Ini = GetConfigCacheIni(ConfigHierarchyType.Engine); bool Value = false; Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bIsScreensApp", out Value); if (Value) { return(manifestApplicationComponentType.ScreensImmersive); } return(manifestApplicationComponentType.Fullscreen); }
private string GetApplicationType() { ConfigHierarchy Ini = GetConfigCacheIni(ConfigHierarchyType.Engine); bool Value = false; Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bIsScreensApp", out Value); if (Value) { return("ScreensImmersive"); } return("Fullscreen"); }
/// <summary> /// Setup the target environment for building /// </summary> /// <param name="InBuildTarget"> The target being built</param> public override void SetUpEnvironment(UEBuildTarget InBuildTarget) { InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("PLATFORM_IOS=1"); InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("PLATFORM_APPLE=1"); InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("WITH_TTS=0"); InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("WITH_SPEECH_RECOGNITION=0"); InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("WITH_DATABASE_SUPPORT=0"); InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("WITH_EDITOR=0"); InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("USE_NULL_RHI=0"); InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("REQUIRES_ALIGNED_INT_ACCESS"); ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(ProjectFile), UnrealTargetPlatform.IOS); Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableRemoteNotificationsSupport", out bNotificationsEnabled); if (bNotificationsEnabled) { InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("NOTIFICATIONS_ENABLED=1"); } else { InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("NOTIFICATIONS_ENABLED=0"); } if (GetActiveArchitecture() == "-simulator") { InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("WITH_SIMULATOR=1"); } else { InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("WITH_SIMULATOR=0"); } // we assume now we are building with IOS8 or later if (UEBuildConfiguration.bCompileAgainstEngine) { InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("HAS_METAL=1"); InBuildTarget.ExtraModuleNames.Add("MetalRHI"); } else { InBuildTarget.GlobalCompileEnvironment.Config.Definitions.Add("HAS_METAL=0"); } InBuildTarget.GlobalLinkEnvironment.Config.AdditionalFrameworks.Add(new UEBuildFramework("GameKit")); InBuildTarget.GlobalLinkEnvironment.Config.AdditionalFrameworks.Add(new UEBuildFramework("StoreKit")); }
public override bool HasSpecificDefaultBuildConfig(UnrealTargetPlatform Platform, DirectoryReference ProjectPath) { ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, ProjectPath, UnrealTargetPlatform.Lumin); bool bUseVulkan = true; Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bUseVulkan", out bUseVulkan); List <string> ConfigBoolKeys = new List <string>(); if (!bUseVulkan) { ConfigBoolKeys.Add("bUseMobileRendering"); } // look up Android specific settings if (!DoProjectSettingsMatchDefault(UnrealTargetPlatform.Lumin, ProjectPath, "/Script/LuminRuntimeSettings.LuminRuntimeSettings", ConfigBoolKeys.ToArray(), null, null)) { return(false); } return(true); }
public static bool UseTegraGraphicsDebugger(FileReference ProjectFile) { if (!HaveTegraGraphicsDebugger.HasValue) { string ProgramsDir = Environment.GetEnvironmentVariable("ProgramFiles(x86)"); string NVDir = ProgramsDir + "/NVIDIA Corporation"; try { string[] TegraDebuggerDirs = Directory.GetDirectories(NVDir, "Tegra Graphics Debugger *"); if (TegraDebuggerDirs.Length > 0) { TegraDebuggerDir = TegraDebuggerDirs[0].Replace('\\', '/'); HaveTegraGraphicsDebugger = true; string[] V = TegraDebuggerDir.Split(' ').Last().Split('.'); TegraDebuggerVersion = new int[2]; TegraDebuggerVersion[0] = Int32.Parse(V[0]); TegraDebuggerVersion[1] = Int32.Parse(V[1]); } else { HaveTegraGraphicsDebugger = false; } } catch (System.IO.IOException) { HaveTegraGraphicsDebugger = false; } } bool bBuild = false; // TODO: do we need this? ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(ProjectFile), UnrealTargetPlatform.Lumin); Ini.GetBool( "/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bBuildWithNvTegraGfxDebugger", out bBuild); return(HaveTegraGraphicsDebugger.Value && bBuild); }
/// <summary> /// Parse crypto settings from INI file /// </summary> public static CryptoSettings ParseCryptoSettings(DirectoryReference InProjectDirectory, UnrealTargetPlatform InTargetPlatform) { CryptoSettings Settings = new CryptoSettings(); ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, InProjectDirectory, InTargetPlatform); Ini.GetBool("PlatformCrypto", "PlatformRequiresDataCrypto", out Settings.bDataCryptoRequired); // For now, we'll just not parse any keys if data crypto is disabled for this platform. In the future, we might want to use // these keys for non-data purposes (other general purpose encryption maybe?) if (!Settings.bDataCryptoRequired) { return(Settings); } { // Start by parsing the legacy encryption.ini settings Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Encryption, InProjectDirectory, InTargetPlatform); Ini.GetBool("Core.Encryption", "SignPak", out Settings.bEnablePakSigning); string[] SigningKeyStrings = new string[3]; Ini.GetString("Core.Encryption", "rsa.privateexp", out SigningKeyStrings[0]); Ini.GetString("Core.Encryption", "rsa.modulus", out SigningKeyStrings[1]); Ini.GetString("Core.Encryption", "rsa.publicexp", out SigningKeyStrings[2]); if (String.IsNullOrEmpty(SigningKeyStrings[0]) || String.IsNullOrEmpty(SigningKeyStrings[1]) || String.IsNullOrEmpty(SigningKeyStrings[2])) { SigningKeyStrings = null; } else { Settings.SigningKey = new SigningKeyPair(); Settings.SigningKey.PrivateKey.Exponent = ParseHexStringToByteArray(ProcessSigningKeyInputStrings(SigningKeyStrings[0]), 64); Settings.SigningKey.PrivateKey.Modulus = ParseHexStringToByteArray(ProcessSigningKeyInputStrings(SigningKeyStrings[1]), 64); Settings.SigningKey.PublicKey.Exponent = ParseHexStringToByteArray(ProcessSigningKeyInputStrings(SigningKeyStrings[2]), 64); Settings.SigningKey.PublicKey.Modulus = Settings.SigningKey.PrivateKey.Modulus; if ((Settings.SigningKey.PrivateKey.Exponent.Length > 64) || (Settings.SigningKey.PrivateKey.Modulus.Length > 64) || (Settings.SigningKey.PublicKey.Exponent.Length > 64) || (Settings.SigningKey.PublicKey.Modulus.Length > 64)) { throw new Exception(string.Format("[{0}] Signing keys parsed from encryption.ini are too long. They must be a maximum of 64 bytes long!", InProjectDirectory)); } } Ini.GetBool("Core.Encryption", "EncryptPak", out Settings.bEnablePakIndexEncryption); Settings.bEnablePakFullAssetEncryption = false; Settings.bEnablePakUAssetEncryption = false; Settings.bEnablePakIniEncryption = Settings.bEnablePakIndexEncryption; string EncryptionKeyString; Ini.GetString("Core.Encryption", "aes.key", out EncryptionKeyString); Settings.EncryptionKey = new EncryptionKey(); if (EncryptionKeyString.Length > 0) { if (EncryptionKeyString.Length < 32) { Log.WriteLine(LogEventType.Warning, "AES key parsed from encryption.ini is too short. It must be 32 bytes, so will be padded with 0s, giving sub-optimal security!"); } else if (EncryptionKeyString.Length > 32) { Log.WriteLine(LogEventType.Warning, "AES key parsed from encryption.ini is too long. It must be 32 bytes, so will be truncated!"); } Settings.EncryptionKey.Key = ParseAnsiStringToByteArray(EncryptionKeyString, 32); } } Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Crypto, InProjectDirectory, InTargetPlatform); string SectionName = "/Script/CryptoKeys.CryptoKeysSettings"; ConfigHierarchySection CryptoSection = Ini.FindSection(SectionName); // If we have new format crypto keys, read them in over the top of the legacy settings if (CryptoSection != null && CryptoSection.KeyNames.Count() > 0) { Ini.GetBool(SectionName, "bEnablePakSigning", out Settings.bEnablePakSigning); Ini.GetBool(SectionName, "bEncryptPakIniFiles", out Settings.bEnablePakIniEncryption); Ini.GetBool(SectionName, "bEncryptPakIndex", out Settings.bEnablePakIndexEncryption); Ini.GetBool(SectionName, "bEncryptUAssetFiles", out Settings.bEnablePakUAssetEncryption); Ini.GetBool(SectionName, "bEncryptAllAssetFiles", out Settings.bEnablePakFullAssetEncryption); // Parse encryption key string EncryptionKeyString; Ini.GetString(SectionName, "EncryptionKey", out EncryptionKeyString); if (!string.IsNullOrEmpty(EncryptionKeyString)) { Settings.EncryptionKey = new EncryptionKey(); Settings.EncryptionKey.Key = System.Convert.FromBase64String(EncryptionKeyString); } // Parse signing key string PrivateExponent, PublicExponent, Modulus; Ini.GetString(SectionName, "SigningPrivateExponent", out PrivateExponent); Ini.GetString(SectionName, "SigningModulus", out Modulus); Ini.GetString(SectionName, "SigningPublicExponent", out PublicExponent); if (!String.IsNullOrEmpty(PrivateExponent) && !String.IsNullOrEmpty(PublicExponent) && !String.IsNullOrEmpty(Modulus)) { Settings.SigningKey = new SigningKeyPair(); Settings.SigningKey.PublicKey.Exponent = System.Convert.FromBase64String(PublicExponent); Settings.SigningKey.PublicKey.Modulus = System.Convert.FromBase64String(Modulus); Settings.SigningKey.PrivateKey.Exponent = System.Convert.FromBase64String(PrivateExponent); Settings.SigningKey.PrivateKey.Modulus = Settings.SigningKey.PublicKey.Modulus; } } return(Settings); }
/// <summary> /// Parse crypto settings from INI file /// </summary> public static CryptoSettings ParseCryptoSettings(DirectoryReference InProjectDirectory, UnrealTargetPlatform InTargetPlatform) { CryptoSettings Settings = new CryptoSettings(); ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, InProjectDirectory, InTargetPlatform); Ini.GetBool("PlatformCrypto", "PlatformRequiresDataCrypto", out Settings.bDataCryptoRequired); Ini.GetBool("PlatformCrypto", "PakSigningRequired", out Settings.PakSigningRequired); Ini.GetBool("PlatformCrypto", "PakEncryptionRequired", out Settings.PakEncryptionRequired); { // Start by parsing the legacy encryption.ini settings Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Encryption, InProjectDirectory, InTargetPlatform); Ini.GetBool("Core.Encryption", "SignPak", out Settings.bEnablePakSigning); string[] SigningKeyStrings = new string[3]; Ini.GetString("Core.Encryption", "rsa.privateexp", out SigningKeyStrings[0]); Ini.GetString("Core.Encryption", "rsa.modulus", out SigningKeyStrings[1]); Ini.GetString("Core.Encryption", "rsa.publicexp", out SigningKeyStrings[2]); if (String.IsNullOrEmpty(SigningKeyStrings[0]) || String.IsNullOrEmpty(SigningKeyStrings[1]) || String.IsNullOrEmpty(SigningKeyStrings[2])) { SigningKeyStrings = null; } else { Settings.SigningKey = new SigningKeyPair(); Settings.SigningKey.PrivateKey.Exponent = ParseHexStringToByteArray(ProcessSigningKeyInputStrings(SigningKeyStrings[0]), 64); Settings.SigningKey.PrivateKey.Modulus = ParseHexStringToByteArray(ProcessSigningKeyInputStrings(SigningKeyStrings[1]), 64); Settings.SigningKey.PublicKey.Exponent = ParseHexStringToByteArray(ProcessSigningKeyInputStrings(SigningKeyStrings[2]), 64); Settings.SigningKey.PublicKey.Modulus = Settings.SigningKey.PrivateKey.Modulus; if ((Settings.SigningKey.PrivateKey.Exponent.Length > 64) || (Settings.SigningKey.PrivateKey.Modulus.Length > 64) || (Settings.SigningKey.PublicKey.Exponent.Length > 64) || (Settings.SigningKey.PublicKey.Modulus.Length > 64)) { throw new Exception(string.Format("[{0}] Signing keys parsed from encryption.ini are too long. They must be a maximum of 64 bytes long!", InProjectDirectory)); } } Ini.GetBool("Core.Encryption", "EncryptPak", out Settings.bEnablePakIndexEncryption); Settings.bEnablePakFullAssetEncryption = false; Settings.bEnablePakUAssetEncryption = false; Settings.bEnablePakIniEncryption = Settings.bEnablePakIndexEncryption; string EncryptionKeyString; Ini.GetString("Core.Encryption", "aes.key", out EncryptionKeyString); Settings.EncryptionKey = new EncryptionKey(); if (EncryptionKeyString.Length > 0) { if (EncryptionKeyString.Length < 32) { Log.WriteLine(LogEventType.Warning, "AES key parsed from encryption.ini is too short. It must be 32 bytes, so will be padded with 0s, giving sub-optimal security!"); } else if (EncryptionKeyString.Length > 32) { Log.WriteLine(LogEventType.Warning, "AES key parsed from encryption.ini is too long. It must be 32 bytes, so will be truncated!"); } Settings.EncryptionKey.Key = ParseAnsiStringToByteArray(EncryptionKeyString, 32); } } Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Crypto, InProjectDirectory, InTargetPlatform); string SectionName = "/Script/CryptoKeys.CryptoKeysSettings"; ConfigHierarchySection CryptoSection = Ini.FindSection(SectionName); // If we have new format crypto keys, read them in over the top of the legacy settings if (CryptoSection != null && CryptoSection.KeyNames.Count() > 0) { Ini.GetBool(SectionName, "bEnablePakSigning", out Settings.bEnablePakSigning); Ini.GetBool(SectionName, "bEncryptPakIniFiles", out Settings.bEnablePakIniEncryption); Ini.GetBool(SectionName, "bEncryptPakIndex", out Settings.bEnablePakIndexEncryption); Ini.GetBool(SectionName, "bEncryptUAssetFiles", out Settings.bEnablePakUAssetEncryption); Ini.GetBool(SectionName, "bEncryptAllAssetFiles", out Settings.bEnablePakFullAssetEncryption); // Parse encryption key string EncryptionKeyString; Ini.GetString(SectionName, "EncryptionKey", out EncryptionKeyString); if (!string.IsNullOrEmpty(EncryptionKeyString)) { Settings.EncryptionKey = new EncryptionKey(); Settings.EncryptionKey.Key = System.Convert.FromBase64String(EncryptionKeyString); Settings.EncryptionKey.Guid = Guid.Empty.ToString(); Settings.EncryptionKey.Name = "Embedded"; } // Parse secondary encryption keys List <EncryptionKey> SecondaryEncryptionKeys = new List <EncryptionKey>(); List <string> SecondaryEncryptionKeyStrings; if (Ini.GetArray(SectionName, "SecondaryEncryptionKeys", out SecondaryEncryptionKeyStrings)) { foreach (string KeySource in SecondaryEncryptionKeyStrings) { EncryptionKey NewKey = new EncryptionKey(); SecondaryEncryptionKeys.Add(NewKey); Regex Search = new Regex("\\(Guid=(?\'Guid\'.*),Name=\\\"(?\'Name\'.*)\\\",Key=\\\"(?\'Key\'.*)\\\"\\)"); Match Match = Search.Match(KeySource); if (Match.Success) { foreach (string GroupName in Search.GetGroupNames()) { string Value = Match.Groups[GroupName].Value; if (GroupName == "Guid") { NewKey.Guid = Value; } else if (GroupName == "Name") { NewKey.Name = Value; } else if (GroupName == "Key") { NewKey.Key = System.Convert.FromBase64String(Value); } } } } } Settings.SecondaryEncryptionKeys = SecondaryEncryptionKeys.ToArray(); // Parse signing key string PrivateExponent, PublicExponent, Modulus; Ini.GetString(SectionName, "SigningPrivateExponent", out PrivateExponent); Ini.GetString(SectionName, "SigningModulus", out Modulus); Ini.GetString(SectionName, "SigningPublicExponent", out PublicExponent); if (!String.IsNullOrEmpty(PrivateExponent) && !String.IsNullOrEmpty(PublicExponent) && !String.IsNullOrEmpty(Modulus)) { Settings.SigningKey = new SigningKeyPair(); Settings.SigningKey.PublicKey.Exponent = System.Convert.FromBase64String(PublicExponent); Settings.SigningKey.PublicKey.Modulus = System.Convert.FromBase64String(Modulus); Settings.SigningKey.PrivateKey.Exponent = System.Convert.FromBase64String(PrivateExponent); Settings.SigningKey.PrivateKey.Modulus = Settings.SigningKey.PublicKey.Modulus; } } // Parse project dynamic keychain keys if (InProjectDirectory != null) { ConfigHierarchy GameIni = ConfigCache.ReadHierarchy(ConfigHierarchyType.Game, InProjectDirectory, InTargetPlatform); if (GameIni != null) { string Filename; if (GameIni.GetString("ContentEncryption", "ProjectKeyChain", out Filename)) { FileReference ProjectKeyChainFile = FileReference.Combine(InProjectDirectory, "Content", Filename); if (FileReference.Exists(ProjectKeyChainFile)) { List <EncryptionKey> EncryptionKeys = new List <EncryptionKey>(); if (Settings.SecondaryEncryptionKeys != null) { EncryptionKeys.AddRange(Settings.SecondaryEncryptionKeys); } string[] Lines = FileReference.ReadAllLines(ProjectKeyChainFile); foreach (string Line in Lines) { string[] KeyParts = Line.Split(':'); if (KeyParts.Length == 4) { EncryptionKey NewKey = new EncryptionKey(); NewKey.Name = KeyParts[0]; NewKey.Guid = KeyParts[2]; NewKey.Key = System.Convert.FromBase64String(KeyParts[3]); EncryptionKey ExistingKey = EncryptionKeys.Find((EncryptionKey OtherKey) => { return(OtherKey.Guid == NewKey.Guid); }); if (ExistingKey != null && !CompareKey(ExistingKey.Key, NewKey.Key)) { throw new Exception("Found multiple encryption keys with the same guid but different AES keys while merging secondary keys from the project key-chain!"); } EncryptionKeys.Add(NewKey); } } Settings.SecondaryEncryptionKeys = EncryptionKeys.ToArray(); } } } } if (!Settings.bDataCryptoRequired) { CryptoSettings NewSettings = new CryptoSettings(); NewSettings.SecondaryEncryptionKeys = Settings.SecondaryEncryptionKeys; Settings = NewSettings; } else { if (!Settings.PakSigningRequired) { Settings.bEnablePakSigning = false; Settings.SigningKey = null; } if (!Settings.PakEncryptionRequired) { Settings.bEnablePakFullAssetEncryption = false; Settings.bEnablePakIndexEncryption = false; Settings.bEnablePakIniEncryption = false; Settings.EncryptionKey = null; Settings.SigningKey = null; } } // Check if we have a valid signing key that is of the old short form if (Settings.SigningKey != null && Settings.SigningKey.IsValid() && Settings.SigningKey.IsUnsecureLegacyKey()) { Log.TraceWarningOnce("Project signing keys found in '{0}' are of the old insecure short format. Please regenerate them using the project crypto settings panel in the editor!", InProjectDirectory); } // Validate the settings we have read if (Settings.bDataCryptoRequired && Settings.bEnablePakSigning && (Settings.SigningKey == null || !Settings.SigningKey.IsValid())) { Log.TraceWarningOnce("Pak signing is enabled, but no valid signing keys were found. Please generate a key in the editor project crypto settings. Signing will be disabled"); Settings.bEnablePakSigning = false; } if (Settings.bDataCryptoRequired && Settings.IsAnyEncryptionEnabled() && (Settings.EncryptionKey == null || !Settings.EncryptionKey.IsValid())) { Log.TraceWarningOnce("Pak encryption is enabled, but no valid encryption key was found. Please generate a key in the editor project crypto settings. Encryption will be disabled"); Settings.bEnablePakUAssetEncryption = false; Settings.bEnablePakFullAssetEncryption = false; Settings.bEnablePakIndexEncryption = false; Settings.bEnablePakIniEncryption = false; } return(Settings); }
public string GenerateManifest(string ProjectName, bool bForDistribution, string Architecture) { ConfigHierarchy GameIni = GetConfigCacheIni(ConfigHierarchyType.Game); string ProjectVersion = string.Empty; GameIni.GetString("/Script/EngineSettings.GeneralProjectSettings", "ProjectVersion", out ProjectVersion); if (string.IsNullOrEmpty(ProjectVersion)) { ProjectVersion = "1.0.0.0"; } ConfigHierarchy EngineIni = GetConfigCacheIni(ConfigHierarchyType.Engine); int VersionCode; EngineIni.GetInt32("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "VersionCode", out VersionCode); bool bInternetRequired; EngineIni.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bInternetRequired", out bInternetRequired); StringBuilder Text = new StringBuilder(); string PackageName = GetPackageName(ProjectName); string ApplicationDisplayName = GetApplicationDisplayName(ProjectName); string TargetExecutableName = "bin/" + ProjectName; Text.AppendLine(string.Format("<manifest xmlns:ml=\"magicleap\" ml:package=\"{0}\" ml:version_name=\"{1}\" ml:version_code=\"{2}\">", PackageName, ProjectVersion, VersionCode)); // @mltodo: query sdk_version Text.AppendLine(string.Format("\t<application ml:visible_name=\"{0}\" ml:is_debuggable=\"{1}\" ml:sdk_version=\"1.0\" ml:minimum_os=\"mlos_1.0\" ml:internet_required=\"{2}\">", ApplicationDisplayName, bForDistribution ? "false" : "true", bInternetRequired ? "true" : "false")); Text.AppendLine(string.Format("\t\t<component ml:name=\".fullscreen\" ml:visible_name=\"{0}\" ml:binary_name=\"{1}\" ml:type=\"{2}\">", ApplicationDisplayName, TargetExecutableName, GetApplicationType())); List <string> AppPrivileges; EngineIni.GetArray("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "AppPrivileges", out AppPrivileges); if (AppPrivileges != null) { foreach (string Privilege in AppPrivileges) { string TrimmedPrivilege = Privilege.Trim(' '); if (TrimmedPrivilege != "") { string PrivilegeString = string.Format("\t\t\t<uses-privilege ml:name=\"{0}\"/>", TrimmedPrivilege); if (!Text.ToString().Contains(PrivilegeString)) { Text.AppendLine(PrivilegeString); } } } } string IconTag = string.Format("<icon ml:name=\"fullscreen\" ml:model_folder=\"{0}\" ml:portal_folder=\"{1}\"/>", GetIconModelStagingPath(), GetIconPortalStagingPath()); Text.AppendLine(string.Format("\t\t\t{0}", IconTag)); List <string> ExtraComponentNodes; EngineIni.GetArray("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "ExtraComponentNodes", out ExtraComponentNodes); if (ExtraComponentNodes != null) { foreach (string ComponentNode in ExtraComponentNodes) { Text.AppendLine(string.Format("\t\t\t{0}", ComponentNode)); } } Text.AppendLine("\t\t</component>"); List <string> ExtraApplicationNodes; EngineIni.GetArray("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "ExtraApplicationNodes", out ExtraApplicationNodes); if (ExtraApplicationNodes != null) { foreach (string ApplicationNode in ExtraApplicationNodes) { Text.AppendLine(string.Format("\t\t{0}", ApplicationNode)); } } Text.AppendLine(string.Format("\t\t{0}", IconTag)); Text.AppendLine("\t</application>"); Text.AppendLine("</manifest>"); // allow plugins to modify final manifest HERE XDocument XDoc; try { XDoc = XDocument.Parse(Text.ToString()); } catch (Exception e) { throw new BuildException("LuminManifest.xml is invalid {0}\n{1}", e, Text.ToString()); } UPL.ProcessPluginNode(Architecture, "luminManifestUpdates", "", ref XDoc); return(XDoc.ToString()); }
/// <summary> /// Parse crypto settings from INI file /// </summary> public static CryptoSettings ParseCryptoSettings(DirectoryReference InProjectDirectory, UnrealTargetPlatform InTargetPlatform) { CryptoSettings Settings = new CryptoSettings(); ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, InProjectDirectory, InTargetPlatform); Ini.GetBool("PlatformCrypto", "PlatformRequiresDataCrypto", out Settings.bDataCryptoRequired); // For now, we'll just not parse any keys if data crypto is disabled for this platform. In the future, we might want to use // these keys for non-data purposes (other general purpose encryption maybe?) if (!Settings.bDataCryptoRequired) { return(Settings); } { // Start by parsing the legacy encryption.ini settings Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Encryption, InProjectDirectory, InTargetPlatform); Ini.GetBool("Core.Encryption", "SignPak", out Settings.bEnablePakSigning); string[] SigningKeyStrings = new string[3]; Ini.GetString("Core.Encryption", "rsa.privateexp", out SigningKeyStrings[0]); Ini.GetString("Core.Encryption", "rsa.modulus", out SigningKeyStrings[1]); Ini.GetString("Core.Encryption", "rsa.publicexp", out SigningKeyStrings[2]); if (String.IsNullOrEmpty(SigningKeyStrings[0]) || String.IsNullOrEmpty(SigningKeyStrings[1]) || String.IsNullOrEmpty(SigningKeyStrings[2])) { SigningKeyStrings = null; } else { Settings.SigningKey = new SigningKeyPair(); Settings.SigningKey.PrivateKey.Exponent = ParseHexStringToByteArray(SigningKeyStrings[0]); Settings.SigningKey.PrivateKey.Modulus = ParseHexStringToByteArray(SigningKeyStrings[1]); Settings.SigningKey.PublicKey.Exponent = ParseHexStringToByteArray(SigningKeyStrings[2]); Settings.SigningKey.PublicKey.Modulus = Settings.SigningKey.PrivateKey.Modulus; } Ini.GetBool("Core.Encryption", "EncryptPak", out Settings.bEnablePakIndexEncryption); Settings.bEnablePakFullAssetEncryption = false; Settings.bEnablePakUAssetEncryption = false; Settings.bEnablePakIniEncryption = Settings.bEnablePakIndexEncryption; string EncryptionKeyString; Ini.GetString("Core.Encryption", "aes.key", out EncryptionKeyString); Settings.EncryptionKey = new EncryptionKey(); Settings.EncryptionKey.Key = ParseAnsiStringToByteArray(EncryptionKeyString); } Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Crypto, InProjectDirectory, InTargetPlatform); string SectionName = "/Script/CryptoKeys.CryptoKeysSettings"; ConfigHierarchySection CryptoSection = Ini.FindSection(SectionName); // If we have new format crypto keys, read them in over the top of the legacy settings if (CryptoSection != null && CryptoSection.KeyNames.Count() > 0) { Ini.GetBool(SectionName, "bEnablePakSigning", out Settings.bEnablePakSigning); Ini.GetBool(SectionName, "bEncryptPakIniFiles", out Settings.bEnablePakIniEncryption); Ini.GetBool(SectionName, "bEncryptPakIndex", out Settings.bEnablePakIndexEncryption); Ini.GetBool(SectionName, "bEncryptUAssetFiles", out Settings.bEnablePakUAssetEncryption); Ini.GetBool(SectionName, "bEncryptAllAssetFiles", out Settings.bEnablePakFullAssetEncryption); // Parse encryption key string EncryptionKeyString; Ini.GetString(SectionName, "EncryptionKey", out EncryptionKeyString); if (!string.IsNullOrEmpty(EncryptionKeyString)) { Settings.EncryptionKey = new EncryptionKey(); Settings.EncryptionKey.Key = System.Convert.FromBase64String(EncryptionKeyString); if (Settings.EncryptionKey.Key.Length != 32) { throw new Exception("The encryption key specified in the crypto config file must be 32 bytes long!"); } } // Parse signing key string PrivateExponent, PublicExponent, Modulus; Ini.GetString(SectionName, "SigningPrivateExponent", out PrivateExponent); Ini.GetString(SectionName, "SigningModulus", out Modulus); Ini.GetString(SectionName, "SigningPublicExponent", out PublicExponent); if (!String.IsNullOrEmpty(PrivateExponent) && !String.IsNullOrEmpty(PublicExponent) && !String.IsNullOrEmpty(Modulus)) { Settings.SigningKey = new SigningKeyPair(); Settings.SigningKey.PublicKey.Exponent = System.Convert.FromBase64String(PublicExponent); Settings.SigningKey.PublicKey.Modulus = System.Convert.FromBase64String(Modulus); Settings.SigningKey.PrivateKey.Exponent = System.Convert.FromBase64String(PrivateExponent); Settings.SigningKey.PrivateKey.Modulus = Settings.SigningKey.PublicKey.Modulus; } } return(Settings); }
public override void SetUpProjectEnvironment(UnrealTargetConfiguration Configuration, TargetInfo Target = null) { if (!bInitializedProject) { base.SetUpProjectEnvironment(Configuration, Target); // update the configuration based on the project file // look in ini settings for what platforms to compile for ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(ProjectFile), Platform); string MinVersion = "IOS_8"; if (Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "MinimumiOSVersion", out MinVersion)) { switch (MinVersion) { case "IOS_61": Log.TraceWarningOnce("IOS 6 is no longer supported in UE4 as 4.11"); RunTimeIOSVersion = "8.0"; break; case "IOS_7": Log.TraceWarningOnce("IOS 7 is no longer supported in UE4 as 4.14"); RunTimeIOSVersion = "8.0"; break; case "IOS_8": RunTimeIOSVersion = "8.0"; break; case "IOS_9": RunTimeIOSVersion = "9.0"; break; case "IOS_10": RunTimeIOSVersion = "10.0"; break; } } bool biPhoneAllowed = true; bool biPadAllowed = true; Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsIPhone", out biPhoneAllowed); Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsIPad", out biPadAllowed); if (biPhoneAllowed && biPadAllowed) { RunTimeIOSDevices = "1,2"; } else if (biPadAllowed) { RunTimeIOSDevices = "2"; } else if (biPhoneAllowed) { RunTimeIOSDevices = "1"; } NonShippingArchitectures = String.Join(",", GetNonShippingArchitectures(Ini)); ShippingArchitectures = String.Join(",", GetShippingArchitectures(Ini)); // determine if we need to generate the dsym Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bGeneratedSYMFile", out BuildConfiguration.bGeneratedSYMFile); Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bGeneratedSYMBundle", out BuildConfiguration.bGeneratedSYMBundle); // determie if bitcode should be generated for the shipping code Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bShipForBitcode", out bShipForBitcode); // @todo tvos: We probably want to handle TVOS versions here Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "AdditionalLinkerFlags", out AdditionalLinkerFlags); Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "AdditionalShippingLinkerFlags", out AdditionalShippingLinkerFlags); Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "MobileProvision", out MobileProvision); Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "SigningCertificate", out SigningCertificate); // bundle identifier Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleIdentifier", out BundleIdentifier); bInitializedProject = true; } ProvisionData Data = new ProvisionData(); string BundleId = BundleIdentifier.Replace("[PROJECT_NAME]", ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UE4Game")).Replace("_", ""); bool bIsTVOS = GetCodesignPlatformName() == "appletvos"; if (!ProvisionCache.ContainsKey(BundleId + " " + bIsTVOS.ToString() + " " + bForDistribtion.ToString())) { Certificate = SigningCertificate; Provision = MobileProvision; if (!string.IsNullOrEmpty(SigningCertificate)) { // verify the certificate Process IPPProcess = new Process(); if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { string IPPCmd = "\"" + UnrealBuildTool.EngineDirectory + "/Binaries/DotNET/IOS/IPhonePackager.exe\" certificates " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + BundleId + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "/Build/BatchFiles/Mac/RunMono.sh"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } else { string IPPCmd = "certificates " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + BundleId + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "\\Binaries\\DotNET\\IOS\\IPhonePackager.exe"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } Utils.RunLocalProcess(IPPProcess); } else { Certificate = bForDistribtion ? "iPhone Distribution" : "iPhone Developer"; bHaveCertificate = true; } if (string.IsNullOrEmpty(MobileProvision) || // no provision specified !File.Exists((BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac ? (Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/") : (Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "/Apple Computer/MobileDevice/Provisioning Profiles/")) + MobileProvision) || // file doesn't exist !bHaveCertificate) // certificate doesn't exist { Certificate = ""; Provision = ""; Log.TraceLog("Provision not specified or not found for " + ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UE4Game") + ", searching for compatible match..."); Process IPPProcess = new Process(); if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { string IPPCmd = "\"" + UnrealBuildTool.EngineDirectory + "/Binaries/DotNET/IOS/IPhonePackager.exe\" signing_match " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + BundleId + (bIsTVOS ? " -tvos" : "") + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "/Build/BatchFiles/Mac/RunMono.sh"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } else { string IPPCmd = "signing_match " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + BundleId + (bIsTVOS ? " -tvos" : "") + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "\\Binaries\\DotNET\\IOS\\IPhonePackager.exe"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } Utils.RunLocalProcess(IPPProcess); Log.TraceLog("Provision found for " + ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UE4Game") + ", Provision: " + Provision + " Certificate: " + Certificate); } // add to the dictionary Data.MobileProvision = Provision; Data.Certificate = Certificate.Replace("\"", ""); // read the provision to get the UUID string filename = (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac ? (Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/") : (Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "/Apple Computer/MobileDevice/Provisioning Profiles/")) + Data.MobileProvision; if (File.Exists(filename)) { string AllText = File.ReadAllText(filename); int idx = AllText.IndexOf("<key>UUID</key>"); if (idx > 0) { idx = AllText.IndexOf("<string>", idx); if (idx > 0) { idx += "<string>".Length; Data.UUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx); } } idx = AllText.IndexOf("<key>com.apple.developer.team-identifier</key>"); if (idx > 0) { idx = AllText.IndexOf("<string>", idx); if (idx > 0) { idx += "<string>".Length; Data.TeamUUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx); } } } else { Log.TraceLog("No matching provision file was discovered. Please ensure you have a compatible provision installed."); } ProvisionCache.Add(BundleId + " " + bIsTVOS.ToString() + " " + bForDistribtion.ToString(), Data); } else { Data = ProvisionCache[BundleId + " " + bIsTVOS.ToString() + " " + bForDistribtion.ToString()]; } MobileProvision = Data.MobileProvision; SigningCertificate = Data.Certificate; MobileProvisionUUID = Data.UUID; TeamUUID = Data.TeamUUID; }
public List <string> CreateManifest(string InManifestName, string InOutputPath, string InIntermediatePath, FileReference InProjectFile, string InProjectDirectory, List <UnrealTargetConfiguration> InTargetConfigs, List <string> InExecutables) { // Verify we can find the SDK. string SDKDirectory = GetSDKDirectory(); if (string.IsNullOrEmpty(SDKDirectory)) { return(null); } // Check parameter values are valid. if (InTargetConfigs.Count != InExecutables.Count) { Log.TraceError("The number of target configurations ({0}) and executables ({1}) passed to manifest generation do not match.", InTargetConfigs.Count, InExecutables.Count); return(null); } if (InTargetConfigs.Count < 1) { Log.TraceError("The number of target configurations is zero, so we cannot generate a manifest."); return(null); } if (!CreateCheckDirectory(InOutputPath)) { Log.TraceError("Failed to create output directory \"{0}\".", InOutputPath); return(null); } if (!CreateCheckDirectory(InIntermediatePath)) { Log.TraceError("Failed to create intermediate directory \"{0}\".", InIntermediatePath); return(null); } OutputPath = InOutputPath; IntermediatePath = InIntermediatePath; ProjectFile = InProjectFile; ProjectPath = InProjectDirectory; UpdatedFilePaths = new List <string>(); // Load up INI settings. We'll use engine settings to retrieve the manifest configuration, but these may reference // values in either game or engine settings, so we'll keep both. GameIni = ConfigCache.ReadHierarchy(ConfigHierarchyType.Game, DirectoryReference.FromFile(InProjectFile), ConfigPlatform); EngineIni = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(InProjectFile), ConfigPlatform); // Load and verify/clean culture list { List <string> CulturesToStageWithDuplicates; GameIni.GetArray("/Script/UnrealEd.ProjectPackagingSettings", "CulturesToStage", out CulturesToStageWithDuplicates); GameIni.GetString("/Script/UnrealEd.ProjectPackagingSettings", "DefaultCulture", out DefaultCulture); if (CulturesToStageWithDuplicates == null || CulturesToStageWithDuplicates.Count < 1) { Log.TraceError("At least one culture must be selected to stage."); return(null); } CulturesToStage = CulturesToStageWithDuplicates.Distinct().ToList(); } if (DefaultCulture == null || DefaultCulture.Length < 1) { DefaultCulture = CulturesToStage[0]; Log.TraceWarning("A default culture must be selected to stage. Using {0}.", DefaultCulture); } if (!CulturesToStage.Contains(DefaultCulture)) { DefaultCulture = CulturesToStage[0]; Log.TraceWarning("The default culture must be one of the staged cultures. Using {0}.", DefaultCulture); } List <string> PerCultureValues; if (EngineIni.GetArray(IniSection_PlatformTargetSettings, "PerCultureResources", out PerCultureValues)) { foreach (string CultureCombinedValues in PerCultureValues) { Dictionary <string, string> SeparatedCultureValues; if (!ConfigHierarchy.TryParse(CultureCombinedValues, out SeparatedCultureValues)) { Log.TraceWarning("Invalid per-culture resource value: {0}", CultureCombinedValues); continue; } string StageId = SeparatedCultureValues["StageId"]; int CultureIndex = CulturesToStage.FindIndex(x => x == StageId); if (CultureIndex >= 0) { CulturesToStage[CultureIndex] = SeparatedCultureValues["CultureId"]; if (DefaultCulture == StageId) { DefaultCulture = SeparatedCultureValues["CultureId"]; } } } } // Only warn if shipping, we can run without translated cultures they're just needed for cert else if (InTargetConfigs.Contains(UnrealTargetConfiguration.Shipping)) { Log.TraceInformation("Staged culture mappings not setup in the editor. See Per Culture Resources in the {0} Target Settings.", Platform.ToString()); } // Clean out the resources intermediate path so that we know there are no stale binary files. string IntermediateResourceDirectory = Path.Combine(IntermediatePath, BuildResourceSubPath); RecursivelyForceDeleteDirectory(IntermediateResourceDirectory); if (!CreateCheckDirectory(IntermediateResourceDirectory)) { Log.TraceError("Could not create directory {0}.", IntermediateResourceDirectory); return(null); } // Construct a single resource writer for the default (no-culture) values string DefaultResourceIntermediatePath = Path.Combine(IntermediateResourceDirectory, "resources.resw"); DefaultResourceWriter = new UEResXWriter(DefaultResourceIntermediatePath); // Construct the ResXWriters for each culture PerCultureResourceWriters = new Dictionary <string, UEResXWriter>(); foreach (string Culture in CulturesToStage) { string IntermediateStringResourcePath = Path.Combine(IntermediateResourceDirectory, Culture); string IntermediateStringResourceFile = Path.Combine(IntermediateStringResourcePath, "resources.resw"); if (!CreateCheckDirectory(IntermediateStringResourcePath)) { Log.TraceWarning("Culture {0} resources not staged.", Culture); CulturesToStage.Remove(Culture); if (Culture == DefaultCulture) { DefaultCulture = CulturesToStage[0]; Log.TraceWarning("Default culture skipped. Using {0} as default culture.", DefaultCulture); } continue; } PerCultureResourceWriters.Add(Culture, new UEResXWriter(IntermediateStringResourceFile)); } // Create the manifest document string IdentityName = null; var ManifestXmlDocument = new XDocument(GetManifest(InTargetConfigs, InExecutables, out IdentityName)); // Export manifest to the intermediate directory then compare the contents to any existing target manifest // and replace if there are differences. string ManifestIntermediatePath = Path.Combine(IntermediatePath, InManifestName); string ManifestTargetPath = Path.Combine(OutputPath, InManifestName); ManifestXmlDocument.Save(ManifestIntermediatePath); CompareAndReplaceModifiedTarget(ManifestIntermediatePath, ManifestTargetPath); ProcessManifest(InTargetConfigs, InExecutables, InManifestName, ManifestTargetPath, ManifestIntermediatePath); // Clean out any resource directories that we aren't staging string TargetResourcePath = Path.Combine(OutputPath, BuildResourceSubPath); if (Directory.Exists(TargetResourcePath)) { List <string> TargetResourceDirectories = new List <string>(Directory.GetDirectories(TargetResourcePath, "*.*", SearchOption.AllDirectories)); foreach (string ResourceDirectory in TargetResourceDirectories) { if (!CulturesToStage.Contains(Path.GetFileName(ResourceDirectory))) { RecursivelyForceDeleteDirectory(ResourceDirectory); } } } // Export the resource tables starting with the default culture string DefaultResourceTargetPath = Path.Combine(OutputPath, BuildResourceSubPath, "resources.resw"); DefaultResourceWriter.Close(); CompareAndReplaceModifiedTarget(DefaultResourceIntermediatePath, DefaultResourceTargetPath); foreach (var Writer in PerCultureResourceWriters) { Writer.Value.Close(); string IntermediateStringResourceFile = Path.Combine(IntermediateResourceDirectory, Writer.Key, "resources.resw"); string TargetStringResourceFile = Path.Combine(OutputPath, BuildResourceSubPath, Writer.Key, "resources.resw"); CompareAndReplaceModifiedTarget(IntermediateStringResourceFile, TargetStringResourceFile); } // Copy all the binary resources into the target directory. CopyResourcesToTargetDir(); // The resource database is dependent on everything else calculated here (manifest, resource string tables, binary resources). // So if any file has been updated we'll need to run the config. if (UpdatedFilePaths.Count > 0) { // Create resource index configuration string PriExecutable = GetMakePriBinaryPath(); string ResourceConfigFile = Path.Combine(IntermediatePath, "priconfig.xml"); bool bEnableAutoResourcePacks = false; EngineIni.GetBool(IniSection_PlatformTargetSettings, "bEnableAutoResourcePacks", out bEnableAutoResourcePacks); // If the game is not going to support language resource packs then merge the culture qualifiers. if (bEnableAutoResourcePacks || CulturesToStage.Count <= 1) { RunExternalProgram(PriExecutable, "createconfig /cf \"" + ResourceConfigFile + "\" /dq " + DefaultCulture + " /o"); } else { RunExternalProgram(PriExecutable, "createconfig /cf \"" + ResourceConfigFile + "\" /dq " + String.Join("_", CulturesToStage) + " /o"); } // Modify configuration to restrict indexing to the Resources directory (saves time and space) XmlDocument PriConfig = new XmlDocument(); PriConfig.Load(ResourceConfigFile); // If the game is not going to support resource packs then remove the autoResourcePackages. if (!bEnableAutoResourcePacks) { XmlNode PackagingNode = PriConfig.SelectSingleNode("/resources/packaging"); PackagingNode.ParentNode.RemoveChild(PackagingNode); } // The previous implementation using startIndexAt="Resources" did not produce the expected ResourceMapSubtree hierarchy, so this manually specifies all resources in a .resfiles instead. string ResourcesResFile = Path.Combine(IntermediatePath, "resources.resfiles"); XmlNode PriIndexNode = PriConfig.SelectSingleNode("/resources/index"); XmlAttribute PriStartIndex = PriIndexNode.Attributes["startIndexAt"]; PriStartIndex.Value = ResourcesResFile; // Swap the default folder indexer-config to a RESFILES indexer-config. XmlElement FolderIndexerConfigNode = PriConfig.SelectSingleNode("/resources/index/indexer-config[@type='folder']") as XmlElement; FolderIndexerConfigNode.SetAttribute("type", "RESFILES"); FolderIndexerConfigNode.RemoveAttribute("foldernameAsQualifier"); FolderIndexerConfigNode.RemoveAttribute("filenameAsQualifier"); PriConfig.Save(ResourceConfigFile); IEnumerable <string> Resources = Directory.EnumerateFiles(Path.Combine(OutputPath, BuildResourceSubPath), "*.*", SearchOption.AllDirectories); System.Text.StringBuilder ResourcesList = new System.Text.StringBuilder(); foreach (string Resource in Resources) { ResourcesList.AppendLine(Resource.Replace(OutputPath, "").TrimStart('\\')); } File.WriteAllText(ResourcesResFile, ResourcesList.ToString()); // Remove previous pri files so we can enumerate which ones are new since the resource generator could produce a file for each staged language. IEnumerable <string> OldPriFiles = Directory.EnumerateFiles(IntermediatePath, "*.pri"); foreach (string OldPri in OldPriFiles) { try { File.Delete(OldPri); } catch (Exception) { Log.TraceError("Could not delete file {0}.", OldPri); } } // Generate the resource index string ResourceLogFile = Path.Combine(IntermediatePath, "ResIndexLog.xml"); string ResourceIndexFile = Path.Combine(IntermediatePath, "resources.pri"); string MakePriCommandLine = "new /pr \"" + OutputPath + "\" /cf \"" + ResourceConfigFile + "\" /mn \"" + ManifestTargetPath + "\" /il \"" + ResourceLogFile + "\" /of \"" + ResourceIndexFile + "\" /o"; if (IdentityName != null) { MakePriCommandLine += " /indexName \"" + IdentityName + "\""; } RunExternalProgram(PriExecutable, MakePriCommandLine); // Remove any existing pri target files that were not generated by this latest update IEnumerable <string> NewPriFiles = Directory.EnumerateFiles(IntermediatePath, "*.pri"); IEnumerable <string> TargetPriFiles = Directory.EnumerateFiles(OutputPath, "*.pri"); foreach (string TargetPri in TargetPriFiles) { if (!NewPriFiles.Contains(TargetPri)) { try { File.Delete(TargetPri); } catch (Exception) { Log.TraceError("Could not remove stale file {0}.", TargetPri); } } } // Stage all the modified pri files to the output directory foreach (string NewPri in NewPriFiles) { string NewResourceIndexFile = Path.Combine(IntermediatePath, Path.GetFileName(NewPri)); string FinalResourceIndexFile = Path.Combine(OutputPath, Path.GetFileName(NewPri)); CompareAndReplaceModifiedTarget(NewResourceIndexFile, FinalResourceIndexFile); } } return(UpdatedFilePaths); }
/// <summary> /// Checks if the editor is currently running and this is a hot-reload /// </summary> public static bool ShouldDoHotReloadFromIDE(BuildConfiguration BuildConfiguration, TargetDescriptor TargetDesc) { // Check if Hot-reload is disabled globally for this project ConfigHierarchy Hierarchy = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(TargetDesc.ProjectFile), TargetDesc.Platform); bool bAllowHotReloadFromIDE; if (Hierarchy.TryGetValue("BuildConfiguration", "bAllowHotReloadFromIDE", out bAllowHotReloadFromIDE) && !bAllowHotReloadFromIDE) { return(false); } if (!BuildConfiguration.bAllowHotReloadFromIDE) { return(false); } // Check if we're using LiveCode instead ConfigHierarchy EditorPerProjectHierarchy = ConfigCache.ReadHierarchy(ConfigHierarchyType.EditorPerProjectUserSettings, DirectoryReference.FromFile(TargetDesc.ProjectFile), TargetDesc.Platform); bool bEnableLiveCode; if (EditorPerProjectHierarchy.GetBool("/Script/LiveCoding.LiveCodingSettings", "bEnabled", out bEnableLiveCode) && bEnableLiveCode) { return(false); } bool bIsRunning = false; // @todo ubtmake: Kind of cheating here to figure out if an editor target. At this point we don't have access to the actual target description, and // this code must be able to execute before we create or load module rules DLLs so that hot reload can work with bUseUBTMakefiles if (TargetDesc.Name.EndsWith("Editor", StringComparison.OrdinalIgnoreCase)) { string EditorBaseFileName = "UE4Editor"; if (TargetDesc.Configuration != UnrealTargetConfiguration.Development) { EditorBaseFileName = String.Format("{0}-{1}-{2}", EditorBaseFileName, TargetDesc.Platform, TargetDesc.Configuration); } FileReference EditorLocation; if (TargetDesc.Platform == UnrealTargetPlatform.Win64) { EditorLocation = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Binaries", "Win64", String.Format("{0}.exe", EditorBaseFileName)); } else if (TargetDesc.Platform == UnrealTargetPlatform.Mac) { EditorLocation = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Binaries", "Mac", String.Format("{0}.app/Contents/MacOS/{0}", EditorBaseFileName)); } else if (TargetDesc.Platform == UnrealTargetPlatform.Linux) { EditorLocation = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Binaries", "Linux", EditorBaseFileName); } else { throw new BuildException("Unknown editor filename for this platform"); } using (Timeline.ScopeEvent("Finding editor processes for hot-reload")) { DirectoryReference EditorRunsDir = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Intermediate", "EditorRuns"); if (!DirectoryReference.Exists(EditorRunsDir)) { return(false); } if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64) { foreach (FileReference EditorInstanceFile in DirectoryReference.EnumerateFiles(EditorRunsDir)) { int ProcessId; if (!Int32.TryParse(EditorInstanceFile.GetFileName(), out ProcessId)) { FileReference.Delete(EditorInstanceFile); continue; } Process RunningProcess; try { RunningProcess = Process.GetProcessById(ProcessId); } catch { RunningProcess = null; } if (RunningProcess == null) { FileReference.Delete(EditorInstanceFile); continue; } FileReference MainModuleFile; try { MainModuleFile = new FileReference(RunningProcess.MainModule.FileName); } catch { MainModuleFile = null; } if (!bIsRunning && EditorLocation == MainModuleFile) { bIsRunning = true; } } } else { FileInfo[] EditorRunsFiles = new DirectoryInfo(EditorRunsDir.FullName).GetFiles(); BuildHostPlatform.ProcessInfo[] Processes = BuildHostPlatform.Current.GetProcesses(); foreach (FileInfo File in EditorRunsFiles) { int PID; BuildHostPlatform.ProcessInfo Proc = null; if (!Int32.TryParse(File.Name, out PID) || (Proc = Processes.FirstOrDefault(P => P.PID == PID)) == default(BuildHostPlatform.ProcessInfo)) { // Delete stale files (it may happen if editor crashes). File.Delete(); continue; } // Don't break here to allow clean-up of other stale files. if (!bIsRunning) { // Otherwise check if the path matches. bIsRunning = new FileReference(Proc.Filename) == EditorLocation; } } } } } return(bIsRunning); }
/// <summary> /// /// </summary> /// <param name="Platform"></param> /// <param name="PlatformGameConfig"></param> /// <param name="AppName"></param> /// <param name="MobileProvisionFile"></param> /// <param name="bForDistribution"></param> /// <param name="IntermediateDir"></param> public static void WriteEntitlements(UnrealTargetPlatform Platform, ConfigHierarchy PlatformGameConfig, string AppName, FileReference MobileProvisionFile, bool bForDistribution, string IntermediateDir) { // get some info from the mobileprovisioning file // the iCloud identifier and the bundle id may differ string iCloudContainerIdentifier = ""; string iCloudContainerIdentifiersXML = "<array><string>iCloud.$(CFBundleIdentifier)</string></array>"; string UbiquityContainerIdentifiersXML = "<array><string>iCloud.$(CFBundleIdentifier)</string></array>"; string iCloudServicesXML = "<array><string>CloudKit</string><string>CloudDocuments</string></array>"; string UbiquityKVStoreIdentifiersXML = "\t<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>"; string OutputFileName = Path.Combine(IntermediateDir, AppName + ".entitlements"); if (MobileProvisionFile != null && File.Exists(MobileProvisionFile.FullName)) { Console.WriteLine("Write entitlements from provisioning file {0}", MobileProvisionFile); MobileProvisionContents MobileProvisionContent = MobileProvisionContents.Read(MobileProvisionFile); iCloudContainerIdentifier = MobileProvisionContent.GetNodeValueByName("com.apple.developer.icloud-container-identifiers"); iCloudContainerIdentifiersXML = MobileProvisionContent.GetNodeXMLValueByName("com.apple.developer.icloud-container-identifiers"); string entitlementXML = MobileProvisionContent.GetNodeXMLValueByName("com.apple.developer.icloud-services"); if (!entitlementXML.Contains("*") || Platform == UnrealTargetPlatform.TVOS) { // for iOS, replace the generic value (*) with the default iCloudServicesXML = entitlementXML; } entitlementXML = MobileProvisionContent.GetNodeXMLValueByName("com.apple.developer.ubiquity-container-identifiers"); if (!entitlementXML.Contains("*") || !bForDistribution) { // for distribution, replace the generic value (*) with the default UbiquityContainerIdentifiersXML = entitlementXML; } entitlementXML = MobileProvisionContent.GetNodeXMLValueByName("com.apple.developer.ubiquity-kvstore-identifier"); if (!entitlementXML.Contains("*") || !bForDistribution) { // for distribution, replace the generic value (*) with the default UbiquityKVStoreIdentifiersXML = entitlementXML; } } else { Console.WriteLine("Couldn't locate the mobile provisioning file {0}", MobileProvisionFile); } // write the entitlements file { bool bCloudKitSupported = false; PlatformGameConfig.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableCloudKitSupport", out bCloudKitSupported); Directory.CreateDirectory(Path.GetDirectoryName(OutputFileName)); // we need to have something so Xcode will compile, so we just set the get-task-allow, since we know the value, // which is based on distribution or not (true means debuggable) StringBuilder Text = new StringBuilder(); Text.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); Text.AppendLine("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); Text.AppendLine("<plist version=\"1.0\">"); Text.AppendLine("<dict>"); Text.AppendLine("\t<key>get-task-allow</key>"); Text.AppendLine(string.Format("\t<{0}/>", bForDistribution ? "false" : "true")); if (bCloudKitSupported) { if (iCloudContainerIdentifiersXML != "") { Text.AppendLine("\t<key>com.apple.developer.icloud-container-identifiers</key>"); Text.AppendLine(iCloudContainerIdentifiersXML); } if (iCloudServicesXML != "") { Text.AppendLine("\t<key>com.apple.developer.icloud-services</key>"); Text.AppendLine(iCloudServicesXML); } if (UbiquityContainerIdentifiersXML != "") { Text.AppendLine("\t<key>com.apple.developer.ubiquity-container-identifiers</key>"); Text.AppendLine(UbiquityContainerIdentifiersXML); } if (UbiquityKVStoreIdentifiersXML != "") { Text.AppendLine("\t<key>com.apple.developer.ubiquity-kvstore-identifier</key>"); Text.AppendLine(UbiquityKVStoreIdentifiersXML); } Text.AppendLine("\t<key>com.apple.developer.icloud-container-environment</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", bForDistribution ? "Production" : "Development")); } bool bRemoteNotificationsSupported = false; PlatformGameConfig.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableRemoteNotificationsSupport", out bRemoteNotificationsSupported); // for TVOS we need push notifications when building for distribution with CloudKit if (bCloudKitSupported && bForDistribution && Platform == UnrealTargetPlatform.TVOS) { bRemoteNotificationsSupported = true; } if (bRemoteNotificationsSupported) { Text.AppendLine("\t<key>aps-environment</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", bForDistribution ? "production" : "development")); } // for Sign in with Apple bool bSignInWithAppleSupported = false; PlatformGameConfig.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableSignInWithAppleSupport", out bSignInWithAppleSupported); if (bSignInWithAppleSupported) { Text.AppendLine("\t<key>com.apple.developer.applesignin</key>"); Text.AppendLine("\t<array><string>Default</string></array>"); } // Add Multi-user support for tvOS bool bRunAsCurrentUser = false; PlatformGameConfig.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bRunAsCurrentUser", out bRunAsCurrentUser); if (bRunAsCurrentUser && Platform == UnrealTargetPlatform.TVOS) { Text.AppendLine("\t<key>com.apple.developer.user-management</key>"); Text.AppendLine("\t<array><string>runs-as-current-user</string></array>"); } // End of entitlements Text.AppendLine("</dict>"); Text.AppendLine("</plist>"); if (File.Exists(OutputFileName)) { // read existing file string ExisitingFileContents = File.ReadAllText(OutputFileName); bool bFileChanged = !ExisitingFileContents.Equals(Text.ToString(), StringComparison.Ordinal); // overwrite file if there are content changes if (bFileChanged) { File.WriteAllText(OutputFileName, Text.ToString()); } } else { File.WriteAllText(OutputFileName, Text.ToString()); } } // create a pList key named ICloudContainerIdentifier // to be used at run-time when intializing the CloudKit services if (iCloudContainerIdentifier != "") { string PListFile = IntermediateDir + "/" + AppName + "-Info.plist"; if (File.Exists(PListFile)) { string OldPListData = File.ReadAllText(PListFile); XDocument XDoc; try { XDoc = XDocument.Parse(OldPListData); if (XDoc.DocumentType != null) { XDoc.DocumentType.InternalSubset = null; } XElement dictElement = XDoc.Root.Element("dict"); if (dictElement != null) { XElement containerIdKeyNew = new XElement("key", "ICloudContainerIdentifier"); XElement containerIdValueNew = new XElement("string", iCloudContainerIdentifier); XElement containerIdKey = dictElement.Elements("key").FirstOrDefault(x => x.Value == "ICloudContainerIdentifier"); if (containerIdKey != null) { // if ICloudContainerIdentifier already exists in the pList file, update its value XElement containerIdValue = containerIdKey.ElementsAfterSelf("string").FirstOrDefault(); if (containerIdValue != null) { containerIdValue.Value = iCloudContainerIdentifier; } else { containerIdKey.AddAfterSelf(containerIdValueNew); } } else { // add ICloudContainerIdentifier to the pList dictElement.Add(containerIdKeyNew); dictElement.Add(containerIdValueNew); } XDoc.Save(PListFile); } } catch (Exception e) { throw new BuildException("plist is invalid {0}\n{1}", e, OldPListData); } } } }