// Copyright Epic Games, Inc. All Rights Reserved.
using EpicGames.Core;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Linq;
namespace UnrealBuildTool
{
class UEDeployVisionOS : UEDeployIOS
{
public UEDeployVisionOS(ILogger InLogger)
: base(InLogger)
{
}
protected override string GetTargetPlatformName()
{
return "VisionOS";
}
public static bool GenerateVisionOSPList(string ProjectDirectory, bool bIsUnrealGame, string GameName, bool bIsClient, string ProjectName, string InEngineDir, string AppDirectory, UnrealPluginLanguage? UPL, string? BundleID, ILogger Logger)
{
// @todo visionos: THIS!
// generate the Info.plist for future use
string BuildDirectory = ProjectDirectory + "/Build/VisionOS";
bool bSkipDefaultPNGs = false;
string IntermediateDirectory = (bIsUnrealGame ? InEngineDir : ProjectDirectory) + "/Intermediate/VisionOS";
string PListFile = IntermediateDirectory + "/" + GameName + "-Info.plist";
// @todo tvos: This is really nasty - both IOS and TVOS are setting static vars
VersionUtilities.BuildDirectory = BuildDirectory;
VersionUtilities.GameName = GameName;
// read the old file
string OldPListData = File.Exists(PListFile) ? File.ReadAllText(PListFile) : "";
// get the settings from the ini file
// plist replacements
// @todo tvos: Are we going to make TVOS specific .ini files?
DirectoryReference? DirRef = bIsUnrealGame ? (!string.IsNullOrEmpty(UnrealBuildTool.GetRemoteIniPath()) ? new DirectoryReference(UnrealBuildTool.GetRemoteIniPath()!) : null) : new DirectoryReference(ProjectDirectory);
ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirRef, UnrealTargetPlatform.IOS);
// bundle display name
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleDisplayName", out string BundleDisplayName);
// bundle identifier
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleIdentifier", out string BundleIdentifier);
if (!string.IsNullOrEmpty(BundleID))
{
BundleIdentifier = BundleID;
}
// bundle name
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleName", out string BundleName);
// short version string
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "VersionInfo", out string BundleShortVersion);
// required capabilities
string RequiredCaps = "\t\tarm64\n";
// minimum iOS version
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "MinimumiOSVersion", out string MinVersionSetting);
string MinVersion = GetMinimumOSVersion(MinVersionSetting, Logger);
// extra plist data
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "AdditionalPlistData", out string ExtraData);
// create the final display name, including converting all entities for XML use
string FinalDisplayName = BundleDisplayName.Replace("[PROJECT_NAME]", ProjectName).Replace("_", "");
FinalDisplayName = FinalDisplayName.Replace("&", "&");
FinalDisplayName = FinalDisplayName.Replace("\"", """);
FinalDisplayName = FinalDisplayName.Replace("\'", "'");
FinalDisplayName = FinalDisplayName.Replace("<", "<");
FinalDisplayName = FinalDisplayName.Replace(">", ">");
// generate the plist file
StringBuilder Text = new();
Text.AppendLine("");
Text.AppendLine("");
Text.AppendLine("");
Text.AppendLine("");
Text.AppendLine("\tCFBundleDevelopmentRegion");
Text.AppendLine("\ten");
Text.AppendLine("\tCFBundleDisplayName");
Text.AppendLine(string.Format("\t{0}", EncodeBundleName(BundleDisplayName, ProjectName)));
Text.AppendLine("\tCFBundleExecutable");
string BundleExecutable = bIsUnrealGame ?
(bIsClient ? "UnrealClient" : "UnrealGame") :
(bIsClient ? GameName + "Client" : GameName);
Text.AppendLine(string.Format("\t{0}", BundleExecutable));
Text.AppendLine("\tCFBundleIdentifier");
Text.AppendLine(string.Format("\t{0}", BundleIdentifier.Replace("[PROJECT_NAME]", ProjectName).Replace("_", "")));
Text.AppendLine("\tCFBundleInfoDictionaryVersion");
Text.AppendLine("\t6.0");
Text.AppendLine("\tCFBundleName");
Text.AppendLine(string.Format("\t{0}", EncodeBundleName(BundleName, ProjectName)));
Text.AppendLine("\tCFBundlePackageType");
Text.AppendLine("\tAPPL");
Text.AppendLine("\tCFBundleSignature");
Text.AppendLine("\t????");
Text.AppendLine("\tCFBundleVersion");
Text.AppendLine(string.Format("\t{0}", VersionUtilities.UpdateBundleVersion(OldPListData, InEngineDir)));
Text.AppendLine("\tCFBundleShortVersionString");
Text.AppendLine(string.Format("\t{0}", BundleShortVersion));
Text.AppendLine("\tLSRequiresIPhoneOS");
Text.AppendLine("\t");
Text.AppendLine("\tUIRequiredDeviceCapabilities");
Text.AppendLine("\t");
foreach (string Line in RequiredCaps.Split("\r\n".ToCharArray()))
{
if (!string.IsNullOrWhiteSpace(Line))
{
Text.AppendLine(Line);
}
}
Text.AppendLine("\t");
Text.AppendLine("\tTVTopShelfImage");
Text.AppendLine("\t");
Text.AppendLine("\t\tTVTopShelfPrimaryImageWide");
Text.AppendLine("\t\tTop Shelf Image Wide");
Text.AppendLine("\t");
Text.AppendLine("\tCFBundleIcons");
Text.AppendLine("\t");
Text.AppendLine("\t\tCFBundlePrimaryIcon");
Text.AppendLine("\t\tApp Icon");
Text.AppendLine("\t");
Text.AppendLine("\tUILaunchStoryboardName");
Text.AppendLine("\tLaunchScreen");
// write the iCloud container identifier, if present in the old file
if (!string.IsNullOrEmpty(OldPListData))
{
int index = OldPListData.IndexOf("ICloudContainerIdentifier");
if (index > 0)
{
index = OldPListData.IndexOf("", index) + 8;
int length = OldPListData.IndexOf("", index) - index;
string ICloudContainerIdentifier = OldPListData.Substring(index, length);
Text.AppendLine("\tICloudContainerIdentifier");
Text.AppendLine(string.Format("\t{0}", ICloudContainerIdentifier));
}
}
Text.AppendLine("");
Text.AppendLine("");
// Create the intermediate directory if needed
if (!Directory.Exists(IntermediateDirectory))
{
Directory.CreateDirectory(IntermediateDirectory);
}
if (UPL != null)
{
// Allow UPL to modify the plist here
XDocument XDoc;
try
{
XDoc = XDocument.Parse(Text.ToString());
}
catch (Exception e)
{
throw new BuildException("plist is invalid {0}\n{1}", e, Text.ToString());
}
XDoc.DocumentType!.InternalSubset = "";
UPL.ProcessPluginNode("None", "iosPListUpdates", "", ref XDoc);
string result = XDoc.Declaration?.ToString() + "\n" + XDoc.ToString().Replace("", "");
File.WriteAllText(PListFile, result);
}
else
{
File.WriteAllText(PListFile, Text.ToString());
}
if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
{
if (!Directory.Exists(AppDirectory))
{
Directory.CreateDirectory(AppDirectory);
}
File.WriteAllText(AppDirectory + "/Info.plist", Text.ToString());
}
return bSkipDefaultPNGs;
}
public override bool GeneratePList(FileReference? ProjectFile, UnrealTargetConfiguration Config, string ProjectDirectory, bool bIsUnrealGame, string GameName, bool bIsClient, string ProjectName, string InEngineDir, string AppDirectory, List UPLScripts, string? BundleID, bool bBuildAsFramework)
{
return GenerateVisionOSPList(ProjectDirectory, bIsUnrealGame, GameName, bIsClient, ProjectName, InEngineDir, AppDirectory, null, BundleID, Logger);
}
}
}