static private void CopyEngineOrGameFiles(string Subdirectory, string Filter) { // build the matching paths string EngineDir = Path.Combine(Config.EngineBuildDirectory + Subdirectory); string GameDir = Path.Combine(Config.BuildDirectory + Subdirectory); // find the files in the engine directory string[] EngineFiles = Directory.GetFiles(EngineDir, Filter); if (Directory.Exists(GameDir)) { string[] GameFiles = Directory.GetFiles(GameDir, Filter); // copy all files from game foreach (string GameFilename in GameFiles) { string DestFilename = Path.Combine(Config.PayloadDirectory, Path.GetFileName(GameFilename)); // copy the game verison instead of engine version FileOperations.CopyRequiredFile(GameFilename, DestFilename); } } // look for matching engine files that weren't in game foreach (string EngineFilename in EngineFiles) { string GameFilename = Path.Combine(GameDir, Path.GetFileName(EngineFilename)); string DestFilename = Path.Combine(Config.PayloadDirectory, Path.GetFileName(EngineFilename)); if (!File.Exists(GameFilename)) { // copy the game verison instead of engine version FileOperations.CopyRequiredFile(EngineFilename, DestFilename); } } }
public static void TryInstallingMobileProvision(string ProvisionFilename, bool ShowPrompt = true) { if (!String.IsNullOrEmpty(ProvisionFilename) || ShowOpenFileDialog(MobileProvisionFilter, "Choose a mobile provision to install", "mobileprovision", "", ref ChoosingFilesToInstallDirectory, out ProvisionFilename)) { try { // Determine if this is a development or distribution certificate bool bIsDistribution = false; MobileProvision Provision = MobileProvisionParser.ParseFile(ProvisionFilename); bIsDistribution = IsProfileForDistribution(Provision); // use the input filename if the GameName is empty string DestName = string.IsNullOrEmpty(Program.GameName) ? Path.GetFileNameWithoutExtension(ProvisionFilename) : Program.GameName; // Copy the file into the destination location string EffectivePrefix = bIsDistribution ? "Distro_" : Config.SigningPrefix; string DestinationFilename = Path.Combine(Config.ProvisionDirectory, EffectivePrefix + DestName + ".mobileprovision"); DestinationFilename = DestinationFilename.Replace("\\", "/"); if (File.Exists(DestinationFilename)) { MobileProvision OldProvision = MobileProvisionParser.ParseFile(DestinationFilename); string MessagePrompt = String.Format( "{0} already contains a {1} mobile provision file. Do you want to replace the provision '{2}' with '{3}'?", Config.BuildDirectory, bIsDistribution ? "distribution" : "development", OldProvision.ProvisionName, Provision.ProvisionName); if (ShowPrompt && System.Windows.Forms.MessageBox.Show(MessagePrompt, Config.AppDisplayName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { return; } if (DestinationFilename != ProvisionFilename) { FileOperations.DeleteFile(DestinationFilename); } } if (DestinationFilename != ProvisionFilename) { FileOperations.CopyRequiredFile(ProvisionFilename, DestinationFilename); } } catch (Exception ex) { ShowError(String.Format("Encountered an error '{0} while trying to install a mobile provision", ex.Message)); } } }
/// <summary> /// Creates a copy of a source IPA to a working path and opens it up as a Zip for further modifications /// </summary> static private ZipFile CreateWorkingIPA(string SourceIPAPath, string WorkIPAPath) { FileInfo ReferenceInfo = new FileInfo(SourceIPAPath); if (!ReferenceInfo.Exists) { Program.Error(String.Format("Failed to find stub IPA '{0}'", SourceIPAPath)); return(null); } else { Program.Log(String.Format("Loaded stub IPA from '{0}' ...", SourceIPAPath)); } if (Program.GameName == "UE4Game") { WorkIPAPath = Config.RemapIPAPath(".ipa"); } // Make sure there are no stale working copies around FileOperations.DeleteFile(WorkIPAPath); // Create a working copy of the IPA FileOperations.CopyRequiredFile(SourceIPAPath, WorkIPAPath); // Open up the zip file ZipFile Stub = ZipFile.Read(WorkIPAPath); // Do a few quick spot checks to catch problems that may have occurred earlier bool bHasCodeSignature = Stub[Config.AppDirectoryInZIP + "/_CodeSignature/CodeResources"] != null; bool bHasMobileProvision = Stub[Config.AppDirectoryInZIP + "/embedded.mobileprovision"] != null; if (!bHasCodeSignature || !bHasMobileProvision) { Program.Error("Stub IPA does not appear to be signed correctly (missing mobileprovision or CodeResources)"); Program.ReturnCode = (int)ErrorCodes.Error_StubNotSignedCorrectly; } // Set encoding to support unicode filenames Stub.AlternateEncodingUsage = ZipOption.Always; Stub.AlternateEncoding = Encoding.UTF8; return(Stub); }
private void ResignButton_Click(object sender, EventArgs e) { saveFileDialog1.DefaultExt = "ipa"; saveFileDialog1.FileName = ""; saveFileDialog1.Filter = ToolsHub.IpaFilter; saveFileDialog1.Title = "Choose a filename for the re-signed IPA"; string CWD = Directory.GetCurrentDirectory(); bool bDialogSucceeded = (saveFileDialog1.ShowDialog() == DialogResult.OK); Directory.SetCurrentDirectory(CWD); if (bDialogSucceeded) { bool bSavedVerbose = Config.bVerbose; Config.bVerbose = true; Program.ProgressDialog.OnBeginBackgroundWork = delegate { string SrcFilename = IPAFilenameEdit.Text; string DestFilename = saveFileDialog1.FileName; // Delete the target location and copy the source there FileOperations.DeleteFile(DestFilename); FileOperations.CopyRequiredFile(SrcFilename, DestFilename); // Open the file FileOperations.ZipFileSystem FileSystem = new FileOperations.ZipFileSystem(DestFilename); FileSystem.SetCompression(CBCompressModifiedFiles.Checked ? Ionic.Zlib.CompressionLevel.BestCompression : Ionic.Zlib.CompressionLevel.None); // Resign and save the file ResignIPA(FileSystem); FileSystem.Close(); }; Config.bVerbose = bSavedVerbose; Program.ProgressDialog.ShowDialog(); } }
/// <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(); }
/// <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(); }