Beispiel #1
0
        void UpdateStatus()
        {
            MobileProvision  Provision;
            X509Certificate2 Cert;
            bool             bOverridesExists;
            bool             bNameMatch;

            CodeSignatureBuilder.FindRequiredFiles(out Provision, out Cert, out bOverridesExists, out bNameMatch, false);

            int ProvisionVal = 0;

            if (Provision != null)
            {
                ProvisionVal = 1;
                if (bNameMatch)
                {
                    ProvisionVal = 2;
                }
            }
            int CertVal = 0;

            if (Cert != null)
            {
                CertVal = 2;
            }
            MobileProvisionCheck2.Image    = MobileProvisionCheck.Image = CheckStateImages[ProvisionVal];
            CertificatePresentCheck2.Image = CertificatePresentCheck.Image = CheckStateImages[CertVal];
//			OverridesPresentCheck2.Image = OverridesPresentCheck.Image = CheckStateImages[bOverridesExists];

//			ReadyToPackageButton.Enabled = /*bOverridesExists && */(Provision != null) && (Cert != null);
        }
        /// <summary>
        /// Export the certificate to a file
        /// </summary>
        static public void ExportCertificate()
        {
            if (Config.ProvisionFile == null)
            {
                Program.Error("Missing -ProvisionFile=... argument");
                return;
            }

            if (Config.OutputCertificate == null)
            {
                Program.Error("Missing -OutputCertificate=... argument");
                return;
            }

            // export the signing certificate to a file
            MobileProvision  Provision   = MobileProvisionParser.ParseFile(Config.ProvisionFile);
            X509Certificate2 Certificate = CodeSignatureBuilder.FindCertificate(Provision);

            if (Certificate == null)
            {
                Program.Error("Failed to find a valid certificate");
                return;
            }

            byte[] Data = Certificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "A");
            File.WriteAllBytes(Config.OutputCertificate, Data);
        }
Beispiel #3
0
        /// <summary>
        /// Export the certificate to a file
        /// </summary>
        static public void ExportCertificate()
        {
            string ProvisionWithPrefix;

            if (!FindMobileProvision("", out ProvisionWithPrefix))
            {
                Program.Error("Missing provision");
                return;
            }

            if (Config.Certificate == null)
            {
                Program.Error("Missing -Certificate=... argument");
                return;
            }

            // export the signing certificate to a file
            MobileProvision Provision   = MobileProvisionParser.ParseFile(ProvisionWithPrefix);
            var             Certificate = CodeSignatureBuilder.FindCertificate(Provision);

            byte[] Data = Certificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "A");
            File.WriteAllBytes(Config.Certificate, Data);
        }
Beispiel #4
0
        /// <summary>
        /// Copy the files always needed (even in a stub IPA)
        /// </summary>
        static public void CopyFilesNeededForMakeApp()
        {
            // Copy Info.plist over (modifiying it as needed)
            string SourcePListFilename = Utilities.GetPrecompileSourcePListFilename();

            Utilities.PListHelper Info = Utilities.PListHelper.CreateFromFile(SourcePListFilename);

            // Edit the plist
            CookTime.UpdateVersion(Info);

            // Write out the <GameName>-Info.plist file to the xcode staging directory
            string TargetPListFilename = Path.Combine(Config.PCXcodeStagingDir, Program.GameName + "-Info.plist");

            Directory.CreateDirectory(Path.GetDirectoryName(TargetPListFilename));
            string OutString = Info.SaveToString();

            OutString = OutString.Replace("${EXECUTABLE_NAME}", Program.GameName);
            OutString = OutString.Replace("${BUNDLE_IDENTIFIER}", Program.GameName.Replace("_", ""));

            // this is a temp way to inject the iphone 6 images without needing to upgrade everyone's plist
            // eventually we want to generate this based on what the user has set in the project settings
            string[] IPhoneConfigs =
            {
                "Default-IPhone6",               "Landscape", "{375, 667}",
                "Default-IPhone6",               "Portrait",  "{375, 667}",
                "Default-IPhone6Plus-Landscape", "Landscape", "{414, 736}",
                "Default-IPhone6Plus-Portrait",  "Portrait",  "{414, 736}",
                "Default",                       "Landscape", "{320, 480}",
                "Default",                       "Portrait",  "{320, 480}",
                "Default-568h",                  "Landscape", "{320, 568}",
                "Default-568h",                  "Portrait",  "{320, 568}",
                "Default-IPhoneX-Landscape",     "Landscape", "{375, 812}",
                "Default-IPhoneX-Portrait",      "Portrait",  "{375, 812}",
            };

            StringBuilder NewLaunchImagesString = new StringBuilder("<key>UILaunchImages~iphone</key>\n\t\t<array>\n");

            for (int ConfigIndex = 0; ConfigIndex < IPhoneConfigs.Length; ConfigIndex += 3)
            {
                NewLaunchImagesString.Append("\t\t\t<dict>\n");
                NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageMinimumOSVersion</key>\n");
                NewLaunchImagesString.Append("\t\t\t\t<string>8.0</string>\n");
                NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageName</key>\n");
                NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 0]);
                NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageOrientation</key>\n");
                NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 1]);
                NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageSize</key>\n");
                NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 2]);
                NewLaunchImagesString.Append("\t\t\t</dict>\n");
            }

            // close it out
            NewLaunchImagesString.Append("\t\t\t</array>\n\t\t<key>UILaunchImages~ipad</key>");
            OutString = OutString.Replace("<key>UILaunchImages~ipad</key>", NewLaunchImagesString.ToString());

            byte[] RawInfoPList = Encoding.UTF8.GetBytes(OutString);
            File.WriteAllBytes(TargetPListFilename, RawInfoPList);

            Program.Log("Updating .plist: {0} --> {1}", SourcePListFilename, TargetPListFilename);

            // look for an entitlements file (optional)
            string SourceEntitlements = FileOperations.FindPrefixedFile(Config.BuildDirectory, Program.GameName + ".entitlements");

            // set where to make the entitlements file (
            string TargetEntitlements = Path.Combine(Config.PCXcodeStagingDir, Program.GameName + ".entitlements");

            if (File.Exists(SourceEntitlements))
            {
                FileOperations.CopyRequiredFile(SourceEntitlements, TargetEntitlements);
            }
            else
            {
                // 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)
                File.WriteAllText(TargetEntitlements, string.Format("<plist><dict><key>get-task-allow</key><{0}/></dict></plist>",
                                                                    Config.bForDistribution ? "false" : "true"));
            }

            // Copy the mobile provision file over
            string CFBundleIdentifier = null;

            Info.GetString("CFBundleIdentifier", out CFBundleIdentifier);
            bool   bNameMatch;
            string ProvisionWithPrefix = MobileProvision.FindCompatibleProvision(CFBundleIdentifier, out bNameMatch);

            if (!File.Exists(ProvisionWithPrefix))
            {
                ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.BuildDirectory, Program.GameName + ".mobileprovision");
                if (!File.Exists(ProvisionWithPrefix))
                {
                    ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.BuildDirectory + "/NotForLicensees/", Program.GameName + ".mobileprovision");
                    if (!File.Exists(ProvisionWithPrefix))
                    {
                        ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.EngineBuildDirectory, "UE4Game.mobileprovision");
                        if (!File.Exists(ProvisionWithPrefix))
                        {
                            ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.EngineBuildDirectory + "/NotForLicensees/", "UE4Game.mobileprovision");
                        }
                    }
                }
            }
            string FinalMobileProvisionFilename = Path.Combine(Config.PCXcodeStagingDir, MacMobileProvisionFilename);

            FileOperations.CopyRequiredFile(ProvisionWithPrefix, FinalMobileProvisionFilename);

            // make sure this .mobileprovision file is newer than any other .mobileprovision file on the Mac (this file gets multiple games named the same file,
            // so the time stamp checking can fail when moving between games, a la the buildmachines!)
            File.SetLastWriteTime(FinalMobileProvisionFilename, DateTime.UtcNow);
            string ProjectFile = Config.RootRelativePath + @"Engine\Intermediate\ProjectFiles\UE4.xcodeproj\project.pbxproj";

            if (Program.GameName != "UE4Game")
            {
                ProjectFile = Path.GetDirectoryName(Config.IntermediateDirectory) + @"\ProjectFiles\" + Program.GameName + @".xcodeproj\project.pbxproj";
            }
            FileOperations.CopyRequiredFile(ProjectFile, Path.Combine(Config.PCXcodeStagingDir, @"project.pbxproj.datecheck"));

            // copy the signing certificate over
            // export the signing certificate to a file
            MobileProvision Provision   = MobileProvisionParser.ParseFile(ProvisionWithPrefix);
            var             Certificate = CodeSignatureBuilder.FindCertificate(Provision);

            byte[] Data = Certificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "A");
            File.WriteAllBytes(Path.Combine(Config.PCXcodeStagingDir, MacSigningIdentityFilename), Data);
            Config.CodeSigningIdentity  = Certificate.FriendlyName; // since the pipeline will use a temporary keychain that will contain only this certificate, this should be the only identity that will work
            CurrentBaseXCodeCommandLine = GetBaseXcodeCommandline();

            // get the UUID
            string AllText = File.ReadAllText(FinalMobileProvisionFilename);
            string UUID    = "";
            int    idx     = AllText.IndexOf("<key>UUID</key>");

            if (idx > 0)
            {
                idx = AllText.IndexOf("<string>", idx);
                if (idx > 0)
                {
                    idx += "<string>".Length;
                    UUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx);
                }
            }
            CurrentBaseXCodeCommandLine += String.Format(" PROVISIONING_PROFILE=" + UUID);

            // needs Mac line endings so it can be executed
            string SrcPath  = @"..\..\..\Build\" + Config.OSString + @"\XcodeSupportFiles\prepackage.sh";
            string DestPath = Path.Combine(Config.PCXcodeStagingDir, @"prepackage.sh");

            Program.Log(" ... '" + SrcPath + "' -> '" + DestPath + "'");
            string SHContents = File.ReadAllText(SrcPath);

            SHContents = SHContents.Replace("\r\n", "\n");
            File.WriteAllText(DestPath, SHContents);

            CookTime.CopySignedFiles();
        }
Beispiel #5
0
        /**
         * Using the stub IPA previously compiled on the Mac, create a new IPA with assets
         */
        static public void RepackageIPAFromStub()
        {
            if (string.IsNullOrEmpty(Config.RepackageStagingDirectory) || !Directory.Exists(Config.RepackageStagingDirectory))
            {
                Program.Error("Directory specified with -stagedir could not be found!");
                return;
            }


            DateTime             StartTime  = DateTime.Now;
            CodeSignatureBuilder CodeSigner = null;

            // Clean the staging directory
            Program.ExecuteCommand("Clean", null);

            // Create a copy of the IPA so as to not trash the original
            ZipFile Zip = SetupWorkIPA();

            if (Zip == null)
            {
                return;
            }

            string ZipWorkingDir = String.Format("Payload/{0}{1}.app/", Program.GameName, Program.Architecture);

            FileOperations.ZipFileSystem FileSystem = new FileOperations.ZipFileSystem(Zip, ZipWorkingDir);

            // Check for a staged plist that needs to be merged into the main one
            {
                // Determine if there is a staged one we should try to use instead
                string PossiblePList = Path.Combine(Config.RepackageStagingDirectory, "Info.plist");
                if (File.Exists(PossiblePList))
                {
                    if (Config.bPerformResignWhenRepackaging)
                    {
                        Program.Log("Found Info.plist ({0}) in stage, which will be merged in with stub plist contents", PossiblePList);

                        // Merge the two plists, using the staged one as the authority when they conflict
                        byte[] StagePListBytes = File.ReadAllBytes(PossiblePList);
                        string StageInfoString = Encoding.UTF8.GetString(StagePListBytes);

                        byte[] StubPListBytes          = FileSystem.ReadAllBytes("Info.plist");
                        Utilities.PListHelper StubInfo = new Utilities.PListHelper(Encoding.UTF8.GetString(StubPListBytes));

                        StubInfo.MergePlistIn(StageInfoString);

                        // Write it back to the cloned stub, where it will be used for all subsequent actions
                        byte[] MergedPListBytes = Encoding.UTF8.GetBytes(StubInfo.SaveToString());
                        FileSystem.WriteAllBytes("Info.plist", MergedPListBytes);
                    }
                    else
                    {
                        Program.Warning("Found Info.plist ({0}) in stage that will be ignored; IPP cannot combine it with the stub plist since -sign was not specified", PossiblePList);
                    }
                }
            }

            // Get the name of the executable file
            string CFBundleExecutable;

            {
                // Load the .plist from the stub
                byte[] RawInfoPList = FileSystem.ReadAllBytes("Info.plist");

                Utilities.PListHelper Info = new Utilities.PListHelper(Encoding.UTF8.GetString(RawInfoPList));

                // Get the name of the executable file
                if (!Info.GetString("CFBundleExecutable", out CFBundleExecutable))
                {
                    throw new InvalidDataException("Info.plist must contain the key CFBundleExecutable");
                }
            }

            // Tell the file system about the executable file name so that we can set correct attributes on
            // the file when zipping it up
            FileSystem.ExecutableFileName = CFBundleExecutable;

            // Prepare for signing if requested
            if (Config.bPerformResignWhenRepackaging)
            {
                // Start the resign process (load the mobileprovision and info.plist, find the cert, etc...)
                CodeSigner            = new CodeSignatureBuilder();
                CodeSigner.FileSystem = FileSystem;

                CodeSigner.PrepareForSigning();

                // Merge in any user overrides that exist
                UpdateVersion(CodeSigner.Info);
            }

            // Empty the current staging directory
            FileOperations.DeleteDirectory(new DirectoryInfo(Config.PCStagingRootDir));

            // we will zip files in the pre-staged payload dir
            string ZipSourceDir = Config.RepackageStagingDirectory;

            // Save the zip
            Program.Log("Saving IPA ...");

            FilesBeingModifiedToPrintOut.Clear();
            Zip.SaveProgress += UpdateSaveProgress;

            Zip.CompressionLevel = (Ionic.Zlib.CompressionLevel)Config.RecompressionSetting;

            // Add all of the payload files, replacing existing files in the stub IPA if necessary (should only occur for icons)
            {
                string   SourceDir    = Path.GetFullPath(ZipSourceDir);
                string[] PayloadFiles = Directory.GetFiles(SourceDir, "*.*", SearchOption.AllDirectories);
                foreach (string Filename in PayloadFiles)
                {
                    // Get the relative path to the file (this implementation only works because we know the files are all
                    // deeper than the base dir, since they were generated from a search)
                    string AbsoluteFilename = Path.GetFullPath(Filename);
                    string RelativeFilename = AbsoluteFilename.Substring(SourceDir.Length + 1).Replace('\\', '/');

                    string ZipAbsolutePath = String.Format("Payload/{0}{1}.app/{1}",
                                                           Program.GameName,
                                                           Program.Architecture,
                                                           RelativeFilename);

                    byte[] FileContents = File.ReadAllBytes(AbsoluteFilename);
                    if (FileContents.Length == 0)
                    {
                        // Zero-length files added by Ionic cause installation/upgrade to fail on device with error 0xE8000050
                        // We store a single byte in the files as a workaround for now
                        FileContents    = new byte[1];
                        FileContents[0] = 0;
                    }

                    FileSystem.WriteAllBytes(RelativeFilename, FileContents);

                    if ((FileContents.Length >= 1024 * 1024) || (Config.bVerbose))
                    {
                        FilesBeingModifiedToPrintOut.Add(ZipAbsolutePath);
                    }
                }
            }

            // Re-sign the executable if there is a signing context
            if (CodeSigner != null)
            {
                if (Config.OverrideBundleName != null)
                {
                    CodeSigner.Info.SetString("CFBundleDisplayName", Config.OverrideBundleName);
                    string CFBundleIdentifier;
                    if (CodeSigner.Info.GetString("CFBundleIdentifier", out CFBundleIdentifier))
                    {
                        CodeSigner.Info.SetString("CFBundleIdentifier", CFBundleIdentifier + "_" + Config.OverrideBundleName);
                    }
                }
                CodeSigner.PerformSigning();
            }

            // Stick in the iTunesArtwork PNG if available
            string iTunesArtworkPath = Path.Combine(Config.BuildDirectory, "iTunesArtwork");

            if (File.Exists(iTunesArtworkPath))
            {
                Zip.UpdateFile(iTunesArtworkPath, "");
            }

            // Save the Zip

            Program.Log("Compressing files into IPA (-compress={1}).{0}", Config.bVerbose ? "" : "  Only large files will be listed next, but other files are also being packaged.", Config.RecompressionSetting);
            FileSystem.Close();

            TimeSpan ZipLength = DateTime.Now - StartTime;

            FileInfo FinalZipInfo = new FileInfo(Zip.Name);


            Program.Log(String.Format("Finished repackaging into {2:0.00} MB IPA, written to '{0}' (took {1:0.00} s for all steps)",
                                      Zip.Name,
                                      ZipLength.TotalSeconds,
                                      FinalZipInfo.Length / (1024.0f * 1024.0f)));
        }
Beispiel #6
0
        public static string FindCompatibleProvision(string CFBundleIdentifier, out bool bNameMatch, bool bCheckCert = true, bool bCheckIdentifier = true, bool bCheckDistro = true)
        {
            bNameMatch = false;

            // remap the gamename if necessary
            string GameName = Program.GameName;

            if (GameName == "UE4Game")
            {
                if (Config.ProjectFile.Length > 0)
                {
                    GameName = Path.GetFileNameWithoutExtension(Config.ProjectFile);
                }
            }

            // ensure the provision directory exists
            if (!Directory.Exists(Config.ProvisionDirectory))
            {
                Directory.CreateDirectory(Config.ProvisionDirectory);
            }

            if (Config.bProvision)
            {
                if (File.Exists(Config.ProvisionDirectory + "/" + Config.Provision))
                {
                    return(Config.ProvisionDirectory + "/" + Config.Provision);
                }
            }

            #region remove after we provide an install mechanism
            CacheMobileProvisions();
            #endregion

            // cache the provision library
            Dictionary <string, MobileProvision> ProvisionLibrary = new Dictionary <string, MobileProvision>();
            foreach (string Provision in Directory.EnumerateFiles(Config.ProvisionDirectory, "*.mobileprovision"))
            {
                MobileProvision p = MobileProvisionParser.ParseFile(Provision);
                ProvisionLibrary.Add(Provision, p);
                if (p.FileName.Contains(p.UUID) && !File.Exists(Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision")))
                {
                    File.Copy(Provision, Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision"));
                    p = MobileProvisionParser.ParseFile(Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision"));
                    ProvisionLibrary.Add(Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision"), p);
                }
            }

            Program.Log("Searching for mobile provisions that match the game '{0}' (distribution: {3}) with CFBundleIdentifier='{1}' in '{2}'", GameName, CFBundleIdentifier, Config.ProvisionDirectory, Config.bForDistribution);

            // check the cache for a provision matching the app id (com.company.Game)
            // First checking for a contains match and then for a wildcard match
            for (int Phase = -1; Phase < 3; ++Phase)
            {
                if (Phase == -1 && string.IsNullOrEmpty(Config.ProvisionUUID))
                {
                    continue;
                }
                foreach (KeyValuePair <string, MobileProvision> Pair in ProvisionLibrary)
                {
                    string          DebugName     = Path.GetFileName(Pair.Key);
                    MobileProvision TestProvision = Pair.Value;

                    // make sure the file is not managed by Xcode
                    if (Path.GetFileName(TestProvision.FileName).ToLower().Equals(TestProvision.UUID.ToLower() + ".mobileprovision"))
                    {
                        continue;
                    }

                    Program.LogVerbose("  Phase {0} considering provision '{1}' named '{2}'", Phase, DebugName, TestProvision.ProvisionName);

                    if (TestProvision.ProvisionName == "iOS Team Provisioning Profile: " + CFBundleIdentifier)
                    {
                        Program.LogVerbose("  Failing as provisioning is automatic");
                        continue;
                    }

                    // check to see if the platform is the same as what we are looking for
                    if (!string.IsNullOrEmpty(TestProvision.Platform) && TestProvision.Platform != Config.OSString && !string.IsNullOrEmpty(Config.OSString))
                    {
                        //Program.LogVerbose("  Failing platform {0} Config: {1}", TestProvision.Platform, Config.OSString);
                        continue;
                    }

                    // Validate the name
                    bool bPassesNameCheck = false;
                    if (Phase == -1)
                    {
                        bPassesNameCheck = TestProvision.UUID == Config.ProvisionUUID;
                        bNameMatch       = bPassesNameCheck;
                    }
                    else if (Phase == 0)
                    {
                        bPassesNameCheck = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1) == CFBundleIdentifier;
                        bNameMatch       = bPassesNameCheck;
                    }
                    else if (Phase == 1)
                    {
                        if (TestProvision.ApplicationIdentifier.Contains("*"))
                        {
                            string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1);
                            if (CompanyName != "*")
                            {
                                CompanyName      = CompanyName.Substring(0, CompanyName.LastIndexOf("."));
                                bPassesNameCheck = CFBundleIdentifier.StartsWith(CompanyName);
                            }
                        }
                    }
                    else
                    {
                        if (TestProvision.ApplicationIdentifier.Contains("*"))
                        {
                            string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1);
                            bPassesNameCheck = CompanyName == "*";
                        }
                    }
                    if (!bPassesNameCheck && bCheckIdentifier)
                    {
                        Program.LogVerbose("  .. Failed phase {0} name check (provision app ID was {1})", Phase, TestProvision.ApplicationIdentifier);
                        continue;
                    }

                    if (Config.bForDistribution)
                    {
                        // Check to see if this is a distribution provision. get-task-allow must be false for distro profiles.
                        // TestProvision.ProvisionedDeviceIDs.Count==0 is not a valid check as ad-hoc distro profiles do list devices.
                        bool bDistroProv = !TestProvision.bDebug;
                        if (!bDistroProv)
                        {
                            Program.LogVerbose("  .. Failed distribution check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count);
                            continue;
                        }
                    }
                    else
                    {
                        if (bCheckDistro)
                        {
                            bool bPassesDebugCheck = TestProvision.bDebug;
                            if (!bPassesDebugCheck)
                            {
                                Program.LogVerbose("  .. Failed debugging check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count);
                                continue;
                            }
                        }
                        else
                        {
                            if (!TestProvision.bDebug)
                            {
                                Config.bForceStripSymbols = true;
                            }
                        }
                    }

                    // Check to see if the provision is in date
                    DateTime CurrentUTCTime   = DateTime.UtcNow;
                    bool     bPassesDateCheck = (CurrentUTCTime >= TestProvision.CreationDate) && (CurrentUTCTime < TestProvision.ExpirationDate);
                    if (!bPassesDateCheck)
                    {
                        Program.LogVerbose("  .. Failed time period check (valid from {0} to {1}, but UTC time is now {2})", TestProvision.CreationDate, TestProvision.ExpirationDate, CurrentUTCTime);
                        continue;
                    }

                    // check to see if we have a certificate for this provision
                    bool bPassesHasMatchingCertCheck = false;
                    if (bCheckCert)
                    {
                        X509Certificate2 Cert = CodeSignatureBuilder.FindCertificate(TestProvision);
                        bPassesHasMatchingCertCheck = (Cert != null);
                        if (bPassesHasMatchingCertCheck && Config.bCert)
                        {
                            bPassesHasMatchingCertCheck &= (CryptoAdapter.GetFriendlyNameFromCert(Cert) == Config.Certificate);
                        }
                    }
                    else
                    {
                        bPassesHasMatchingCertCheck = true;
                    }

                    if (!bPassesHasMatchingCertCheck)
                    {
                        Program.LogVerbose("  .. Failed to find a matching certificate that was in date");
                        continue;
                    }

                    // Made it past all the tests
                    Program.LogVerbose("  Picked '{0}' with AppID '{1}' and Name '{2}' as a matching provision for the game '{3}'", DebugName, TestProvision.ApplicationIdentifier, TestProvision.ProvisionName, GameName);
                    return(Pair.Key);
                }
            }

            // check to see if there is already an embedded provision
            string EmbeddedMobileProvisionFilename = Path.Combine(Config.RepackageStagingDirectory, "embedded.mobileprovision");

            Program.Warning("Failed to find a valid matching mobile provision, will attempt to use the embedded mobile provision instead if present");
            return(EmbeddedMobileProvisionFilename);
        }
Beispiel #7
0
        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);

            // 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("	 -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("	 -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 UDKGame 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 = "UE4Game";
                }

                // 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)
                    {
                        dllPath = "/Applications/Xcode.app/Contents/MacOS/Xcode";
                    }
                    else
                    {
                        dllPath = Microsoft.Win32.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 = Microsoft.Win32.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))
                    {
                        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 (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);
                        }
                    }
                    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;

                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;
                }
            }
            finally
            {
                if (DeploymentHelper.DeploymentServerProcess != null)
                {
                    DeploymentHelper.DeploymentServerProcess.Close();
                }
            }

            Environment.ExitCode = ReturnCode;
            return(ReturnCode);
        }
Beispiel #8
0
        public static string FindCompatibleProvision(string CFBundleIdentifier, out bool bNameMatch, bool bCheckCert = true, bool bCheckIdentifier = true)
        {
            bNameMatch = false;

            // remap the gamename if necessary
            string GameName = Program.GameName;

            if (GameName == "UE4Game")
            {
                if (Config.ProjectFile.Length > 0)
                {
                    GameName = Path.GetFileNameWithoutExtension(Config.ProjectFile);
                }
            }

            // ensure the provision directory exists
            if (!Directory.Exists(Config.ProvisionDirectory))
            {
                Directory.CreateDirectory(Config.ProvisionDirectory);
            }

            if (Config.bProvision)
            {
                if (File.Exists(Config.ProvisionDirectory + "/" + Config.Provision))
                {
                    return(Config.ProvisionDirectory + "/" + Config.Provision);
                }
            }

            #region remove after we provide an install mechanism
            // copy all of the provisions from the game directory to the library
            if (!String.IsNullOrEmpty(Config.ProjectFile))
            {
                var ProjectFileBuildIOSPath = Path.GetDirectoryName(Config.ProjectFile) + "/Build/IOS/";
                if (Directory.Exists(ProjectFileBuildIOSPath))
                {
                    foreach (string Provision in Directory.EnumerateFiles(ProjectFileBuildIOSPath, "*.mobileprovision", SearchOption.AllDirectories))
                    {
                        if (!File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision)) || File.GetLastWriteTime(Config.ProvisionDirectory + Path.GetFileName(Provision)) < File.GetLastWriteTime(Provision))
                        {
                            FileInfo DestFileInfo;
                            if (File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision)))
                            {
                                DestFileInfo            = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision));
                                DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
                            }
                            File.Copy(Provision, Config.ProvisionDirectory + Path.GetFileName(Provision), true);
                            DestFileInfo            = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision));
                            DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
                        }
                    }
                }
            }

            // copy all of the provisions from the engine directory to the library
            {
                if (Directory.Exists(Config.EngineBuildDirectory))
                {
                    foreach (string Provision in Directory.EnumerateFiles(Config.EngineBuildDirectory, "*.mobileprovision", SearchOption.AllDirectories))
                    {
                        if (!File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision)) || File.GetLastWriteTime(Config.ProvisionDirectory + Path.GetFileName(Provision)) < File.GetLastWriteTime(Provision))
                        {
                            FileInfo DestFileInfo;
                            if (File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision)))
                            {
                                DestFileInfo            = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision));
                                DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
                            }
                            File.Copy(Provision, Config.ProvisionDirectory + Path.GetFileName(Provision), true);
                            DestFileInfo            = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision));
                            DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
                        }
                    }
                }
            }
            #endregion

            // cache the provision library
            Dictionary <string, MobileProvision> ProvisionLibrary = new Dictionary <string, MobileProvision>();
            foreach (string Provision in Directory.EnumerateFiles(Config.ProvisionDirectory, "*.mobileprovision"))
            {
                MobileProvision p = MobileProvisionParser.ParseFile(Provision);
                ProvisionLibrary.Add(Provision, p);
            }

            Program.Log("Searching for mobile provisions that match the game '{0}' with CFBundleIdentifier='{1}' in '{2}'", GameName, CFBundleIdentifier, Config.ProvisionDirectory);

            // check the cache for a provision matching the app id (com.company.Game)
            // First checking for a contains match and then for a wildcard match
            for (int Phase = 0; Phase < 3; ++Phase)
            {
                foreach (KeyValuePair <string, MobileProvision> Pair in ProvisionLibrary)
                {
                    string          DebugName     = Path.GetFileName(Pair.Key);
                    MobileProvision TestProvision = Pair.Value;

                    Program.LogVerbose("  Phase {0} considering provision '{1}' named '{2}'", Phase, DebugName, TestProvision.ProvisionName);

                    // Validate the name
                    bool bPassesNameCheck = false;
                    if (Phase == 0)
                    {
                        bPassesNameCheck = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1) == CFBundleIdentifier;
                        bNameMatch       = bPassesNameCheck;
                    }
                    else if (Phase == 1)
                    {
                        if (TestProvision.ApplicationIdentifier.Contains("*"))
                        {
                            string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1);
                            if (CompanyName != "*")
                            {
                                CompanyName      = CompanyName.Substring(0, CompanyName.LastIndexOf("."));
                                bPassesNameCheck = CFBundleIdentifier.StartsWith(CompanyName);
                            }
                        }
                    }
                    else
                    {
                        if (TestProvision.ApplicationIdentifier.Contains("*"))
                        {
                            string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1);
                            bPassesNameCheck = CompanyName == "*";
                        }
                    }
                    if (!bPassesNameCheck && bCheckIdentifier)
                    {
                        Program.LogVerbose("  .. Failed phase {0} name check (provision app ID was {1})", Phase, TestProvision.ApplicationIdentifier);
                        continue;
                    }

                    if (Config.bForDistribution)
                    {
                        // check to see if this is a distribution provision
                        bool bDistroProv = (TestProvision.ProvisionedDeviceIDs.Count == 0) && !TestProvision.bDebug;
                        if (!bDistroProv)
                        {
                            Program.LogVerbose("  .. Failed distribution check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count);
                            continue;
                        }
                    }
                    else
                    {
                        // check to see if we pass the debug check for non-distribution
                        bool bPassesDebugCheck = TestProvision.bDebug;
                        if (!bPassesDebugCheck)
                        {
                            Program.LogVerbose("  .. Failed debugging check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count);
                            continue;
                        }
                    }

                    // Check to see if the provision is in date
                    DateTime CurrentUTCTime   = DateTime.UtcNow;
                    bool     bPassesDateCheck = (CurrentUTCTime >= TestProvision.CreationDate) && (CurrentUTCTime < TestProvision.ExpirationDate);
                    if (!bPassesDateCheck)
                    {
                        Program.LogVerbose("  .. Failed time period check (valid from {0} to {1}, but UTC time is now {2})", TestProvision.CreationDate, TestProvision.ExpirationDate, CurrentUTCTime);
                        continue;
                    }

                    // check to see if we have a certificate for this provision
                    bool bPassesHasMatchingCertCheck = false;
                    if (bCheckCert)
                    {
                        X509Certificate2 Cert = CodeSignatureBuilder.FindCertificate(TestProvision);
                        bPassesHasMatchingCertCheck = (Cert != null);
                        if (bPassesHasMatchingCertCheck && Config.bCert)
                        {
                            bPassesHasMatchingCertCheck &= (Cert.FriendlyName == Config.Certificate);
                        }
                    }
                    else
                    {
                        bPassesHasMatchingCertCheck = true;
                    }

                    if (!bPassesHasMatchingCertCheck)
                    {
                        Program.LogVerbose("  .. Failed to find a matching certificate that was in date");
                        continue;
                    }

                    // Made it past all the tests
                    Program.LogVerbose("  Picked '{0}' with AppID '{1}' and Name '{2}' as a matching provision for the game '{3}'", DebugName, TestProvision.ApplicationIdentifier, TestProvision.ProvisionName, GameName);
                    return(Pair.Key);
                }
            }

            // check to see if there is already an embedded provision
            string EmbeddedMobileProvisionFilename = Path.Combine(Config.RepackageStagingDirectory, "embedded.mobileprovision");

            Program.Warning("Failed to find a valid matching mobile provision, will attempt to use the embedded mobile provision instead if present");
            return(EmbeddedMobileProvisionFilename);
        }
Beispiel #9
0
        /// <summary>
        /// Makes sure the required files for code signing exist and can be found
        /// </summary>
        public static bool FindRequiredFiles(out MobileProvision Provision, out X509Certificate2 Cert, out bool bHasOverridesFile, out bool bNameMatch, bool bNameCheck = true)
        {
            Provision         = null;
            Cert              = null;
            bHasOverridesFile = File.Exists(Config.GetPlistOverrideFilename());
            bNameMatch        = false;

            string CFBundleIdentifier = Config.OverrideBundleName;

            if (string.IsNullOrEmpty(CFBundleIdentifier))
            {
                // Load Info.plist, which guides nearly everything else
                string plistFile = Config.EngineBuildDirectory + "/UE4Game-Info.plist";
                if (!string.IsNullOrEmpty(Config.ProjectFile))
                {
                    plistFile = Path.GetDirectoryName(Config.ProjectFile) + "/Intermediate/IOS/" + Path.GetFileNameWithoutExtension(Config.ProjectFile) + "-Info.plist";

                    if (!File.Exists(plistFile))
                    {
                        plistFile = Config.IntermediateDirectory + "/UE4Game-Info.plist";
                        if (!File.Exists(plistFile))
                        {
                            plistFile = Config.EngineBuildDirectory + "/UE4Game-Info.plist";
                        }
                    }
                }
                Utilities.PListHelper Info = null;
                try
                {
                    string RawInfoPList = File.ReadAllText(plistFile, Encoding.UTF8);
                    Info = new Utilities.PListHelper(RawInfoPList);;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }

                if (Info == null)
                {
                    return(false);
                }

                // Get the name of the bundle
                Info.GetString("CFBundleIdentifier", out CFBundleIdentifier);
                if (CFBundleIdentifier == null)
                {
                    return(false);
                }
                else
                {
                    CFBundleIdentifier = CFBundleIdentifier.Replace("${BUNDLE_IDENTIFIER}", Path.GetFileNameWithoutExtension(Config.ProjectFile));
                }
            }

            // Check for a mobile provision
            try
            {
                string MobileProvisionFilename = MobileProvision.FindCompatibleProvision(CFBundleIdentifier, out bNameMatch);
                Provision = MobileProvisionParser.ParseFile(MobileProvisionFilename);
            }
            catch (Exception)
            {
            }

            // if we have a null provision see if we can find a compatible provision without checking for a certificate
            if (Provision == null)
            {
                try
                {
                    string MobileProvisionFilename = MobileProvision.FindCompatibleProvision(CFBundleIdentifier, out bNameMatch, false);
                    Provision = MobileProvisionParser.ParseFile(MobileProvisionFilename);
                }
                catch (Exception)
                {
                }

                // if we have a null provision see if we can find a valid provision without checking for name match
                if (Provision == null && !bNameCheck)
                {
                    try
                    {
                        string MobileProvisionFilename = MobileProvision.FindCompatibleProvision(CFBundleIdentifier, out bNameMatch, false, false);
                        Provision = MobileProvisionParser.ParseFile(MobileProvisionFilename);
                    }
                    catch (Exception)
                    {
                    }
                }
            }

            // Check for a suitable signature to match the mobile provision
            if (Provision != null)
            {
                Cert = CodeSignatureBuilder.FindCertificate(Provision);
            }

            return(true);
        }
Beispiel #10
0
        public static bool ExecuteDeployCommand(string Command, string GamePath, string RPCCommand)
        {
            switch (Command.ToLowerInvariant())
            {
            case "backup":
            {
                string ApplicationIdentifier = RPCCommand;
                if (ApplicationIdentifier == null)
                {
                    ApplicationIdentifier = Utilities.GetStringFromPList("CFBundleIdentifier");
                }

                if (Config.FilesForBackup.Count > 0)
                {
                    if (!DeploymentHelper.Get().BackupFiles(ApplicationIdentifier, Config.FilesForBackup.ToArray()))
                    {
                        Program.Error("Failed to transfer manifest file from device to PC");
                        Program.ReturnCode = (int)ErrorCodes.Error_DeviceBackupFailed;
                    }
                }
                else if (!DeploymentHelper.Get().BackupDocumentsDirectory(ApplicationIdentifier, Config.GetRootBackedUpDocumentsDirectory()))
                {
                    Program.Error("Failed to transfer documents directory from device to PC");
                    Program.ReturnCode = (int)ErrorCodes.Error_DeviceBackupFailed;
                }
            }
            break;

            case "uninstall":
            {
                string ApplicationIdentifier = RPCCommand;
                if (ApplicationIdentifier == null)
                {
                    ApplicationIdentifier = Utilities.GetStringFromPList("CFBundleIdentifier");
                }

                if (!DeploymentHelper.Get().UninstallIPAOnDevice(ApplicationIdentifier))
                {
                    Program.Error("Failed to uninstall IPA on device");
                    Program.ReturnCode = (int)ErrorCodes.Error_AppUninstallFailed;
                }
            }
            break;

            case "deploy":
            case "install":
            {
                string IPAPath = GamePath;
                string AdditionalCommandline = Program.AdditionalCommandline;

                if (!String.IsNullOrEmpty(AdditionalCommandline) && !Config.bIterate)
                {
                    // Read the mobile provision to check for issues
                    FileOperations.ReadOnlyZipFileSystem Zip = new FileOperations.ReadOnlyZipFileSystem(IPAPath);
                    try
                    {
                        // Compare the commandline embedded to prevent us from any unnecessary writing.
                        byte[] CommandlineBytes    = Zip.ReadAllBytes("ue4commandline.txt");
                        string ExistingCommandline = Encoding.UTF8.GetString(CommandlineBytes, 0, CommandlineBytes.Length);
                        if (ExistingCommandline != AdditionalCommandline)
                        {
                            // Ensure we have a temp dir to stage our temporary ipa
                            if (!Directory.Exists(Config.PCStagingRootDir))
                            {
                                Directory.CreateDirectory(Config.PCStagingRootDir);
                            }

                            string TmpFilePath = Path.Combine(Path.GetDirectoryName(Config.PCStagingRootDir), Path.GetFileNameWithoutExtension(IPAPath) + ".tmp.ipa");
                            if (File.Exists(TmpFilePath))
                            {
                                File.Delete(TmpFilePath);
                            }

                            File.Copy(IPAPath, TmpFilePath);

                            // Get the project name:
                            string ProjectFile = ExistingCommandline.Split(' ').FirstOrDefault();

                            // Write out the new commandline.
                            FileOperations.ZipFileSystem WritableZip = new FileOperations.ZipFileSystem(TmpFilePath);
                            byte[] NewCommandline = Encoding.UTF8.GetBytes(ProjectFile + " " + AdditionalCommandline);
                            WritableZip.WriteAllBytes("ue4commandline.txt", NewCommandline);

                            // We need to residn the application after the commandline file has changed.
                            CodeSignatureBuilder CodeSigner = new CodeSignatureBuilder();
                            CodeSigner.FileSystem = WritableZip;

                            CodeSigner.PrepareForSigning();
                            CodeSigner.PerformSigning();

                            WritableZip.Close();

                            // Set the deploying ipa path to our new ipa
                            IPAPath = TmpFilePath;
                        }
                    }
                    catch (System.Exception ex)
                    {
                        Program.Warning(String.Format("Failed to override the commandline.txt file: ({0})", ex.Message));
                    }
                    Zip.Close();
                }

                if (Config.bIterate)
                {
                    string ApplicationIdentifier = RPCCommand;
                    if (String.IsNullOrEmpty(ApplicationIdentifier))
                    {
                        ApplicationIdentifier = Utilities.GetStringFromPList("CFBundleIdentifier");
                    }

                    DeploymentHelper.Get().DeviceId = Config.DeviceId;
                    if (!DeploymentHelper.Get().InstallFilesOnDevice(ApplicationIdentifier, Config.DeltaManifest))
                    {
                        Program.Error("Failed to install Files on device");
                        Program.ReturnCode = (int)ErrorCodes.Error_FilesInstallFailed;
                    }
                }
                else if (File.Exists(IPAPath))
                {
                    DeploymentHelper.Get().DeviceId = Config.DeviceId;
                    if (!DeploymentHelper.Get().InstallIPAOnDevice(IPAPath))
                    {
                        Program.Error("Failed to install IPA on device");
                        Program.ReturnCode = (int)ErrorCodes.Error_AppInstallFailed;
                    }
                }
                else
                {
                    Program.Error(String.Format("Failed to find IPA file: '{0}'", IPAPath));
                    Program.ReturnCode = (int)ErrorCodes.Error_AppNotFound;
                }
            }
            break;

            default:
                return(false);
            }

            return(true);
        }
Beispiel #11
0
        /**
         * Using the stub IPA previously compiled on the Mac, create a new IPA with assets
         */
        public static void RepackageIPAFromStub()
        {
            if (string.IsNullOrEmpty(Config.RepackageStagingDirectory) || !Directory.Exists(Config.RepackageStagingDirectory))
            {
                Program.Error("Directory specified with -stagedir could not be found!");
                return;
            }

            DateTime StartTime = DateTime.Now;
            CodeSignatureBuilder CodeSigner = null;

            // Clean the staging directory
            Program.ExecuteCommand("Clean", null);

            // Create a copy of the IPA so as to not trash the original
            ZipFile Zip = SetupWorkIPA();
            if (Zip == null)
            {
                return;
            }

            string ZipWorkingDir = String.Format("Payload/{0}{1}.app/", Program.GameName, Program.Architecture);

            FileOperations.ZipFileSystem FileSystem = new FileOperations.ZipFileSystem(Zip, ZipWorkingDir);

            // Check for a staged plist that needs to be merged into the main one
            {
                // Determine if there is a staged one we should try to use instead
                string PossiblePList = Path.Combine(Config.RepackageStagingDirectory, "Info.plist");
                if (File.Exists(PossiblePList))
                {
                    if (Config.bPerformResignWhenRepackaging)
                    {
                        Program.Log("Found Info.plist ({0}) in stage, which will be merged in with stub plist contents", PossiblePList);

                        // Merge the two plists, using the staged one as the authority when they conflict
                        byte[] StagePListBytes = File.ReadAllBytes(PossiblePList);
                        string StageInfoString = Encoding.UTF8.GetString(StagePListBytes);

                        byte[] StubPListBytes = FileSystem.ReadAllBytes("Info.plist");
                        Utilities.PListHelper StubInfo = new Utilities.PListHelper(Encoding.UTF8.GetString(StubPListBytes));

                        StubInfo.MergePlistIn(StageInfoString);

                        // Write it back to the cloned stub, where it will be used for all subsequent actions
                        byte[] MergedPListBytes = Encoding.UTF8.GetBytes(StubInfo.SaveToString());
                        FileSystem.WriteAllBytes("Info.plist", MergedPListBytes);
                    }
                    else
                    {
                        Program.Warning("Found Info.plist ({0}) in stage that will be ignored; IPP cannot combine it with the stub plist since -sign was not specified", PossiblePList);
                    }
                }
            }

            // Get the name of the executable file
            string CFBundleExecutable;
            {
                // Load the .plist from the stub
                byte[] RawInfoPList = FileSystem.ReadAllBytes("Info.plist");

                Utilities.PListHelper Info = new Utilities.PListHelper(Encoding.UTF8.GetString(RawInfoPList));

                // Get the name of the executable file
                if (!Info.GetString("CFBundleExecutable", out CFBundleExecutable))
                {
                    throw new InvalidDataException("Info.plist must contain the key CFBundleExecutable");
                }
            }

            // Tell the file system about the executable file name so that we can set correct attributes on
            // the file when zipping it up
            FileSystem.ExecutableFileName = CFBundleExecutable;

            // Prepare for signing if requested
            if (Config.bPerformResignWhenRepackaging)
            {
                // Start the resign process (load the mobileprovision and info.plist, find the cert, etc...)
                CodeSigner = new CodeSignatureBuilder();
                CodeSigner.FileSystem = FileSystem;

                CodeSigner.PrepareForSigning();

                // Merge in any user overrides that exist
                UpdateVersion(CodeSigner.Info);
            }

            // Empty the current staging directory
            FileOperations.DeleteDirectory(new DirectoryInfo(Config.PCStagingRootDir));

            // we will zip files in the pre-staged payload dir
            string ZipSourceDir = Config.RepackageStagingDirectory;

            // Save the zip
            Program.Log("Saving IPA ...");

            FilesBeingModifiedToPrintOut.Clear();
            Zip.SaveProgress += UpdateSaveProgress;

            Zip.CompressionLevel = (Ionic.Zlib.CompressionLevel)Config.RecompressionSetting;

            // Add all of the payload files, replacing existing files in the stub IPA if necessary (should only occur for icons)
            {
                string SourceDir = Path.GetFullPath(ZipSourceDir);
                string[] PayloadFiles = Directory.GetFiles(SourceDir, "*.*", SearchOption.AllDirectories);
                foreach (string Filename in PayloadFiles)
                {
                    // Get the relative path to the file (this implementation only works because we know the files are all
                    // deeper than the base dir, since they were generated from a search)
                    string AbsoluteFilename = Path.GetFullPath(Filename);
                    string RelativeFilename = AbsoluteFilename.Substring(SourceDir.Length + 1).Replace('\\', '/');

                    string ZipAbsolutePath = String.Format("Payload/{0}{1}.app/{1}",
                        Program.GameName,
                        Program.Architecture,
                        RelativeFilename);

                    byte[] FileContents = File.ReadAllBytes(AbsoluteFilename);
                    if (FileContents.Length == 0)
                    {
                        // Zero-length files added by Ionic cause installation/upgrade to fail on device with error 0xE8000050
                        // We store a single byte in the files as a workaround for now
                        FileContents = new byte[1];
                        FileContents[0] = 0;
                    }

                    FileSystem.WriteAllBytes(RelativeFilename, FileContents);

                    if ((FileContents.Length >= 1024 * 1024) || (Config.bVerbose))
                    {
                        FilesBeingModifiedToPrintOut.Add(ZipAbsolutePath);
                    }
                }
            }

            // Re-sign the executable if there is a signing context
            if (CodeSigner != null)
            {
                if (Config.OverrideBundleName != null)
                {
                    CodeSigner.Info.SetString("CFBundleDisplayName", Config.OverrideBundleName);
                    string CFBundleIdentifier;
                    if (CodeSigner.Info.GetString("CFBundleIdentifier", out CFBundleIdentifier))
                    {
                        CodeSigner.Info.SetString("CFBundleIdentifier", CFBundleIdentifier + "_" + Config.OverrideBundleName);
                    }
                }
                CodeSigner.PerformSigning();
            }

            // Stick in the iTunesArtwork PNG if available
            string iTunesArtworkPath = Path.Combine(Config.BuildDirectory, "iTunesArtwork");
            if (File.Exists(iTunesArtworkPath))
            {
                Zip.UpdateFile(iTunesArtworkPath, "");
            }

            // Save the Zip
            Program.Log("Compressing files into IPA.{0}", Config.bVerbose ? "" : "  Only large files will be listed next, but other files are also being packaged.");
            FileSystem.Close();

            TimeSpan ZipLength = DateTime.Now - StartTime;

            FileInfo FinalZipInfo = new FileInfo(Config.GetIPAPath(".ipa"));

            Program.Log(String.Format("Finished repackaging into {2:0.00} MB IPA, written to '{0}' (took {1:0.00} s)",
                Zip.Name,
                ZipLength.TotalSeconds,
                FinalZipInfo.Length / (1024.0f * 1024.0f)));
        }
		public static bool ExecuteDeployCommand(string Command, string GamePath, string RPCCommand)
		{
			switch (Command.ToLowerInvariant())
			{
				case "backup":
					{
						string ApplicationIdentifier = RPCCommand;
						if (ApplicationIdentifier == null)
						{
							ApplicationIdentifier = Utilities.GetStringFromPList("CFBundleIdentifier");
						}

						if (Config.FilesForBackup.Count > 0)
						{
							if (!DeploymentHelper.Get().BackupFiles(ApplicationIdentifier, Config.FilesForBackup.ToArray()))
							{
								Program.Error("Failed to transfer manifest file from device to PC");
								Program.ReturnCode = (int)ErrorCodes.Error_DeviceBackupFailed;
							}
						}
						else if (!DeploymentHelper.Get().BackupDocumentsDirectory(ApplicationIdentifier, Config.GetRootBackedUpDocumentsDirectory()))
						{
							Program.Error("Failed to transfer documents directory from device to PC");
							Program.ReturnCode = (int)ErrorCodes.Error_DeviceBackupFailed;
						}
					}
					break;
				case "uninstall":
					{
						string ApplicationIdentifier = RPCCommand;
						if (ApplicationIdentifier == null)
						{
							ApplicationIdentifier = Utilities.GetStringFromPList("CFBundleIdentifier");
						}

						if (!DeploymentHelper.Get().UninstallIPAOnDevice(ApplicationIdentifier))
						{
							Program.Error("Failed to uninstall IPA on device");
							Program.ReturnCode = (int)ErrorCodes.Error_AppUninstallFailed;
						}
					}
					break;
				case "deploy":
				case "install":
					{
						string IPAPath = GamePath;
						string AdditionalCommandline = Program.AdditionalCommandline;

						if (!String.IsNullOrEmpty(AdditionalCommandline) && !Config.bIterate)
						{
							// Read the mobile provision to check for issues
							FileOperations.ReadOnlyZipFileSystem Zip = new FileOperations.ReadOnlyZipFileSystem(IPAPath);
							try
							{
								// Compare the commandline embedded to prevent us from any unnecessary writing.
								byte[] CommandlineBytes = Zip.ReadAllBytes("ue4commandline.txt");
								string ExistingCommandline = Encoding.UTF8.GetString(CommandlineBytes, 0, CommandlineBytes.Length);
								if (ExistingCommandline != AdditionalCommandline)
								{
									// Ensure we have a temp dir to stage our temporary ipa
									if( !Directory.Exists( Config.PCStagingRootDir ) )
									{
										Directory.CreateDirectory(Config.PCStagingRootDir);
									}

									string TmpFilePath = Path.Combine(Path.GetDirectoryName(Config.PCStagingRootDir), Path.GetFileNameWithoutExtension(IPAPath) + ".tmp.ipa");
									if( File.Exists( TmpFilePath ) )
									{
										File.Delete(TmpFilePath);
									}

									File.Copy(IPAPath, TmpFilePath);
								
									// Get the project name:
									string ProjectFile = ExistingCommandline.Split(' ').FirstOrDefault();

									// Write out the new commandline.
									FileOperations.ZipFileSystem WritableZip = new FileOperations.ZipFileSystem(TmpFilePath);
									byte[] NewCommandline = Encoding.UTF8.GetBytes(ProjectFile + " " + AdditionalCommandline);
									WritableZip.WriteAllBytes("ue4commandline.txt", NewCommandline);

									// We need to residn the application after the commandline file has changed.
									CodeSignatureBuilder CodeSigner = new CodeSignatureBuilder();
									CodeSigner.FileSystem = WritableZip;

									CodeSigner.PrepareForSigning();
									CodeSigner.PerformSigning();

									WritableZip.Close();

									// Set the deploying ipa path to our new ipa
									IPAPath = TmpFilePath;
								}
							}
							catch (System.Exception ex)
							{
								Program.Warning(String.Format("Failed to override the commandline.txt file: ({0})", ex.Message));
							}
							Zip.Close();
						}

						if (Config.bIterate)
						{
							string ApplicationIdentifier = RPCCommand;
							if (String.IsNullOrEmpty(ApplicationIdentifier))
							{
								ApplicationIdentifier = Utilities.GetStringFromPList("CFBundleIdentifier");
							}

							DeploymentHelper.Get().DeviceId = Config.DeviceId;
							if (!DeploymentHelper.Get().InstallFilesOnDevice(ApplicationIdentifier, Config.DeltaManifest))
							{
								Program.Error("Failed to install Files on device");
								Program.ReturnCode = (int)ErrorCodes.Error_FilesInstallFailed;
							}
						}
						else if (File.Exists(IPAPath))
						{
							DeploymentHelper.Get().DeviceId = Config.DeviceId;
							if (!DeploymentHelper.Get().InstallIPAOnDevice(IPAPath))
							{
								Program.Error("Failed to install IPA on device");
								Program.ReturnCode = (int)ErrorCodes.Error_AppInstallFailed;
							}
						}
						else
						{
							Program.Error(String.Format("Failed to find IPA file: '{0}'", IPAPath));
							Program.ReturnCode = (int)ErrorCodes.Error_AppNotFound;
						}
					}
					break;

				default:
					return false;
			}

			return true;
		}