/// <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, out string TeamIdentifier) { // Load the base entitlements string from the mobile provision string ProvisionEntitlements = Provision.GetEntitlementsString(CFBundleIdentifier, out TeamIdentifier); // 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); } }
/** * 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))); }
/** * 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))); }
/// <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; } }