// CFBundleDisplayName
		
		// CFBundleName, fewer than 16 characters long

		// CFBundleIdentifier
		// http://developer.apple.com/library/ios/#documentation/general/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-102070
		// Alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.) characters.
		// The string should also be in reverse-DNS format.

		void ReloadPList()
		{
			string Filename = Config.GetPlistOverrideFilename();
			string SourcePList = ReadOrCreate(Filename);
			Utilities.PListHelper Helper = new Utilities.PListHelper(SourcePList);
			
			string BundleID;
			string BundleName;
			string BundleDisplayName;
			if (!Helper.GetString("CFBundleIdentifier", out BundleID))
			{
				BundleID = "com.YourCompany.GameNameNoSpaces";
				Helper.SetString("CFBundleIdentifier", BundleID);
			}

			if (!Helper.GetString("CFBundleName", out BundleName))
			{
				BundleName = "MyUDKGame";
				Helper.SetString("CFBundleName", BundleName);
			}

			if (!Helper.GetString("CFBundleDisplayName", out BundleDisplayName))
			{
				BundleDisplayName = "UDK Game";
				Helper.SetString("CFBundleDisplayName", BundleDisplayName);
			}

			BundleIdentifierEdit.Text = BundleID;
			BundleNameEdit.Text = BundleName;
			BundleDisplayNameEdit.Text = BundleDisplayName;
		}
Exemple #2
0
        /// <summary>
        /// Creates an entitlements blob string from the entitlements structure in the mobile provision, merging in an on disk file if it is present.
        /// </summary>
        private string BuildEntitlementString(string CFBundleIdentifier)
        {
            // Load the base entitlements string from the mobile provision
            string ProvisionEntitlements = Provision.GetEntitlementsString(CFBundleIdentifier);

            // See if there is an override entitlements file on disk
            string UserOverridesEntitlementsFilename = FileOperations.FindPrefixedFile(Config.BuildDirectory, Program.GameName + ".entitlements");

            if (File.Exists(UserOverridesEntitlementsFilename))
            {
                // Merge in the entitlements from the on disk file as overrides
                Program.Log("Merging override entitlements from {0} into provision specified entitlements", Path.GetFileName(UserOverridesEntitlementsFilename));

                Utilities.PListHelper Merger = new Utilities.PListHelper(ProvisionEntitlements);
                string Overrides             = File.ReadAllText(UserOverridesEntitlementsFilename, Encoding.UTF8);
                Merger.MergePlistIn(Overrides);

                return(Merger.SaveToString());
            }
            else
            {
                // The ones from the mobile provision need no overrides
                return(ProvisionEntitlements);
            }
        }
Exemple #3
0
        // CFBundleDisplayName

        // CFBundleName, fewer than 16 characters long

        // CFBundleIdentifier
        // http://developer.apple.com/library/ios/#documentation/general/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-102070
        // Alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.) characters.
        // The string should also be in reverse-DNS format.

        void ReloadPList()
        {
            string Filename    = Config.GetPlistOverrideFilename();
            string SourcePList = ReadOrCreate(Filename);

            Utilities.PListHelper Helper = new Utilities.PListHelper(SourcePList);

            string BundleID;
            string BundleName;
            string BundleDisplayName;

            if (!Helper.GetString("CFBundleIdentifier", out BundleID))
            {
                BundleID = "com.YourCompany.GameNameNoSpaces";
                Helper.SetString("CFBundleIdentifier", BundleID);
            }

            if (!Helper.GetString("CFBundleName", out BundleName))
            {
                BundleName = "MyUDKGame";
                Helper.SetString("CFBundleName", BundleName);
            }

            if (!Helper.GetString("CFBundleDisplayName", out BundleDisplayName))
            {
                BundleDisplayName = "UDK Game";
                Helper.SetString("CFBundleDisplayName", BundleDisplayName);
            }

            BundleIdentifierEdit.Text  = BundleID;
            BundleNameEdit.Text        = BundleName;
            BundleDisplayNameEdit.Text = BundleDisplayName;
        }
Exemple #4
0
        /// <summary>
        /// Updates the version string and then applies the settings in the user overrides plist
        /// </summary>
        /// <param name="Info"></param>
        public static void UpdateVersion(Utilities.PListHelper Info)
        {
            // Update the minor version number if the current one is older than the version tracker file
            // Assuming that the version will be set explicitly in the overrides file for distribution
            VersionUtilities.UpdateMinorVersion(Info);

            // Mark the type of build (development or distribution)
            Info.SetString("EpicPackagingMode", Config.bForDistribution ? "Distribution" : "Development");
        }
        private void SavePList(Utilities.PListHelper Helper, string Filename)
        {
            FileInfo DestInfo = new FileInfo(Filename);

            if (DestInfo.Exists && DestInfo.IsReadOnly)
            {
                DestInfo.IsReadOnly = false;
            }
            Helper.SaveToFile(Filename);
        }
Exemple #6
0
        protected virtual byte[] CreateCodeResourcesDirectory(string CFBundleExecutable)
        {
            // Create a rules dict that includes (by wildcard) everything but Info.plist and the rules file
            Dictionary <string, object> Rules = new Dictionary <string, object>();

            Rules.Add(".*", true);
            Rules.Add("^Info.plist", CreateOmittedResource(10));

            // Create the full list of files to exclude (some files get excluded by 'magic' even though they aren't listed as special by rules)
            Dictionary <string, object> TrueExclusionList = new Dictionary <string, object>();

            TrueExclusionList.Add("Info.plist", null);
            TrueExclusionList.Add(CFBundleExecutable, null);
            TrueExclusionList.Add("CodeResources", null);
            TrueExclusionList.Add("_CodeSignature/CodeResources", null);

            // Hash each file
            IEnumerable <string>      FileList     = FileSystem.GetAllPayloadFiles();
            SHA1CryptoServiceProvider HashProvider = new SHA1CryptoServiceProvider();

            Utilities.PListHelper HashedFileEntries = new Utilities.PListHelper();
            foreach (string Filename in FileList)
            {
                if (!TrueExclusionList.ContainsKey(Filename))
                {
                    byte[] FileData = FileSystem.ReadAllBytes(Filename);
                    byte[] HashData = HashProvider.ComputeHash(FileData);

                    HashedFileEntries.AddKeyValuePair(Filename, HashData);
                }
            }

            // Create the CodeResources file that will contain the hashes
            Utilities.PListHelper CodeResources = new Utilities.PListHelper();
            CodeResources.AddKeyValuePair("files", HashedFileEntries);
            CodeResources.AddKeyValuePair("rules", Rules);

            // Write the CodeResources file out
            string CodeResourcesAsString = CodeResources.SaveToString();

            byte[] CodeResourcesAsBytes = Encoding.UTF8.GetBytes(CodeResourcesAsString);
            FileSystem.WriteAllBytes("_CodeSignature/CodeResources", CodeResourcesAsBytes);

            return(CodeResourcesAsBytes);
        }
Exemple #7
0
        /// <summary>
        /// Prepares this signer to sign an application
        ///   Modifies the following files:
        ///	 embedded.mobileprovision
        /// </summary>
        public void PrepareForSigning()
        {
            // Load Info.plist, which guides nearly everything else
            Info = LoadInfoPList();

            // Get the name of the bundle
            string CFBundleIdentifier;

            if (!Info.GetString("CFBundleIdentifier", out CFBundleIdentifier))
            {
                throw new InvalidDataException("Info.plist must contain the key CFBundleIdentifier");
            }

            // Load the mobile provision, which provides entitlements and a partial cert which can be used to find an installed certificate
            LoadMobileProvision(CFBundleIdentifier);
            if (Provision == null)
            {
                return;
            }

            // Install the Apple trust chain certs (required to do a CMS signature with full chain embedded)
            List <string> TrustChainCertFilenames = new List <string>();

            string CertPath = Path.GetFullPath(Config.EngineBuildDirectory);

            TrustChainCertFilenames.Add(Path.Combine(CertPath, "AppleWorldwideDeveloperRelationsCA.pem"));
            TrustChainCertFilenames.Add(Path.Combine(CertPath, "AppleRootCA.pem"));

            InstallCertificates(TrustChainCertFilenames);

            // Find and load the signing cert
            SigningCert = LoadSigningCertificate();
            if (SigningCert == null)
            {
                // Failed to find a cert already installed or to install, cannot proceed any futher
                Program.Error("... Failed to find a certificate that matches the mobile provision to be used for code signing");
                Program.ReturnCode = (int)ErrorCodes.Error_CertificateNotFound;
                throw new InvalidDataException("Certificate not found!");
            }
            else
            {
                Program.Log("... Found matching certificate '{0}' (valid from {1} to {2})", SigningCert.FriendlyName, SigningCert.GetEffectiveDateString(), SigningCert.GetExpirationDateString());
            }
        }
Exemple #8
0
        /// <summary>
        /// Extracts the dict values for the Entitlements key and creates a new full .plist file
        /// from them (with outer plist and dict keys as well as doctype, etc...)
        /// </summary>
        public string GetEntitlementsString(string CFBundleIdentifier, out string TeamIdentifier)
        {
            Utilities.PListHelper XCentPList = null;
            Data.ProcessValueForKey("Entitlements", "dict", delegate(XmlNode ValueNode)
            {
                XCentPList = Utilities.PListHelper.CloneDictionaryRootedAt(ValueNode);
            });

            // Modify the application-identifier to be fully qualified if needed
            string CurrentApplicationIdentifier;

            XCentPList.GetString("application-identifier", out CurrentApplicationIdentifier);
            XCentPList.GetString("com.apple.developer.team-identifier", out TeamIdentifier);

//			if (CurrentApplicationIdentifier.Contains("*"))
            {
                // Replace the application identifier
                string NewApplicationIdentifier = String.Format("{0}.{1}", ApplicationIdentifierPrefix, CFBundleIdentifier);
                XCentPList.SetString("application-identifier", NewApplicationIdentifier);


                // Replace the keychain access groups
                // Note: This isn't robust, it ignores the existing value in the wildcard and uses the same value for
                // each entry.  If there is a legitimate need for more than one entry in the access group list, then
                // don't use a wildcard!
                List <string> KeyGroups = XCentPList.GetArray("keychain-access-groups", "string");

                for (int i = 0; i < KeyGroups.Count; ++i)
                {
                    string Entry = KeyGroups[i];
                    if (Entry.Contains("*"))
                    {
                        Entry = NewApplicationIdentifier;
                    }
                    KeyGroups[i] = Entry;
                }

                XCentPList.SetValueForKey("keychain-access-groups", KeyGroups);
            }

            return(XCentPList.SaveToString());
        }
Exemple #9
0
        /// <summary>
        /// Updates the minor version in the CFBundleVersion key of the specified PList if this is a new package.
        /// Also updates the key EpicAppVersion with the bundle version and the current date/time (no year)
        /// </summary>
        public static void UpdateMinorVersion(Utilities.PListHelper PList)
        {
            string CFBundleVersion;

            if (PList.GetString("CFBundleVersion", out CFBundleVersion))
            {
                string UpdatedValue = CalculateUpdatedMinorVersionString(CFBundleVersion);
                Program.Log("Found CFBundleVersion string '{0}' and updated it to '{1}'", CFBundleVersion, UpdatedValue);
                PList.SetString("CFBundleVersion", UpdatedValue);
            }
            else
            {
                CFBundleVersion = "0.0";
            }

            // Write a second key with the packaging date/time in it
            string PackagingTime = DateTime.Now.ToString(@"MM-dd HH:mm");

            PList.SetString("EpicAppVersion", CFBundleVersion + " " + PackagingTime);
        }
		private void SaveChanges()
		{
			// Development settings
			{
				// Open the existing plist override file
				string DevFilename = Config.GetPlistOverrideFilename(false);
				string SourcePList = ReadOrCreate(DevFilename);
				Utilities.PListHelper Helper = new Utilities.PListHelper(SourcePList);

				// Jam the edited values in
				if (!Helper.HasKey("UIFileSharingEnabled"))
				{
					Helper.SetValueForKey("UIFileSharingEnabled", true);
				}

				Helper.SetString("CFBundleIdentifier", BundleIdentifierEdit.Text);
				Helper.SetString("CFBundleName", BundleNameEdit.Text);
				Helper.SetString("CFBundleDisplayName", BundleDisplayNameEdit.Text);

				// Save the modified plist
				SavePList(Helper, DevFilename);
			}

			// Distribution settings
			{
				// Open the existing plist override file
				string DistFilename = Config.GetPlistOverrideFilename(true);
				string SourcePList = ReadOrCreate(DistFilename);
				Utilities.PListHelper Helper = new Utilities.PListHelper(SourcePList);

				// Jam the edited values in
				if (!Helper.HasKey("UIFileSharingEnabled"))
				{
					Helper.SetValueForKey("UIFileSharingEnabled", false);
				}

				// Save the modified plist
				SavePList(Helper, DistFilename);
			}
		}
        private void SaveChanges()
        {
            // Development settings
            {
                // Open the existing plist override file
                string DevFilename           = Config.GetPlistOverrideFilename(false);
                string SourcePList           = ReadOrCreate(DevFilename);
                Utilities.PListHelper Helper = new Utilities.PListHelper(SourcePList);

                // Jam the edited values in
                if (!Helper.HasKey("UIFileSharingEnabled"))
                {
                    Helper.SetValueForKey("UIFileSharingEnabled", true);
                }

                Helper.SetString("CFBundleIdentifier", BundleIdentifierEdit.Text);
                Helper.SetString("CFBundleName", BundleNameEdit.Text);
                Helper.SetString("CFBundleDisplayName", BundleDisplayNameEdit.Text);

                // Save the modified plist
                SavePList(Helper, DevFilename);
            }

            // Distribution settings
            {
                // Open the existing plist override file
                string DistFilename          = Config.GetPlistOverrideFilename(true);
                string SourcePList           = ReadOrCreate(DistFilename);
                Utilities.PListHelper Helper = new Utilities.PListHelper(SourcePList);

                // Jam the edited values in
                if (!Helper.HasKey("UIFileSharingEnabled"))
                {
                    Helper.SetValueForKey("UIFileSharingEnabled", false);
                }

                // Save the modified plist
                SavePList(Helper, DistFilename);
            }
        }
Exemple #12
0
        string PickIPAAndFetchBundleIdentifier()
        {
            string PickedFilename;

            if (ToolsHub.ShowOpenFileDialog(ToolsHub.IpaFilter, "Choose an IPA to provide the bundle identifier", "ipa", "", ref ToolsHub.ChoosingIpaDirectory, out PickedFilename))
            {
                FileOperations.FileSystemAdapter IPA = new FileOperations.ReadOnlyZipFileSystem(PickedFilename);

                if (IPA != null)
                {
                    byte[] InfoData            = IPA.ReadAllBytes("Info.plist");
                    Utilities.PListHelper Info = new Utilities.PListHelper(Encoding.UTF8.GetString(InfoData));

                    string BundleID;
                    if (Info.GetString("CFBundleIdentifier", out BundleID))
                    {
                        return(BundleID);
                    }
                }
            }

            return(null);
        }
        /// <summary>
        /// Constructs a MobileProvision from an xml blob extracted from the real ASN.1 file
        /// </summary>
        public MobileProvision(string EmbeddedPListText)
        {
            Data = new Utilities.PListHelper(EmbeddedPListText);

            // Now extract things

            // Key: ApplicationIdentifierPrefix, Array<String>
            List<string> PrefixList = Data.GetArray("ApplicationIdentifierPrefix", "string");
            if (PrefixList.Count > 1)
            {
                Program.Warning("Found more than one entry for ApplicationIdentifierPrefix in the .mobileprovision, using the first one found");
            }

            if (PrefixList.Count > 0)
            {
                ApplicationIdentifierPrefix = PrefixList[0];
            }

            // Key: DeveloperCertificates, Array<Data> (uuencoded)
            string CertificatePassword = "";
            List<string> CertificateList = Data.GetArray("DeveloperCertificates", "data");
            foreach (string EncodedCert in CertificateList)
            {
                byte[] RawCert = Convert.FromBase64String(EncodedCert);
                DeveloperCertificates.Add(new X509Certificate2(RawCert, CertificatePassword));
            }

            // Key: Name, String
            if (!Data.GetString("Name", out ProvisionName))
            {
                ProvisionName = "(unknown)";
            }

            // Key: ProvisionedDevices, Array<String>
            ProvisionedDeviceIDs = Data.GetArray("ProvisionedDevices", "string");
        }
        /// <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}",
            };

            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 no sign resource rules file over
            if (!Config.bForDistribution)
            {
                FileOperations.CopyRequiredFile(@"..\..\..\Build\IOS\XcodeSupportFiles\CustomResourceRules.plist", Path.Combine(Config.PCStagingRootDir, "CustomResourceRules.plist"));
            }

            // Copy the mobile provision file over
            string 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);

            FileOperations.CopyRequiredFile(Config.RootRelativePath + @"Engine\Intermediate\IOS\UE4.xcodeproj\project.pbxproj", Path.Combine(Config.PCXcodeStagingDir, @"project.pbxproj.datecheck"));

            // needs Mac line endings so it can be executed
            string SrcPath  = @"..\..\..\Build\IOS\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();
        }
Exemple #15
0
        protected virtual byte[] CreateCodeResourcesDirectory(string CFBundleExecutable)
        {
            // Create a rules dict that includes (by wildcard) everything but Info.plist and the rules file
            Dictionary <string, object> Rules1 = new Dictionary <string, object>();

            Rules1.Add("^", true);
            Rules1.Add("^.*\\.lproj/", CreateOptionalResource(1000));
            Rules1.Add("^.*\\.lproj/locversion.plist$", CreateOmittedResource(1100));
            Rules1.Add("^version.plist$", true);

            Dictionary <string, object> Rules2 = new Dictionary <string, object>();

            Rules2.Add(".*\\.dSYM($|/)", CreateResource(11));
            Rules2.Add("^", CreateResource(20));
            Rules2.Add("^(.*/)?\\.DS_Store$", CreateOmittedResource(2000));
            Rules2.Add("^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/", CreateNestedResource(10));
            Rules2.Add("^.*", true);
            Rules2.Add("^.*\\.lproj/", CreateOptionalResource(1000));
            Rules2.Add("^.*\\.lproj/locversion.plist$", CreateOmittedResource(1100));
            Rules2.Add("^Info\\.plist$", CreateOmittedResource(20));
            Rules2.Add("^PkgInfo$", CreateOmittedResource(20));
            Rules2.Add("^[^/]+$", CreateNestedResource(10));
            Rules2.Add("^embedded\\.provisionprofile$", CreateResource(20));
            Rules2.Add("^version\\.plist$", CreateResource(20));

            // Create the full list of files to exclude (some files get excluded by 'magic' even though they aren't listed as special by rules)
            Dictionary <string, object> TrueExclusionList1 = new Dictionary <string, object>();

            TrueExclusionList1.Add(CFBundleExecutable, null);
            TrueExclusionList1.Add("CodeResources", null);
            TrueExclusionList1.Add("_CodeSignature/CodeResources", null);

            Dictionary <string, object> TrueExclusionList2 = new Dictionary <string, object>();

            TrueExclusionList2.Add("Info.plist", null);
            TrueExclusionList2.Add("PkgInfo", null);
            TrueExclusionList2.Add(CFBundleExecutable, null);
            TrueExclusionList2.Add("CodeResources", null);
            TrueExclusionList2.Add("_CodeSignature/CodeResources", null);

            // Hash each file
            IEnumerable <string>      FileList     = FileSystem.GetAllPayloadFiles();
            SHA1CryptoServiceProvider HashProvider = new SHA1CryptoServiceProvider();

            Utilities.PListHelper HashedFileEntries1 = new Utilities.PListHelper();
            Utilities.PListHelper HashedFileEntries2 = new Utilities.PListHelper();
            foreach (string Filename in FileList)
            {
                if (!TrueExclusionList1.ContainsKey(Filename))
                {
                    byte[] FileData = FileSystem.ReadAllBytes(Filename);
                    byte[] HashData = HashProvider.ComputeHash(FileData);

                    HashedFileEntries1.AddKeyValuePair(Filename, HashData);
                }
                if (!TrueExclusionList2.ContainsKey(Filename))
                {
                    byte[] FileData = FileSystem.ReadAllBytes(Filename);
                    byte[] HashData = HashProvider.ComputeHash(FileData);

                    HashedFileEntries2.AddKeyValuePair(Filename, HashData);
                }
            }

            // Create the CodeResources file that will contain the hashes
            Utilities.PListHelper CodeResources = new Utilities.PListHelper();
            CodeResources.AddKeyValuePair("files", HashedFileEntries1);
            CodeResources.AddKeyValuePair("files2", HashedFileEntries2);
            CodeResources.AddKeyValuePair("rules", Rules1);
            CodeResources.AddKeyValuePair("rules2", Rules2);

            // Write the CodeResources file out
            string CodeResourcesAsString = CodeResources.SaveToString();

            byte[] CodeResourcesAsBytes = Encoding.UTF8.GetBytes(CodeResourcesAsString);
            FileSystem.WriteAllBytes("_CodeSignature/CodeResources", CodeResourcesAsBytes);

            return(CodeResourcesAsBytes);
        }
Exemple #16
0
        /// <summary>
        /// Constructs a MobileProvision from an xml blob extracted from the real ASN.1 file
        /// </summary>
        public MobileProvision(string EmbeddedPListText)
        {
            Data = new Utilities.PListHelper(EmbeddedPListText);

            // Now extract things

            // Key: ApplicationIdentifierPrefix, Array<String>
            List <string> PrefixList = Data.GetArray("ApplicationIdentifierPrefix", "string");

            if (PrefixList.Count > 1)
            {
                Program.Warning("Found more than one entry for ApplicationIdentifierPrefix in the .mobileprovision, using the first one found");
            }

            if (PrefixList.Count > 0)
            {
                ApplicationIdentifierPrefix = PrefixList[0];
            }

            // Example date string from the XML: "2014-06-30T20:45:55Z";
            DateTimeStyles AppleDateStyle = DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal;

            string CreationDateString;

            if (Data.GetDate("CreationDate", out CreationDateString))
            {
                CreationDate = DateTime.Parse(CreationDateString, CultureInfo.InvariantCulture, AppleDateStyle);
            }

            string ExpirationDateString;

            if (Data.GetDate("ExpirationDate", out ExpirationDateString))
            {
                ExpirationDate = DateTime.Parse(ExpirationDateString, CultureInfo.InvariantCulture, AppleDateStyle);
            }

            // Key: DeveloperCertificates, Array<Data> (uuencoded)
            string        CertificatePassword = "";
            List <string> CertificateList     = Data.GetArray("DeveloperCertificates", "data");

            foreach (string EncodedCert in CertificateList)
            {
                byte[] RawCert = Convert.FromBase64String(EncodedCert);
                DeveloperCertificates.Add(new X509Certificate2(RawCert, CertificatePassword));
            }

            // Key: Name, String
            if (!Data.GetString("Name", out ProvisionName))
            {
                ProvisionName = "(unknown)";
            }

            // Key: ProvisionedDevices, Array<String>
            ProvisionedDeviceIDs = Data.GetArray("ProvisionedDevices", "string");

            // Key: application-identifier, Array<String>
            Utilities.PListHelper XCentPList = null;
            Data.ProcessValueForKey("Entitlements", "dict", delegate(XmlNode ValueNode)
            {
                XCentPList = Utilities.PListHelper.CloneDictionaryRootedAt(ValueNode);
            });

            // Modify the application-identifier to be fully qualified if needed
            if (!XCentPList.GetString("application-identifier", out ApplicationIdentifier))
            {
                ApplicationIdentifier = "(unknown)";
            }

            // check for get-task-allow
            bDebug = XCentPList.GetBool("get-task-allow");

            if (!Data.GetString("UUID", out UUID))
            {
                UUID = "(unkown)";
            }

            List <string> Platforms = Data.GetArray("Platform", "string");

            if (Platforms.Contains("iOS"))
            {
                Platform = "IOS";
            }
            else if (Platforms.Contains("tvOS"))
            {
                Platform = "TVOS";
            }
            else
            {
                Platform = "";
            }
        }
Exemple #17
0
        /// <summary>
        /// Prepares this signer to sign an application
        ///   Modifies the following files:
        ///	 embedded.mobileprovision
        /// </summary>
        public void PrepareForSigning()
        {
            // Load Info.plist, which guides nearly everything else
            Info = LoadInfoPList();

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

            // Load the mobile provision, which provides entitlements and a partial cert which can be used to find an installed certificate
            LoadMobileProvision(CFBundleIdentifier);
            if (Provision == null)
            {
                return;
            }

            // Install the Apple trust chain certs (required to do a CMS signature with full chain embedded)
            List<string> TrustChainCertFilenames = new List<string>();

            string CertPath = Path.GetFullPath(Config.EngineBuildDirectory);
            TrustChainCertFilenames.Add(Path.Combine(CertPath, "AppleWorldwideDeveloperRelationsCA.pem"));
            TrustChainCertFilenames.Add(Path.Combine(CertPath, "AppleRootCA.pem"));

            InstallCertificates(TrustChainCertFilenames);

            // Find and load the signing cert
            SigningCert = LoadSigningCertificate();
            if (SigningCert == null)
            {
                // Failed to find a cert already installed or to install, cannot proceed any futher
                Program.Error("... Failed to find a certificate that matches the mobile provision to be used for code signing");
                Program.ReturnCode = (int)ErrorCodes.Error_CertificateNotFound;
                throw new InvalidDataException("Certificate not found!");
            }
            else
            {
                Program.Log("... Found matching certificate '{0}' (valid from {1} to {2})", SigningCert.FriendlyName, SigningCert.GetEffectiveDateString(), SigningCert.GetExpirationDateString());
            }
        }
Exemple #18
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;
        }
		/// <summary>
		/// Constructs a MobileProvision from an xml blob extracted from the real ASN.1 file
		/// </summary>
		public MobileProvision(string EmbeddedPListText)
		{
			Data = new Utilities.PListHelper(EmbeddedPListText);

			// Now extract things

			// Key: ApplicationIdentifierPrefix, Array<String>
			List<string> PrefixList = Data.GetArray("ApplicationIdentifierPrefix", "string");
			if (PrefixList.Count > 1)
			{
				Program.Warning("Found more than one entry for ApplicationIdentifierPrefix in the .mobileprovision, using the first one found");
			}

			if (PrefixList.Count > 0)
			{
				ApplicationIdentifierPrefix = PrefixList[0];
			}

			// Example date string from the XML: "2014-06-30T20:45:55Z";
			DateTimeStyles AppleDateStyle = DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal;

			string CreationDateString;
			if (Data.GetDate("CreationDate", out CreationDateString))
			{
				CreationDate = DateTime.Parse(CreationDateString, CultureInfo.InvariantCulture, AppleDateStyle);
			}

			string ExpirationDateString;
			if (Data.GetDate("ExpirationDate", out ExpirationDateString))
			{
				ExpirationDate = DateTime.Parse(ExpirationDateString, CultureInfo.InvariantCulture, AppleDateStyle);
			}

			// Key: DeveloperCertificates, Array<Data> (uuencoded)
			string CertificatePassword = "";
			List<string> CertificateList = Data.GetArray("DeveloperCertificates", "data");
			foreach (string EncodedCert in CertificateList)
			{
				byte[] RawCert = Convert.FromBase64String(EncodedCert);
				DeveloperCertificates.Add(new X509Certificate2(RawCert, CertificatePassword));
			}

			// Key: Name, String
			if (!Data.GetString("Name", out ProvisionName))
			{
				ProvisionName = "(unknown)";
			}

			// Key: ProvisionedDevices, Array<String>
			ProvisionedDeviceIDs = Data.GetArray("ProvisionedDevices", "string");

			// Key: application-identifier, Array<String>
			Utilities.PListHelper XCentPList = null;
			Data.ProcessValueForKey("Entitlements", "dict", delegate(XmlNode ValueNode)
			{
				XCentPList = Utilities.PListHelper.CloneDictionaryRootedAt(ValueNode);
			});

			// Modify the application-identifier to be fully qualified if needed
			if (!XCentPList.GetString("application-identifier", out ApplicationIdentifier))
			{
				ApplicationIdentifier = "(unknown)";
			}

			// check for get-task-allow
			bDebug = XCentPList.GetBool("get-task-allow");
		}
Exemple #20
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);
        }
Exemple #21
0
        protected virtual byte[] CreateCodeResourcesDirectory(string CFBundleExecutable)
        {
            // Verify that there is a rules plist
            string CFBundleResourceSpecification;
            if (!Info.GetString("CFBundleResourceSpecification", out CFBundleResourceSpecification))
            {
                throw new InvalidDataException("Info.plist must contain the key CFBundleResourceSpecification");
            }

            // Create a rules dict that includes (by wildcard) everything but Info.plist and the rules file
            Dictionary<string, object> Rules = new Dictionary<string, object>();
            Rules.Add(".*", true);
            Rules.Add("^Info.plist", CreateOmittedResource(10));
            Rules.Add(CFBundleResourceSpecification, CreateOmittedResource(100));

            // Write the rules file out
            {
                Utilities.PListHelper RulesPList = new Utilities.PListHelper();
                RulesPList.AddKeyValuePair("rules", Rules);
                string PListString = RulesPList.SaveToString();
                FileSystem.WriteAllBytes(CFBundleResourceSpecification, Encoding.UTF8.GetBytes(PListString));
            }

            // Create the full list of files to exclude (some files get excluded by 'magic' even though they aren't listed as special by rules)
            Dictionary<string, object> TrueExclusionList = new Dictionary<string, object>();
            TrueExclusionList.Add("Info.plist", null);
            TrueExclusionList.Add(CFBundleResourceSpecification, null);
            TrueExclusionList.Add(CFBundleExecutable, null);
            TrueExclusionList.Add("CodeResources", null);
            TrueExclusionList.Add("_CodeSignature/CodeResources", null);

            // Hash each file
            IEnumerable<string> FileList = FileSystem.GetAllPayloadFiles();
            SHA1CryptoServiceProvider HashProvider = new SHA1CryptoServiceProvider();

            Utilities.PListHelper HashedFileEntries = new Utilities.PListHelper();
            foreach (string Filename in FileList)
            {
                if (!TrueExclusionList.ContainsKey(Filename))
                {
                    byte[] FileData = FileSystem.ReadAllBytes(Filename);
                    byte[] HashData = HashProvider.ComputeHash(FileData);

                    HashedFileEntries.AddKeyValuePair(Filename, HashData);
                }
            }

            // Create the CodeResources file that will contain the hashes
            Utilities.PListHelper CodeResources = new Utilities.PListHelper();
            CodeResources.AddKeyValuePair("files", HashedFileEntries);
            CodeResources.AddKeyValuePair("rules", Rules);

            // Write the CodeResources file out
            string CodeResourcesAsString = CodeResources.SaveToString();
            byte[] CodeResourcesAsBytes = Encoding.UTF8.GetBytes(CodeResourcesAsString);
            FileSystem.WriteAllBytes("_CodeSignature/CodeResources", CodeResourcesAsBytes);

            return CodeResourcesAsBytes;
        }
Exemple #22
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)));
        }
Exemple #23
0
            public XmlElement ConvertValueToPListFormat(object Value)
            {
                XmlElement ValueElement = null;

                if (Value is string)
                {
                    ValueElement           = Doc.CreateElement("string");
                    ValueElement.InnerText = Value as string;
                }
                else if (Value is Dictionary <string, object> )
                {
                    ValueElement = Doc.CreateElement("dict");
                    foreach (var KVP in Value as Dictionary <string, object> )
                    {
                        AddKeyValuePair(ValueElement, KVP.Key, KVP.Value);
                    }
                }
                else if (Value is Utilities.PListHelper)
                {
                    Utilities.PListHelper PList = Value as Utilities.PListHelper;

                    ValueElement = Doc.CreateElement("dict");

                    XmlNode SourceDictionaryNode = PList.Doc.DocumentElement.SelectSingleNode("/plist/dict");
                    foreach (XmlNode TheirChild in SourceDictionaryNode)
                    {
                        ValueElement.AppendChild(Doc.ImportNode(TheirChild, true));
                    }
                }
                else if (Value is Array)
                {
                    if (Value is byte[])
                    {
                        ValueElement           = Doc.CreateElement("data");
                        ValueElement.InnerText = Convert.ToBase64String(Value as byte[]);
                    }
                    else
                    {
                        ValueElement = Doc.CreateElement("array");
                        foreach (var A in Value as Array)
                        {
                            ValueElement.AppendChild(ConvertValueToPListFormat(A));
                        }
                    }
                }
                else if (Value is IList)
                {
                    ValueElement = Doc.CreateElement("array");
                    foreach (var A in Value as IList)
                    {
                        ValueElement.AppendChild(ConvertValueToPListFormat(A));
                    }
                }
                else if (Value is bool)
                {
                    ValueElement = Doc.CreateElement(((bool)Value) ? "true" : "false");
                }
                else if (Value is double)
                {
                    ValueElement           = Doc.CreateElement("real");
                    ValueElement.InnerText = ((double)Value).ToString();
                }
                else if (Value is int)
                {
                    ValueElement           = Doc.CreateElement("integer");
                    ValueElement.InnerText = ((int)Value).ToString();
                }
                else
                {
                    throw new InvalidDataException(String.Format("Object '{0}' is in an unknown type that cannot be converted to PList format", Value));
                }

                return(ValueElement);
            }
        /// <summary>
        /// Prepares this signer to sign an application
        ///   Modifies the following files:
        ///	 embedded.mobileprovision
        /// </summary>
        public void PrepareForSigning()
        {
            // Load Info.plist, which guides nearly everything else
            Info = LoadInfoPList();

            // Load the mobile provision, which provides entitlements and a partial cert which can be used to find an installed certificate
            LoadMobileProvision();
            if (Provision == null)
            {
                return;
            }

            // Install the Apple trust chain certs (required to do a CMS signature with full chain embedded)
            List<string> TrustChainCertFilenames = new List<string>();
            TrustChainCertFilenames.Add("AppleWorldwideDeveloperRelationsCA.pem");
            TrustChainCertFilenames.Add("AppleRootCA.pem");
            InstallCertificates(TrustChainCertFilenames);

            // Find and load the signing cert
            SigningCert = LoadSigningCertificate();
            if (SigningCert == null)
            {
                // Failed to find a cert already installed or to install, cannot proceed any futher
                Program.Error("... Failed to find a certificate that matches the mobile provision to be used for code signing");
            }
            else
            {
                Program.Log("... Found matching certificate '{0}'", SigningCert.FriendlyName);
            }
        }
Exemple #25
0
        /// <summary>
        /// Extracts the dict values for the Entitlements key and creates a new full .plist file
        /// from them (with outer plist and dict keys as well as doctype, etc...)
        /// </summary>
        public string GetEntitlementsString(string CFBundleIdentifier, out string TeamIdentifier)
        {
            Utilities.PListHelper XCentPList = null;
            Data.ProcessValueForKey("Entitlements", "dict", delegate(XmlNode ValueNode)
            {
                XCentPList = Utilities.PListHelper.CloneDictionaryRootedAt(ValueNode);
            });

            // Modify the application-identifier to be fully qualified if needed
            string CurrentApplicationIdentifier;

            XCentPList.GetString("application-identifier", out CurrentApplicationIdentifier);
            XCentPList.GetString("com.apple.developer.team-identifier", out TeamIdentifier);

            //			if (CurrentApplicationIdentifier.Contains("*"))
            {
                // Replace the application identifier
                string NewApplicationIdentifier = String.Format("{0}.{1}", ApplicationIdentifierPrefix, CFBundleIdentifier);
                XCentPList.SetString("application-identifier", NewApplicationIdentifier);


                // Replace the keychain access groups
                // Note: This isn't robust, it ignores the existing value in the wildcard and uses the same value for
                // each entry.  If there is a legitimate need for more than one entry in the access group list, then
                // don't use a wildcard!
                List <string> KeyGroups = XCentPList.GetArray("keychain-access-groups", "string");

                for (int i = 0; i < KeyGroups.Count; ++i)
                {
                    string Entry = KeyGroups[i];
                    if (Entry.Contains("*"))
                    {
                        Entry = NewApplicationIdentifier;
                    }
                    KeyGroups[i] = Entry;
                }

                XCentPList.SetValueForKey("keychain-access-groups", KeyGroups);
            }

            // must have CloudKit and CloudDocuments for com.apple.developer.icloud-services
            // otherwise the game will not be listed in the Settings->iCloud apps menu on the device
            {
                // iOS only
                if (Platform == "IOS" && XCentPList.HasKey("com.apple.developer.icloud-services"))
                {
                    List <string> ServicesGroups = XCentPList.GetArray("com.apple.developer.icloud-services", "string");
                    ServicesGroups.Clear();

                    ServicesGroups.Add("CloudKit");
                    ServicesGroups.Add("CloudDocuments");
                    XCentPList.SetValueForKey("com.apple.developer.icloud-services", ServicesGroups);
                }

                // For distribution builds, the entitlements from mobileprovisioning have a modified syntax
                if (Config.bForDistribution)
                {
                    // remove the wildcards from the ubiquity-kvstore-identifier string
                    if (XCentPList.HasKey("com.apple.developer.ubiquity-kvstore-identifier"))
                    {
                        string UbiquityKvstoreString;
                        XCentPList.GetString("com.apple.developer.ubiquity-kvstore-identifier", out UbiquityKvstoreString);

                        int DotPosition = UbiquityKvstoreString.LastIndexOf("*");
                        if (DotPosition >= 0)
                        {
                            string TeamPrefix = DotPosition > 1 ? UbiquityKvstoreString.Substring(0, DotPosition - 1) : TeamIdentifier;
                            string NewUbiquityKvstoreIdentifier = String.Format("{0}.{1}", TeamPrefix, CFBundleIdentifier);
                            XCentPList.SetValueForKey("com.apple.developer.ubiquity-kvstore-identifier", NewUbiquityKvstoreIdentifier);
                        }
                    }

                    // remove the wildcards from the ubiquity-container-identifiers array
                    if (XCentPList.HasKey("com.apple.developer.ubiquity-container-identifiers"))
                    {
                        List <string> UbiquityContainerIdentifiersGroups = XCentPList.GetArray("com.apple.developer.ubiquity-container-identifiers", "string");

                        for (int i = 0; i < UbiquityContainerIdentifiersGroups.Count; i++)
                        {
                            int DotPosition = UbiquityContainerIdentifiersGroups[i].LastIndexOf("*");
                            if (DotPosition >= 0)
                            {
                                string TeamPrefix = DotPosition > 1 ? UbiquityContainerIdentifiersGroups[i].Substring(0, DotPosition - 1) : TeamIdentifier;
                                string NewUbiquityContainerIdentifier = String.Format("{0}.{1}", TeamPrefix, CFBundleIdentifier);
                                UbiquityContainerIdentifiersGroups[i] = NewUbiquityContainerIdentifier;
                            }
                        }

                        if (UbiquityContainerIdentifiersGroups.Count == 0)
                        {
                            string NewUbiquityKvstoreIdentifier = String.Format("{0}.{1}", TeamIdentifier, CFBundleIdentifier);
                            UbiquityContainerIdentifiersGroups.Add(NewUbiquityKvstoreIdentifier);
                        }

                        XCentPList.SetValueForKey("com.apple.developer.ubiquity-container-identifiers", UbiquityContainerIdentifiersGroups);
                    }

                    // remove the wildcards from the developer.associated-domains array or string
                    if (XCentPList.HasKey("com.apple.developer.associated-domains"))
                    {
                        string AssociatedDomainsString;
                        XCentPList.GetString("com.apple.developer.associated-domains", out AssociatedDomainsString);

                        //check if the value is string
                        if (AssociatedDomainsString != null && AssociatedDomainsString.Contains("*"))
                        {
                            XCentPList.RemoveKeyValue("com.apple.developer.associated-domains");
                        }
                        else
                        {
                            //check if the value is an array
                            List <string> AssociatedDomainsGroup = XCentPList.GetArray("com.apple.developer.associated-domains", "string");

                            if (AssociatedDomainsGroup.Count == 1 && AssociatedDomainsGroup[0].Contains("*"))
                            {
                                XCentPList.RemoveKeyValue("com.apple.developer.associated-domains");
                            }
                        }
                    }

                    // remove development keys - generated when the cloudkit container is in development mode
                    XCentPList.RemoveKeyValue("com.apple.developer.icloud-container-development-container-identifiers");
                }

                // set the icloud-container-environment according to the project settings
                if (XCentPList.HasKey("com.apple.developer.icloud-container-environment"))
                {
                    List <string> ContainerEnvironmentGroup = XCentPList.GetArray("com.apple.developer.icloud-container-environment", "string");

                    if (ContainerEnvironmentGroup.Count != 0)
                    {
                        ContainerEnvironmentGroup.Clear();

                        // The new value is a string, not an array
                        string NewContainerEnvironment = Config.bForDistribution ? "Production" : "Development";
                        XCentPList.SetValueForKey("com.apple.developer.icloud-container-environment", NewContainerEnvironment);
                    }
                }
            }

            return(XCentPList.SaveToString());
        }
Exemple #26
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)));
        }
Exemple #27
0
        /// <summary>
        /// Creates an entitlements blob string from the entitlements structure in the mobile provision, merging in an on disk file if it is present.
        /// </summary>
        private string BuildEntitlementString(string CFBundleIdentifier)
        {
            // Load the base entitlements string from the mobile provision
            string ProvisionEntitlements = Provision.GetEntitlementsString(CFBundleIdentifier);

            // See if there is an override entitlements file on disk
            string UserOverridesEntitlementsFilename = FileOperations.FindPrefixedFile(Config.BuildDirectory, Program.GameName + ".entitlements");
            if (File.Exists(UserOverridesEntitlementsFilename))
            {
                // Merge in the entitlements from the on disk file as overrides
                Program.Log("Merging override entitlements from {0} into provision specified entitlements", Path.GetFileName(UserOverridesEntitlementsFilename));

                Utilities.PListHelper Merger = new Utilities.PListHelper(ProvisionEntitlements);
                string Overrides = File.ReadAllText(UserOverridesEntitlementsFilename, Encoding.UTF8);
                Merger.MergePlistIn(Overrides);

                return Merger.SaveToString();
            }
            else
            {
                // The ones from the mobile provision need no overrides
                return ProvisionEntitlements;
            }
        }
        string PickIPAAndFetchBundleIdentifier()
        {
            string PickedFilename;
            if (ToolsHub.ShowOpenFileDialog(ToolsHub.IpaFilter, "Choose an IPA to provide the bundle identifier", "ipa", "", ref ToolsHub.ChoosingIpaDirectory, out PickedFilename))
            {
                FileOperations.FileSystemAdapter IPA = new FileOperations.ReadOnlyZipFileSystem(PickedFilename);

                if (IPA != null)
                {
                    byte[] InfoData = IPA.ReadAllBytes("Info.plist");
                    Utilities.PListHelper Info = new Utilities.PListHelper(Encoding.UTF8.GetString(InfoData));

                    string BundleID;
                    if (Info.GetString("CFBundleIdentifier", out BundleID))
                    {
                        return BundleID;
                    }
                }
            }

            return null;
        }
Exemple #29
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();
        }
		protected virtual byte[] CreateCodeResourcesDirectory(string CFBundleExecutable)
		{
			// Create a rules dict that includes (by wildcard) everything but Info.plist and the rules file
			Dictionary<string, object> Rules1 = new Dictionary<string, object>();
			Rules1.Add("^", true);
			Rules1.Add("^.*\\.lproj/", CreateOptionalResource(1000));
			Rules1.Add("^.*\\.lproj/locversion.plist$", CreateOmittedResource(1100));
			Rules1.Add("^version.plist$", true);

			Dictionary<string, object> Rules2 = new Dictionary<string, object>();
			Rules2.Add(".*\\.dSYM($|/)", CreateResource(11));
			Rules2.Add("^", CreateResource(20));
			Rules2.Add("^(.*/)?\\.DS_Store$", CreateOmittedResource(2000));
			Rules2.Add("^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/", CreateNestedResource(10));
			Rules2.Add("^.*", true);
			Rules2.Add("^.*\\.lproj/", CreateOptionalResource(1000));
			Rules2.Add("^.*\\.lproj/locversion.plist$", CreateOmittedResource(1100));
			Rules2.Add("^Info\\.plist$", CreateOmittedResource(20));
			Rules2.Add("^PkgInfo$", CreateOmittedResource(20));
			Rules2.Add("^[^/]+$", CreateNestedResource(10));
			Rules2.Add("^embedded\\.provisionprofile$", CreateResource(20));
			Rules2.Add("^version\\.plist$", CreateResource(20));

			// Create the full list of files to exclude (some files get excluded by 'magic' even though they aren't listed as special by rules)
			Dictionary<string, object> TrueExclusionList1 = new Dictionary<string, object>();
			TrueExclusionList1.Add(CFBundleExecutable, null);
			TrueExclusionList1.Add("CodeResources", null);
			TrueExclusionList1.Add("_CodeSignature/CodeResources", null);

			Dictionary<string, object> TrueExclusionList2 = new Dictionary<string, object>();
			TrueExclusionList2.Add("Info.plist", null);
			TrueExclusionList2.Add("PkgInfo", null);
			TrueExclusionList2.Add(CFBundleExecutable, null);
			TrueExclusionList2.Add("CodeResources", null);
			TrueExclusionList2.Add("_CodeSignature/CodeResources", null);

			// Hash each file
			IEnumerable<string> FileList = FileSystem.GetAllPayloadFiles();
			SHA1CryptoServiceProvider HashProvider = new SHA1CryptoServiceProvider();

			Utilities.PListHelper HashedFileEntries1 = new Utilities.PListHelper();
			Utilities.PListHelper HashedFileEntries2 = new Utilities.PListHelper();
			foreach (string Filename in FileList)
			{
				if (!TrueExclusionList1.ContainsKey(Filename))
				{
					byte[] FileData = FileSystem.ReadAllBytes(Filename);
					byte[] HashData = HashProvider.ComputeHash(FileData);

					HashedFileEntries1.AddKeyValuePair(Filename, HashData);
				}
				if (!TrueExclusionList2.ContainsKey(Filename))
				{
					byte[] FileData = FileSystem.ReadAllBytes(Filename);
					byte[] HashData = HashProvider.ComputeHash(FileData);

					HashedFileEntries2.AddKeyValuePair(Filename, HashData);
				}
			}

			// Create the CodeResources file that will contain the hashes
			Utilities.PListHelper CodeResources = new Utilities.PListHelper();
			CodeResources.AddKeyValuePair("files", HashedFileEntries1);
			CodeResources.AddKeyValuePair("files2", HashedFileEntries2);
			CodeResources.AddKeyValuePair("rules", Rules1);
			CodeResources.AddKeyValuePair("rules2", Rules2);

			// Write the CodeResources file out
			string CodeResourcesAsString = CodeResources.SaveToString();
			byte[] CodeResourcesAsBytes = Encoding.UTF8.GetBytes(CodeResourcesAsString);
			FileSystem.WriteAllBytes("_CodeSignature/CodeResources", CodeResourcesAsBytes);

			return CodeResourcesAsBytes;
		}