// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.Xml.Linq;
using EpicGames.Core;
using UnrealBuildBase;
using Microsoft.Extensions.Logging;
namespace UnrealBuildTool
{
class UEDeployTVOS : UEDeployIOS
{
public UEDeployTVOS(ILogger InLogger)
: base(InLogger)
{
}
protected override string GetTargetPlatformName()
{
return "TVOS";
}
public static bool GenerateTVOSPList(string ProjectDirectory, bool bIsUnrealGame, string GameName, bool bIsClient, string ProjectName, string InEngineDir, string AppDirectory, UnrealPluginLanguage? UPL, string? BundleID, ILogger Logger)
{
// @todo tvos: THIS!
// generate the Info.plist for future use
string BuildDirectory = ProjectDirectory + "/Build/TVOS";
bool bSkipDefaultPNGs = false;
string IntermediateDirectory = (bIsUnrealGame ? InEngineDir : ProjectDirectory) + "/Intermediate/TVOS";
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
string BundleDisplayName;
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleDisplayName", out BundleDisplayName);
// bundle identifier
string BundleIdentifier;
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleIdentifier", out BundleIdentifier);
if (!string.IsNullOrEmpty(BundleID))
{
BundleIdentifier = BundleID;
}
// bundle name
string BundleName;
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleName", out BundleName);
// short version string
string BundleShortVersion;
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "VersionInfo", out BundleShortVersion);
// required capabilities
string RequiredCaps = "\t\tarm64\n";
// minimum iOS version
string MinVersionSetting = "";
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "MinimumiOSVersion", out MinVersionSetting);
string MinVersion = GetMinimumOSVersion(MinVersionSetting, Logger);
// extra plist data
string ExtraData = "";
Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "AdditionalPlistData", out 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 StringBuilder();
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, out bool bSupportsPortrait, out bool bSupportsLandscape)
{
bSupportsLandscape = bSupportsPortrait = true;
return GenerateTVOSPList(ProjectDirectory, bIsUnrealGame, GameName, bIsClient, ProjectName, InEngineDir, AppDirectory, null, BundleID, Logger);
}
}
}