1077 lines
43 KiB
C#
1077 lines
43 KiB
C#
/**
|
|
* Copyright Epic Games, Inc. All Rights Reserved.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using Ionic.Zlib;
|
|
using System.ComponentModel;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using Microsoft.Win32;
|
|
|
|
namespace iPhonePackager
|
|
{
|
|
// NOTE: this needs to be kept in sync with EditorAnalytics.h and AutomationTool/Program.cs
|
|
public enum ErrorCodes
|
|
{
|
|
Error_UATNotFound = -1,
|
|
Error_Success = 0,
|
|
Error_Unknown = 1,
|
|
Error_Arguments = 2,
|
|
Error_UnknownCommand = 3,
|
|
Error_SDKNotFound = 10,
|
|
Error_ProvisionNotFound = 11,
|
|
Error_CertificateNotFound = 12,
|
|
Error_ProvisionAndCertificateNotFound = 13,
|
|
Error_InfoPListNotFound = 14,
|
|
Error_KeyNotFoundInPList = 15,
|
|
Error_ProvisionExpired = 16,
|
|
Error_CertificateExpired = 17,
|
|
Error_CertificateProvisionMismatch = 18,
|
|
Error_CodeUnsupported = 19,
|
|
Error_PluginsUnsupported = 20,
|
|
Error_UnknownCookFailure = 25,
|
|
Error_UnknownDeployFailure = 26,
|
|
Error_UnknownBuildFailure = 27,
|
|
Error_UnknownPackageFailure = 28,
|
|
Error_UnknownLaunchFailure = 29,
|
|
Error_StageMissingFile = 30,
|
|
Error_FailedToCreateIPA = 31,
|
|
Error_FailedToCodeSign = 32,
|
|
Error_DeviceBackupFailed = 33,
|
|
Error_AppUninstallFailed = 34,
|
|
Error_AppInstallFailed = 35,
|
|
Error_AppNotFound = 36,
|
|
Error_StubNotSignedCorrectly = 37,
|
|
Error_IPAMissingInfoPList = 38,
|
|
Error_DeleteFile = 39,
|
|
Error_DeleteDirectory = 40,
|
|
Error_CreateDirectory = 41,
|
|
Error_CopyFile = 42,
|
|
Error_OnlyOneObbFileSupported = 50,
|
|
Error_FailureGettingPackageInfo = 51,
|
|
Error_OnlyOneTargetConfigurationSupported = 52,
|
|
Error_ObbNotFound = 53,
|
|
Error_AndroidBuildToolsPathNotFound = 54,
|
|
Error_NoApkSuitableForArchitecture = 55,
|
|
Error_FilesInstallFailed = 56,
|
|
Error_RemoteCertificatesNotFound = 57,
|
|
Error_LauncherFailed = 100,
|
|
Error_UATLaunchFailure = 101,
|
|
Error_FailedToDeleteStagingDirectory = 102,
|
|
Error_MissingExecutable = 103,
|
|
Error_DeviceNotSetupForDevelopment = 150,
|
|
Error_DeviceOSNewerThanSDK = 151,
|
|
Error_TestFailure = 152,
|
|
Error_SymbolizedSONotFound = 153,
|
|
Error_LicenseNotAccepted = 154,
|
|
Error_AndroidOBBError = 155,
|
|
Error_SDKInstallFailed = 200,
|
|
Error_DeviceUpdateFailed = 201,
|
|
};
|
|
|
|
public partial class Program
|
|
{
|
|
static public int ReturnCode = 0;
|
|
static string MainCommand = "";
|
|
static string MainRPCCommand = "";
|
|
static public string GameName = "";
|
|
static private string GamePath = "";
|
|
static public string GameConfiguration = "";
|
|
static public string Architecture = "";
|
|
static public string SchemeName = "";
|
|
static public string SchemeConfiguration = "";
|
|
|
|
static public SlowProgressDialog ProgressDialog = null;
|
|
static public BackgroundWorker BGWorker = null;
|
|
static public int ProgressIndex = 0;
|
|
static public string AdditionalCommandline = "";
|
|
static public bool IsClient = false;
|
|
|
|
static public void UpdateStatus(string Line)
|
|
{
|
|
if (BGWorker != null)
|
|
{
|
|
int Percent = Math.Min(Math.Max(ProgressIndex, 0), 100);
|
|
BGWorker.ReportProgress(Percent, Line);
|
|
}
|
|
}
|
|
|
|
static public void Log(string Line)
|
|
{
|
|
UpdateStatus(Line);
|
|
Console.WriteLine(Line);
|
|
}
|
|
|
|
static public void Log(string Line, params object[] Args)
|
|
{
|
|
Log(String.Format(Line, Args));
|
|
}
|
|
|
|
static public void LogVerbose(string Line)
|
|
{
|
|
if (Config.bVerbose)
|
|
{
|
|
UpdateStatus(Line);
|
|
Console.WriteLine(Line);
|
|
}
|
|
}
|
|
|
|
static public void LogVerbose(string Line, params object[] Args)
|
|
{
|
|
LogVerbose(String.Format(Line, Args));
|
|
}
|
|
|
|
static public void Error(string Line, int Code = 1)
|
|
{
|
|
if (Program.ReturnCode == 0)
|
|
{
|
|
Program.ReturnCode = Code;
|
|
}
|
|
|
|
Console.ForegroundColor = ConsoleColor.Red;
|
|
Log("IPP ERROR: " + Line);
|
|
|
|
Console.ResetColor();
|
|
}
|
|
|
|
static public void Error(string Line, params object[] Args)
|
|
{
|
|
Error(String.Format(Line, Args));
|
|
}
|
|
|
|
static public void Warning(string Line)
|
|
{
|
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
|
Log("IPP WARNING: " + Line);
|
|
Console.ResetColor();
|
|
}
|
|
|
|
static public void Warning(string Line, params object[] Args)
|
|
{
|
|
Warning(String.Format(Line, Args));
|
|
}
|
|
|
|
static private bool ParseCommandLine(ref string[] Arguments)
|
|
{
|
|
if (Arguments.Length == 0)
|
|
{
|
|
StartVisuals();
|
|
|
|
// we NEED a project, so show a uproject picker
|
|
string UProjectFile;
|
|
string StartingDir = "";
|
|
if (ToolsHub.ShowOpenFileDialog("Unreal Project Files (*.uproject)|*.uproject;", "IPhonePackager now requires a .uproject file for certificate/provisioning setup", "mobileprovision", "", ref StartingDir, out UProjectFile))
|
|
{
|
|
Arguments = new string[] { UProjectFile };
|
|
}
|
|
else
|
|
{
|
|
Arguments = new string[] { "gui" };
|
|
}
|
|
}
|
|
|
|
if (Arguments.Length == 1)
|
|
{
|
|
// if the only argument is a uproject, then assume gui mode, with the uproject as the project
|
|
if (Arguments[0].EndsWith(".uproject"))
|
|
{
|
|
Config.ProjectFile = GamePath = Arguments[0];
|
|
|
|
MainCommand = "gui";
|
|
}
|
|
else
|
|
{
|
|
MainCommand = Arguments[0];
|
|
}
|
|
}
|
|
else if (Arguments.Length == 2)
|
|
{
|
|
MainCommand = Arguments[0];
|
|
GamePath = Arguments[1];
|
|
if (GamePath.EndsWith(".uproject"))
|
|
{
|
|
Config.ProjectFile = GamePath;
|
|
}
|
|
}
|
|
else if (Arguments.Length >= 2)
|
|
{
|
|
MainCommand = Arguments[0];
|
|
GamePath = Arguments[1];
|
|
if (GamePath.EndsWith(".uproject"))
|
|
{
|
|
Config.ProjectFile = GamePath;
|
|
}
|
|
|
|
for (int ArgIndex = 2; ArgIndex < Arguments.Length; ArgIndex++)
|
|
{
|
|
string Arg = Arguments[ArgIndex].ToLowerInvariant();
|
|
|
|
if (Arg.StartsWith("-"))
|
|
{
|
|
// Behavior switches
|
|
switch (Arg)
|
|
{
|
|
case "-verbose":
|
|
Config.bVerbose = true;
|
|
break;
|
|
case "-strip":
|
|
Config.bForceStripSymbols = true;
|
|
break;
|
|
case "-compress=best":
|
|
Config.RecompressionSetting = (int)CompressionLevel.BestCompression;
|
|
break;
|
|
case "-compress=fast":
|
|
Config.RecompressionSetting = (int)CompressionLevel.BestSpeed;
|
|
break;
|
|
case "-compress=none":
|
|
Config.RecompressionSetting = (int)CompressionLevel.None;
|
|
break;
|
|
case "-distribution":
|
|
Config.bForDistribution = true;
|
|
break;
|
|
case "-codebased":
|
|
Config.bIsCodeBasedProject = true;
|
|
break;
|
|
case "-createstub":
|
|
Config.bCreateStubSet = true;
|
|
break;
|
|
case "-sign":
|
|
Config.bPerformResignWhenRepackaging = true;
|
|
break;
|
|
case "-cookonthefly":
|
|
Config.bCookOnTheFly = true;
|
|
break;
|
|
case "-iterate":
|
|
Config.bIterate = true;
|
|
break;
|
|
case "-tvos":
|
|
Config.OSString = "TVOS";
|
|
break;
|
|
case "-autosigning":
|
|
Config.bAutomaticSigning = true;
|
|
break;
|
|
}
|
|
|
|
// get the stage dir path
|
|
if (Arg == "-stagedir")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.RepackageStagingDirectory = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
// get the provisioning uuid
|
|
else if (Arg == "-provisioninguuid")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.ProvisionUUID = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-provisionfile")
|
|
{
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.ProvisionFile = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-teamID")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.TeamID = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-manifest")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.DeltaManifest = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-backup")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.FilesForBackup.Add(Arguments[++ArgIndex]);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-config")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
GameConfiguration = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
// append a name to the bungle identifier and display name
|
|
else if (Arg == "-bundlename")
|
|
{
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
string projectName = "[PROJECT_NAME]";
|
|
string bundleId = Arguments[++ArgIndex];
|
|
|
|
// Check for an illegal bundle id
|
|
for (int i = 0; i < bundleId.Length; ++i)
|
|
{
|
|
char c = bundleId[i];
|
|
|
|
if (c == '[')
|
|
{
|
|
if (bundleId.IndexOf(projectName, i) != i)
|
|
{
|
|
Error("Illegal character in bundle ID");
|
|
return false;
|
|
}
|
|
i += projectName.Length;
|
|
}
|
|
else if ((c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && c != '.' && c != '-')
|
|
{
|
|
Error("Illegal character in bundle ID");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Save the verified bundle id
|
|
Config.OverrideBundleName = bundleId;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-schemename")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
SchemeName = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-schemeconfig")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
SchemeConfiguration = Arguments[++ArgIndex];
|
|
|
|
if (SchemeConfiguration.EndsWith("Client"))
|
|
{
|
|
IsClient = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-mac")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.OverrideMacName = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-architecture" || Arg == "-arch")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Architecture = "-" + Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-project")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.ProjectFile = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-device")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.DeviceId = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-additionalcommandline")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
AdditionalCommandline = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-provision")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.Provision = Arguments[++ArgIndex];
|
|
Config.bProvision = !String.IsNullOrEmpty(Config.Provision);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-certificate")
|
|
{
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.Certificate = Arguments[++ArgIndex];
|
|
Config.bCert = !String.IsNullOrEmpty(Config.Certificate);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-outputcertificate")
|
|
{
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.OutputCertificate = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (Arg == "-targetname")
|
|
{
|
|
// make sure there's at least one more arg
|
|
if (Arguments.Length > ArgIndex + 1)
|
|
{
|
|
Config.TargetName = Arguments[++ArgIndex];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// RPC command
|
|
MainRPCCommand = Arguments[ArgIndex];
|
|
}
|
|
}
|
|
|
|
if (!SSHCommandHelper.ParseSSHProperties(Arguments))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
static private bool CheckArguments()
|
|
{
|
|
if (GameConfiguration.Length == 0)
|
|
{
|
|
GameConfiguration = "GameConfigurationWasNotSpecifiedToIPP";
|
|
}
|
|
|
|
if (GameName.Length == 0)
|
|
{
|
|
Error("Invalid number of arguments");
|
|
Program.ReturnCode = (int)ErrorCodes.Error_Arguments;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static public void ExecuteCommand(string Command, string RPCCommand)
|
|
{
|
|
if (ReturnCode > 0)
|
|
{
|
|
Warning("Error in previous command; suppressing: " + Command + " " + RPCCommand ?? "");
|
|
return;
|
|
}
|
|
|
|
if (Config.bVerbose)
|
|
{
|
|
Log("");
|
|
Log("----------");
|
|
Log(String.Format("Executing command '{0}' {1}...", Command, (RPCCommand != null) ? ("'" + RPCCommand + "' ") : ""));
|
|
}
|
|
|
|
try
|
|
{
|
|
bool bHandledCommand = CookTime.ExecuteCookCommand(Command, RPCCommand);
|
|
|
|
if (!bHandledCommand)
|
|
{
|
|
bHandledCommand = CompileTime.ExecuteCompileCommand(Command, RPCCommand);
|
|
}
|
|
|
|
if (!bHandledCommand)
|
|
{
|
|
bHandledCommand = DeploymentHelper.ExecuteDeployCommand(Command, GamePath, RPCCommand);
|
|
}
|
|
|
|
if (!bHandledCommand)
|
|
{
|
|
bHandledCommand = true;
|
|
switch (Command)
|
|
{
|
|
case "configure":
|
|
if (Config.bForDistribution)
|
|
{
|
|
Error("configure cannot be used with -distribution");
|
|
Program.ReturnCode = (int)ErrorCodes.Error_Arguments;
|
|
}
|
|
else
|
|
{
|
|
RunInVisualMode(delegate { return new ConfigureMobileGame(); });
|
|
}
|
|
break;
|
|
|
|
default:
|
|
bHandledCommand = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bHandledCommand)
|
|
{
|
|
Error("Unknown command");
|
|
Program.ReturnCode = (int)ErrorCodes.Error_UnknownCommand;
|
|
}
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
Error("Error while executing command: " + Ex.ToString());
|
|
if (Program.ReturnCode == 0)
|
|
{
|
|
Program.ReturnCode = (int)ErrorCodes.Error_Unknown;
|
|
}
|
|
}
|
|
|
|
Console.WriteLine();
|
|
}
|
|
|
|
/**
|
|
* Assembly resolve method to pick correct StandaloneSymbolParser DLL
|
|
*/
|
|
static Assembly CurrentDomain_AssemblyResolve(Object sender, ResolveEventArgs args)
|
|
{
|
|
// Name is fully qualified assembly definition - e.g. "p4dn, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ff968dc1933aba6f"
|
|
string[] AssemblyInfo = args.Name.Split(",".ToCharArray());
|
|
string AssemblyName = AssemblyInfo[0];
|
|
|
|
if (AssemblyName.ToLowerInvariant() == "unrealcontrols")
|
|
{
|
|
AssemblyName = Path.GetFullPath(@"..\" + AssemblyName + ".dll");
|
|
|
|
Debug.WriteLineIf(System.Diagnostics.Debugger.IsAttached, "Loading assembly: " + AssemblyName);
|
|
|
|
if (File.Exists(AssemblyName))
|
|
{
|
|
Assembly SymParser = Assembly.LoadFile(AssemblyName);
|
|
return SymParser;
|
|
}
|
|
}
|
|
else if (AssemblyName.ToLowerInvariant() == "rpcutility")
|
|
{
|
|
AssemblyName = Path.GetFullPath(@"..\" + AssemblyName + ".exe");
|
|
|
|
Debug.WriteLineIf(System.Diagnostics.Debugger.IsAttached, "Loading assembly: " + AssemblyName);
|
|
|
|
if (File.Exists(AssemblyName))
|
|
{
|
|
Assembly SymParser = Assembly.LoadFile(AssemblyName);
|
|
return SymParser;
|
|
}
|
|
}
|
|
else if (AssemblyName.ToLowerInvariant() == "bouncycastle.crypto")
|
|
{
|
|
AssemblyName = Path.GetFullPath(@"..\..\ThirdParty\IOS\" + AssemblyName + ".dll");
|
|
|
|
Debug.WriteLineIf(System.Diagnostics.Debugger.IsAttached, "Loading assembly: " + AssemblyName);
|
|
|
|
if (File.Exists(AssemblyName))
|
|
{
|
|
Assembly A = Assembly.LoadFile(AssemblyName);
|
|
return A;
|
|
}
|
|
}
|
|
else if (AssemblyName.ToLowerInvariant() == "sysadminslv.asn1parser")
|
|
{
|
|
AssemblyName = Path.GetFullPath(@"..\..\ThirdParty\IOS\" + AssemblyName + ".dll");
|
|
|
|
Debug.WriteLineIf(System.Diagnostics.Debugger.IsAttached, "Loading assembly: " + AssemblyName);
|
|
|
|
if (File.Exists(AssemblyName))
|
|
{
|
|
Assembly A = Assembly.LoadFile(AssemblyName);
|
|
return A;
|
|
}
|
|
}
|
|
else if (AssemblyName.ToLowerInvariant() == "ionic.zip.reduced")
|
|
{
|
|
AssemblyName = Path.GetFullPath(@"..\" + AssemblyName + ".dll");
|
|
|
|
Debug.WriteLineIf(System.Diagnostics.Debugger.IsAttached, "Loading assembly: " + AssemblyName);
|
|
|
|
if (File.Exists(AssemblyName))
|
|
{
|
|
Assembly A = Assembly.LoadFile(AssemblyName);
|
|
return A;
|
|
}
|
|
}
|
|
|
|
return (null);
|
|
}
|
|
|
|
delegate Form CreateFormDelegate();
|
|
|
|
static bool bVisualsStarted = false;
|
|
static void StartVisuals()
|
|
{
|
|
if (bVisualsStarted)
|
|
{
|
|
return;
|
|
}
|
|
bVisualsStarted = true;
|
|
|
|
// make the form look good on modern displays!
|
|
if (Environment.OSVersion.Version.Major >= 6)
|
|
{
|
|
SetProcessDPIAware();
|
|
}
|
|
|
|
Application.EnableVisualStyles();
|
|
Application.SetCompatibleTextRenderingDefault(false);
|
|
|
|
ProgressDialog = new SlowProgressDialog();
|
|
}
|
|
[System.Runtime.InteropServices.DllImport("user32.dll")]
|
|
private static extern bool SetProcessDPIAware();
|
|
|
|
static void RunInVisualMode(CreateFormDelegate Work)
|
|
{
|
|
StartVisuals();
|
|
|
|
Form DisplayForm = Work.Invoke();
|
|
Application.Run(DisplayForm);
|
|
}
|
|
|
|
static void ListDevices()
|
|
{
|
|
var DeviceList = DeploymentHelper.GetAllConnectedDevices();
|
|
|
|
Console.WriteLine("-------------------------------------------------------");
|
|
Console.WriteLine("List of devices attached");
|
|
|
|
foreach (var DeviceInfo in DeviceList)
|
|
{
|
|
string UDID = DeviceInfo.UDID;
|
|
string DeviceName = DeviceInfo.DeviceName;
|
|
|
|
Console.WriteLine("{0} device:{1}", UDID, DeviceName);
|
|
}
|
|
}
|
|
|
|
static string FindWindowsStoreITunesDLL()
|
|
{
|
|
string InstallPath = null;
|
|
|
|
string PackagesKeyName = "Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppModel\\PackageRepository\\Packages";
|
|
|
|
RegistryKey PackagesKey = Registry.LocalMachine.OpenSubKey(PackagesKeyName);
|
|
if (PackagesKey != null)
|
|
{
|
|
string[] PackageSubKeyNames = PackagesKey.GetSubKeyNames();
|
|
|
|
foreach (string PackageSubKeyName in PackageSubKeyNames)
|
|
{
|
|
if (PackageSubKeyName.Contains("AppleInc.iTunes") && (PackageSubKeyName.Contains("_x64") || PackageSubKeyName.Contains("_x86")))
|
|
{
|
|
string FullPackageSubKeyName = PackagesKeyName + "\\" + PackageSubKeyName;
|
|
|
|
RegistryKey iTunesKey = Registry.LocalMachine.OpenSubKey(FullPackageSubKeyName);
|
|
if (iTunesKey != null)
|
|
{
|
|
InstallPath = (string)iTunesKey.GetValue("Path") + "\\AMDS32\\MobileDevice.dll";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return InstallPath;
|
|
}
|
|
|
|
/**
|
|
* Main control loop
|
|
*/
|
|
[STAThread]
|
|
static int Main(string[] args)
|
|
{
|
|
// remember the working directory at start, as the game path could be relative to this path
|
|
string InitialCurrentDirectory = Environment.CurrentDirectory;
|
|
|
|
// set the working directory to the location of the application (so relative paths always work)
|
|
Environment.CurrentDirectory = Path.GetDirectoryName(Application.ExecutablePath);
|
|
|
|
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
|
|
|
|
// Force UTF-8 encoding so UBT and the engine can parse the IPP output consistently.
|
|
Console.OutputEncoding = Encoding.UTF8;
|
|
|
|
// A simple, top-level try-catch block
|
|
try
|
|
{
|
|
if (!ParseCommandLine(ref args))
|
|
{
|
|
Log("Usage: iPhonePackager <Command> <GameName> [RPCCommand &| Switch]");
|
|
Log("");
|
|
Log("Common commands:");
|
|
Log(" ... RepackageIPA GameName");
|
|
Log(" ... PackageIPA GameName");
|
|
Log(" ... PackageApp GameName");
|
|
Log(" ... Deploy PathToIPA");
|
|
Log(" ... RepackageFromStage GameName");
|
|
Log(" ... Devices");
|
|
Log(" ... Validate");
|
|
Log(" ... Install");
|
|
Log("");
|
|
Log("Configuration switches:");
|
|
Log(" -stagedir <path> sets the directory to copy staged files from (defaults to none)");
|
|
Log(" -project <path> path to the project being packaged");
|
|
Log(" -provisioning <uuid> uuid of the provisioning selected");
|
|
Log(" -compress=fast|best|none packaging compression level (defaults to none)");
|
|
Log(" -strip strip symbols during packaging");
|
|
Log(" -config game configuration (e.g., Shipping, Development, etc...)");
|
|
Log(" -distribution packaging for final distribution");
|
|
Log(" -codebased packaging a c++ code based project");
|
|
Log(" -createstub packaging stub IPA for later repackaging");
|
|
Log(" -mac <MacName> overrides the machine to use for any Mac operations");
|
|
Log(" -arch <Architecture> sets the architecture to use (blank for default, -simulator for simulator builds)");
|
|
Log(" -device <DeviceID> sets the device to install the IPA on");
|
|
Log("");
|
|
Log("Commands: RPC, Clean");
|
|
Log(" StageMacFiles, GetIPA, Deploy, Install, Uninstall");
|
|
Log("");
|
|
Log("RPC Commands: SetExec, InstallProvision, MakeApp, DeleteIPA, Copy, Kill, Strip, Zip, GenDSYM");
|
|
Log("");
|
|
Log("Sample commandlines:");
|
|
Log(" ... iPhonePackager Deploy UnrealGame Release");
|
|
Log(" ... iPhonePackager RPC SwordGame Shipping MakeApp");
|
|
return (int)ErrorCodes.Error_Arguments;
|
|
}
|
|
|
|
Log("Executing iPhonePackager " + String.Join(" ", args));
|
|
Log("CWD: " + Directory.GetCurrentDirectory());
|
|
Log("Initial Dir: " + InitialCurrentDirectory);
|
|
Log("Env CWD: " + Environment.CurrentDirectory);
|
|
|
|
// Ensure shipping configuration for final distributions
|
|
if (Config.bForDistribution && (GameConfiguration != "Shipping"))
|
|
{
|
|
Program.Warning("Distribution builds should be made in the Shipping configuration!");
|
|
}
|
|
|
|
// process the GamePath (if could be ..\Samples\MyDemo\ or ..\Samples\MyDemo\MyDemo.uproject
|
|
GameName = Path.GetFileNameWithoutExtension(GamePath);
|
|
if (GameName.Equals("UE4", StringComparison.InvariantCultureIgnoreCase) || GameName.Equals("Engine", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
GameName = "UnrealGame";
|
|
}
|
|
|
|
// setup configuration
|
|
if (!Config.Initialize(InitialCurrentDirectory, GamePath))
|
|
{
|
|
return (int)ErrorCodes.Error_Arguments;
|
|
}
|
|
|
|
switch (MainCommand.ToLowerInvariant())
|
|
{
|
|
case "validate":
|
|
// check to see if iTunes is installed
|
|
string dllPath = "";
|
|
if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix)
|
|
{
|
|
ProcessStartInfo StartInfo = new ProcessStartInfo("/usr/bin/xcode-select", "--print-path");
|
|
StartInfo.UseShellExecute = false;
|
|
StartInfo.RedirectStandardOutput = true;
|
|
StartInfo.CreateNoWindow = true;
|
|
|
|
using (Process LocalProcess = Process.Start(StartInfo))
|
|
{
|
|
StreamReader OutputReader = LocalProcess.StandardOutput;
|
|
// trim off any extraneous new lines, helpful for those one-line outputs
|
|
dllPath = OutputReader.ReadToEnd().Trim();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dllPath = Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Apple Inc.\\Apple Mobile Device Support\\Shared", "iTunesMobileDeviceDLL", null) as string;
|
|
if (String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath))
|
|
{
|
|
dllPath = Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Apple Inc.\\Apple Mobile Device Support\\Shared", "MobileDeviceDLL", null) as string;
|
|
if (String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath))
|
|
{
|
|
// iTunes >= 12.7 doesn't have a key specifying the 32-bit DLL but it does have a ASMapiInterfaceDLL key and MobileDevice.dll is in usually in the same directory
|
|
dllPath = Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Apple Inc.\\Apple Mobile Device Support\\Shared", "ASMapiInterfaceDLL", null) as string;
|
|
dllPath = String.IsNullOrEmpty(dllPath) ? null : dllPath.Substring(0, dllPath.LastIndexOf('\\') + 1) + "MobileDevice.dll";
|
|
|
|
if (String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath))
|
|
{
|
|
dllPath = FindWindowsStoreITunesDLL();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (String.IsNullOrEmpty(dllPath) || (!File.Exists(dllPath) && !Directory.Exists(dllPath)))
|
|
{
|
|
Error("iTunes Not Found!!", (int)ErrorCodes.Error_SDKNotFound);
|
|
}
|
|
else
|
|
{
|
|
// validate there is a useable provision and cert
|
|
MobileProvision Provision;
|
|
X509Certificate2 Cert;
|
|
bool bHasOverrides;
|
|
bool bNameMatch;
|
|
bool foundPlist = CodeSignatureBuilder.FindRequiredFiles(out Provision, out Cert, out bHasOverrides, out bNameMatch);
|
|
if (!foundPlist)
|
|
{
|
|
Error("Could not find a valid plist file!!", (int)ErrorCodes.Error_InfoPListNotFound);
|
|
}
|
|
else if (!Config.bAutomaticSigning)
|
|
{
|
|
if (Provision == null && Cert == null)
|
|
{
|
|
Error("No Provision or cert found!!", (int)ErrorCodes.Error_ProvisionAndCertificateNotFound);
|
|
}
|
|
else if (Provision == null)
|
|
{
|
|
Error("No Provision found!!", (int)ErrorCodes.Error_ProvisionNotFound);
|
|
}
|
|
else if (Cert == null)
|
|
{
|
|
Error("No Signing Certificate found!!", (int)ErrorCodes.Error_CertificateNotFound);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Config.TeamID == null)
|
|
{
|
|
Error("No TeamID for automatic signing!!", (int)ErrorCodes.Error_ProvisionNotFound);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case "packageapp":
|
|
if (CheckArguments())
|
|
{
|
|
if (Config.bCreateStubSet)
|
|
{
|
|
Error("packageapp cannot be used with the -createstub switch");
|
|
Program.ReturnCode = (int)ErrorCodes.Error_Arguments;
|
|
}
|
|
else
|
|
{
|
|
// Create the .app on the Mac
|
|
CompileTime.CreateApplicationDirOnMac();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case "repackagefromstage":
|
|
if (CheckArguments())
|
|
{
|
|
if (Config.bCreateStubSet)
|
|
{
|
|
Error("repackagefromstage cannot be used with the -createstub switches");
|
|
Program.ReturnCode = (int)ErrorCodes.Error_Arguments;
|
|
}
|
|
else
|
|
{
|
|
bool bProbablyCreatedStub = Utilities.GetEnvironmentVariable("ue.IOSCreateStubIPA", true);
|
|
if (!bProbablyCreatedStub)
|
|
{
|
|
Warning("ue.IOSCreateStubIPA is currently FALSE, which means you may be repackaging with an out of date stub IPA!");
|
|
}
|
|
|
|
CookTime.RepackageIPAFromStub();
|
|
}
|
|
}
|
|
break;
|
|
|
|
// this is the "super fast just move executable" mode for quick programmer iteration
|
|
case "dangerouslyfast":
|
|
if (CheckArguments())
|
|
{
|
|
CompileTime.DangerouslyFastMode();
|
|
}
|
|
break;
|
|
|
|
case "packageipa":
|
|
if (CheckArguments())
|
|
{
|
|
CompileTime.PackageIPAOnMac();
|
|
}
|
|
break;
|
|
|
|
case "install":
|
|
GameName = "";
|
|
if (Config.bProvision)
|
|
{
|
|
ToolsHub.TryInstallingMobileProvision(Config.Provision, false);
|
|
}
|
|
if (Config.bCert)
|
|
{
|
|
ToolsHub.TryInstallingCertificate_PromptForKey(Config.Certificate, false);
|
|
}
|
|
CodeSignatureBuilder.FindCertificates();
|
|
CodeSignatureBuilder.FindProvisions(Config.OverrideBundleName);
|
|
break;
|
|
|
|
case "certificates":
|
|
{
|
|
CodeSignatureBuilder.FindCertificates();
|
|
CodeSignatureBuilder.FindProvisions(Config.OverrideBundleName);
|
|
}
|
|
break;
|
|
|
|
case "resigntool":
|
|
RunInVisualMode(delegate { return new GraphicalResignTool(); });
|
|
break;
|
|
|
|
case "certrequest":
|
|
RunInVisualMode(delegate { return new GenerateSigningRequestDialog(); });
|
|
break;
|
|
|
|
case "gui":
|
|
RunInVisualMode(delegate { return ToolsHub.CreateShowingTools(); });
|
|
break;
|
|
|
|
case "devices":
|
|
ListDevices();
|
|
break;
|
|
|
|
case "signing_match":
|
|
{
|
|
MobileProvision Provision;
|
|
X509Certificate2 Cert;
|
|
bool bNameMatch;
|
|
bool bHasOverrideFile;
|
|
MobileProvision.CacheMobileProvisions();
|
|
if (CodeSignatureBuilder.FindRequiredFiles(out Provision, out Cert, out bHasOverrideFile, out bNameMatch) && Cert != null)
|
|
{
|
|
// print out the provision and cert name
|
|
Program.LogVerbose("CERTIFICATE-{0},PROVISION-{1}", CryptoAdapter.GetFriendlyNameFromCert(Cert), Provision.FileName);
|
|
}
|
|
else
|
|
{
|
|
Program.LogVerbose("No matching Signing Data found!");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case "cleanprovisions":
|
|
MobileProvision.CleanMobileProvisions();
|
|
break;
|
|
|
|
default:
|
|
// Commands by themself default to packaging for the device
|
|
if (CheckArguments())
|
|
{
|
|
ExecuteCommand(MainCommand, MainRPCCommand);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
Error("Application exception: " + Ex.ToString());
|
|
if (ReturnCode == 0)
|
|
{
|
|
Program.ReturnCode = (int)ErrorCodes.Error_Unknown;
|
|
}
|
|
}
|
|
|
|
Environment.ExitCode = ReturnCode;
|
|
return (ReturnCode);
|
|
}
|
|
}
|
|
}
|
|
|