diff --git a/cmd/ipadecrypt/decrypt.go b/cmd/ipadecrypt/decrypt.go index fcf2b27c6..05d5397e4 100644 --- a/cmd/ipadecrypt/decrypt.go +++ b/cmd/ipadecrypt/decrypt.go @@ -423,7 +423,7 @@ func decryptHandler(cmd *cobra.Command, args []string) { live.OK("%s", progress.Summary()) - outLocal, err := localOutputPath(appBundleID, appVersion) + outLocal, err := localOutputPath(decryptOutput, appBundleID, appVersion) if err != nil { tui.Err("output path: %v", err) return @@ -703,13 +703,28 @@ func remoteOutputPath(bundleID, version string) string { ) } -func localOutputPath(bundleID, version string) (string, error) { - cwd, err := os.Getwd() +func localOutputPath(override, bundleID, version string) (string, error) { + defaultName := fmt.Sprintf("%s_%s.decrypted.ipa", bundleID, version) + + if override == "" { + cwd, err := os.Getwd() + if err != nil { + return "", err + } + return filepath.Join(cwd, defaultName), nil + } + + abs, err := filepath.Abs(override) if err != nil { return "", err } - return filepath.Join(cwd, fmt.Sprintf("%s_%s.decrypted.ipa", bundleID, version)), nil + // if override is just a directory (not a full file path), place the default filename inside it + if info, serr := os.Stat(abs); serr == nil && info.IsDir() { + return filepath.Join(abs, defaultName), nil + } + + return abs, nil } func cleanupDecrypt(dev *device.Client, noCleanup bool, stagingRemote, outRemote string) { diff --git a/cmd/ipadecrypt/main.go b/cmd/ipadecrypt/main.go index c84d13f57..189b5175f 100644 --- a/cmd/ipadecrypt/main.go +++ b/cmd/ipadecrypt/main.go @@ -15,6 +15,7 @@ var ( bootstrapReset bool decryptExtVerID string + decryptOutput string decryptNoCleanup bool decryptKeepMetadata bool decryptNoVerify bool @@ -47,6 +48,7 @@ func main() { Run: decryptHandler, } decrypt.Flags().StringVar(&decryptExtVerID, "external-version-id", "", "pin to a specific historical App Store version") + decrypt.Flags().StringVarP(&decryptOutput, "output", "o", "", "output path for the decrypted IPA (default: ./_.decrypted.ipa)") decrypt.Flags().BoolVar(&decryptNoCleanup, "no-cleanup", false, "leave remote staging files in place") decrypt.Flags().BoolVar(&decryptKeepMetadata, "keep-metadata", false, "keep iTunesMetadata.plist (Apple ID + purchase info) in the output IPA") decrypt.Flags().BoolVar(&decryptNoVerify, "no-verify", false, "skip the post-decrypt cryptid==0 check on every Mach-O")