mirror of
https://github.com/blacktop/ipsw.git
synced 2026-05-08 12:22:26 +00:00
feat: add --sim flag to download XCode simulators via ipsw dl ota cmd #700
This commit is contained in:
@@ -65,6 +65,7 @@ func init() {
|
||||
otaDLCmd.Flags().Bool("latest", false, "Download latest OTAs")
|
||||
otaDLCmd.Flags().Bool("delta", false, "Download Delta OTAs")
|
||||
otaDLCmd.Flags().Bool("rsr", false, "Download Rapid Security Response OTAs")
|
||||
otaDLCmd.Flags().Bool("sim", false, "Download Simulator OTAs")
|
||||
otaDLCmd.Flags().BoolP("kernel", "k", false, "Extract kernelcache from remote OTA zip")
|
||||
otaDLCmd.Flags().Bool("dyld", false, "Extract dyld_shared_cache(s) from remote OTA zip")
|
||||
otaDLCmd.Flags().BoolP("urls", "u", false, "Dump URLs only")
|
||||
@@ -87,6 +88,7 @@ func init() {
|
||||
viper.BindPFlag("download.ota.latest", otaDLCmd.Flags().Lookup("latest"))
|
||||
viper.BindPFlag("download.ota.delta", otaDLCmd.Flags().Lookup("delta"))
|
||||
viper.BindPFlag("download.ota.rsr", otaDLCmd.Flags().Lookup("rsr"))
|
||||
viper.BindPFlag("download.ota.sim", otaDLCmd.Flags().Lookup("sim"))
|
||||
viper.BindPFlag("download.ota.dyld", otaDLCmd.Flags().Lookup("dyld"))
|
||||
viper.BindPFlag("download.ota.urls", otaDLCmd.Flags().Lookup("urls"))
|
||||
viper.BindPFlag("download.ota.json", otaDLCmd.Flags().Lookup("json"))
|
||||
@@ -159,6 +161,7 @@ var otaDLCmd = &cobra.Command{
|
||||
getBeta := viper.GetBool("download.ota.beta")
|
||||
getLatest := viper.GetBool("download.ota.latest")
|
||||
getRSR := viper.GetBool("download.ota.rsr")
|
||||
getSim := viper.GetBool("download.ota.sim")
|
||||
remoteDyld := viper.GetBool("download.ota.dyld")
|
||||
dyldArches := viper.GetStringSlice("download.ota.dyld-arch")
|
||||
dyldDriverKit := viper.GetBool("download.ota.driver-kit")
|
||||
@@ -269,6 +272,7 @@ var otaDLCmd = &cobra.Command{
|
||||
Latest: getLatest,
|
||||
Delta: viper.GetBool("download.ota.delta"),
|
||||
RSR: getRSR,
|
||||
Simulator: getSim,
|
||||
Device: device,
|
||||
Model: model,
|
||||
Version: ver,
|
||||
@@ -322,7 +326,7 @@ var otaDLCmd = &cobra.Command{
|
||||
for _, o := range otas {
|
||||
utils.Indent(log.WithFields(log.Fields{
|
||||
"name": o.DocumentationID,
|
||||
"version": o.OSVersion,
|
||||
"version": or([]string{o.OSVersion, o.SimulatorVersion}),
|
||||
"build": o.Build,
|
||||
"device_count": len(o.SupportedDevices),
|
||||
"model_count": len(o.SupportedDeviceModels),
|
||||
@@ -357,7 +361,7 @@ var otaDLCmd = &cobra.Command{
|
||||
"devices": fmt.Sprintf("%s... (count=%d)", strings.Join(o.SupportedDevices, " "), len(o.SupportedDevices)),
|
||||
"model": strings.Join(o.SupportedDeviceModels, " "),
|
||||
}
|
||||
if o.IsEncrypted {
|
||||
if o.IsEncrypted || len(o.ArchiveDecryptionKey) > 0 {
|
||||
fields["encrypted"] = true
|
||||
fields["key"] = o.ArchiveDecryptionKey
|
||||
}
|
||||
@@ -422,6 +426,9 @@ var otaDLCmd = &cobra.Command{
|
||||
downloader := download.NewDownload(proxy, insecure, skipAll, resumeAll, restartAll, false, viper.GetBool("verbose"))
|
||||
for _, o := range otas {
|
||||
folder := filepath.Join(destPath, fmt.Sprintf("%s%s_OTAs", o.ProductSystemName, strings.TrimPrefix(o.OSVersion, "9.9.")))
|
||||
if getSim {
|
||||
folder = filepath.Join(destPath, fmt.Sprintf("%s_%s_Simulator_OTAs", strings.ToUpper(platform), o.SimulatorVersion))
|
||||
}
|
||||
os.MkdirAll(folder, 0750)
|
||||
var devices string
|
||||
if len(o.SupportedDevices) > 0 {
|
||||
@@ -445,21 +452,24 @@ var otaDLCmd = &cobra.Command{
|
||||
isRSR = fmt.Sprintf("%s_%s_%s_RSR_", o.OSVersion, o.ProductVersionExtra, o.Build)
|
||||
}
|
||||
var isAEA string
|
||||
if o.IsEncrypted {
|
||||
if o.IsEncrypted || len(o.ArchiveDecryptionKey) > 0 {
|
||||
filesafe := o.ArchiveDecryptionKey
|
||||
filesafe = strings.ReplaceAll(filesafe, "/", "_")
|
||||
filesafe = strings.ReplaceAll(filesafe, "+", "-")
|
||||
isAEA = "KEY_[" + filesafe + "]_"
|
||||
}
|
||||
destName := filepath.Join(folder, fmt.Sprintf("%s_%s%s%s", devices, isRSR, isAEA, getDestName(url, removeCommas)))
|
||||
if getSim {
|
||||
destName = filepath.Join(folder, fmt.Sprintf("simulator_%s%s", isAEA, getDestName(url, removeCommas)))
|
||||
}
|
||||
if _, err := os.Stat(destName); os.IsNotExist(err) {
|
||||
fields := log.Fields{
|
||||
"device": strings.Join(o.SupportedDevices, " "),
|
||||
"model": strings.Join(o.SupportedDeviceModels, " "),
|
||||
"build": o.Build,
|
||||
"type": o.DocumentationID,
|
||||
"type": or([]string{o.DocumentationID, "simulator"}),
|
||||
}
|
||||
if o.IsEncrypted {
|
||||
if o.IsEncrypted || len(o.ArchiveDecryptionKey) > 0 {
|
||||
fields["encrypted"] = true
|
||||
fields["key"] = o.ArchiveDecryptionKey
|
||||
}
|
||||
@@ -482,3 +492,12 @@ var otaDLCmd = &cobra.Command{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func or(values []string) string {
|
||||
for _, v := range values {
|
||||
if len(v) > 0 {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ type OtaConf struct {
|
||||
Latest bool
|
||||
Delta bool
|
||||
RSR bool
|
||||
Simulator bool
|
||||
Device string
|
||||
Model string
|
||||
Version *version.Version
|
||||
@@ -110,6 +111,7 @@ type pallasRequest struct {
|
||||
BuildVersion string `json:"BuildVersion"`
|
||||
Build string `json:"Build,omitempty"`
|
||||
RequestedProductVersion string `json:"RequestedProductVersion,omitempty"`
|
||||
RequestedBuild string `json:"RequestedBuild,omitempty"`
|
||||
Supervised bool `json:"Supervised,omitempty"`
|
||||
DelayRequested bool `json:"DelayRequested,omitempty"`
|
||||
CompatibilityVersion int `json:"CompatibilityVersion,omitempty"`
|
||||
@@ -305,6 +307,20 @@ func (o *Ota) getRequestAssetTypes() ([]assetType, error) {
|
||||
if o.Config.RSR {
|
||||
return []assetType{rsrUpdate}, nil
|
||||
}
|
||||
if o.Config.Simulator {
|
||||
switch o.Config.Platform {
|
||||
case "ios":
|
||||
return []assetType{iOsSimulatorUpdate}, nil
|
||||
case "watchos":
|
||||
return []assetType{watchOsSimulatorUpdate}, nil
|
||||
case "tvos":
|
||||
return []assetType{tvOsSimulatorUpdate}, nil
|
||||
case "visionos":
|
||||
return []assetType{visionOaSimulatorUpdate}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported simulator platform %s", o.Config.Platform)
|
||||
}
|
||||
}
|
||||
if o.Config.Platform == "ios" {
|
||||
return []assetType{softwareUpdate}, nil
|
||||
}
|
||||
@@ -381,6 +397,9 @@ func (o *Ota) getRequestAudienceIDs() ([]string, error) {
|
||||
}, nil
|
||||
}
|
||||
default:
|
||||
if o.Config.Simulator {
|
||||
return []string{assetAudienceDB["macos"].Generic}, nil
|
||||
}
|
||||
if o.Config.Version != nil {
|
||||
segs := o.Config.Version.Segments()
|
||||
if len(segs) == 0 {
|
||||
@@ -464,6 +483,10 @@ func (o *Ota) getRequests(atype assetType, audienceID string) (reqs []pallasRequ
|
||||
req.Build = o.Config.Build
|
||||
}
|
||||
|
||||
if o.Config.Simulator {
|
||||
req.RequestedBuild = o.Config.Build
|
||||
}
|
||||
|
||||
if len(o.Config.Device) > 0 && len(o.Config.Model) == 0 {
|
||||
dev, err := o.db.LookupDevice(o.Config.Device)
|
||||
if err != nil {
|
||||
@@ -561,6 +584,21 @@ func (o *Ota) GetPallasOTAs() ([]types.Asset, error) {
|
||||
|
||||
oassets := o.QueryPublicXML()
|
||||
|
||||
if o.Config.Simulator {
|
||||
if o.Config.Version.Original() == "0" && o.Config.Build == "0" {
|
||||
return nil, fmt.Errorf("you must supply: --build, --version or --latest WITH --sim")
|
||||
} else if o.Config.Version.Original() != "0" && o.Config.Build == "0" {
|
||||
dvt, err := GetDVTDownloadableIndex()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get simulators index: %v", err)
|
||||
}
|
||||
o.Config.Build, err = dvt.LookupBuild(o.Config.Version.Original(), o.Config.Platform)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to lookup simulator build: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pallasReqs, err := o.buildPallasRequests()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build the pallas requests: %v", err)
|
||||
@@ -618,6 +656,8 @@ func (o *Ota) GetPallasOTAs() ([]types.Asset, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// os.WriteFile("pallas.json", b64data, 0644)
|
||||
|
||||
res := ota{}
|
||||
if err := json.Unmarshal(b64data, &res); err != nil {
|
||||
log.Errorf("failed to unmarshall JSON: %v", err)
|
||||
@@ -699,6 +739,16 @@ func (o *Ota) filterOTADevices(otas []types.Asset) []types.Asset { // FIXME: thi
|
||||
var filteredDevices []string
|
||||
var filteredOtas []types.Asset
|
||||
|
||||
if o.Config.Simulator {
|
||||
for _, ota := range otas {
|
||||
switch assetType(ota.AssetType) {
|
||||
case iOsSimulatorUpdate, watchOsSimulatorUpdate, tvOsSimulatorUpdate, visionOaSimulatorUpdate:
|
||||
filteredOtas = append(filteredOtas, ota)
|
||||
}
|
||||
}
|
||||
return filteredOtas
|
||||
}
|
||||
|
||||
if o.Config.Platform == "macos" {
|
||||
if o.Config.Build != "0" && !o.Config.RSR {
|
||||
for _, ota := range otas {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/blacktop/go-plist"
|
||||
"github.com/blacktop/ipsw/internal/utils"
|
||||
)
|
||||
@@ -24,6 +25,14 @@ const (
|
||||
xcodeReleasesAPI = "https://xcodereleases.com/data.json"
|
||||
)
|
||||
|
||||
var platforms = map[string]string{
|
||||
"macos": "com.apple.platform.macosx",
|
||||
"ios": "com.apple.platform.iphoneos",
|
||||
"tvos": "com.apple.platform.appletvos",
|
||||
"watchos": "com.apple.platform.watchos",
|
||||
"visionos": "com.apple.platform.xros",
|
||||
}
|
||||
|
||||
type Downloadable struct {
|
||||
Authentication string `plist:"authentication,omitempty"`
|
||||
Category string `plist:"category,omitempty"`
|
||||
@@ -88,6 +97,20 @@ func GetDVTDownloadableIndex() (*DVTDownloadable, error) {
|
||||
return &dvt, nil
|
||||
}
|
||||
|
||||
func (d *DVTDownloadable) LookupBuild(version, platform string) (string, error) {
|
||||
platform, ok := platforms[platform]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("platform not supported: %s", platform)
|
||||
}
|
||||
for _, dl := range d.Downloadables {
|
||||
if dl.SimulatorVersion.Version == version && dl.Platform == platform {
|
||||
log.WithField("name", dl.Name).Debug("Simulator")
|
||||
return dl.SimulatorVersion.BuildUpdate, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("build not found for: %s", version)
|
||||
}
|
||||
|
||||
type Contents struct {
|
||||
Key string
|
||||
Generation int64
|
||||
|
||||
@@ -46,6 +46,7 @@ type Asset struct {
|
||||
AssetType string `json:"AssetType" plist:"AssetType,omitempty"`
|
||||
BridgeVersionInfo bridgeVersionInfo `json:"BridgeVersionInfo" plist:"BridgeVersionInfo,omitempty"`
|
||||
Build string `json:"Build" plist:"Build,omitempty"`
|
||||
SimulatorVersion string `json:"SimulatorVersion" plist:"SimulatorVersion,omitempty"`
|
||||
DataTemplateSize int `json:"DataTemplateSize" plist:"DataTemplateSize,omitempty"`
|
||||
EAPFSEnabled bool `json:"EAPFSEnabled,omitempty" plist:"EAPFSEnabled,omitempty"`
|
||||
InstallationSize string `json:"InstallationSize" plist:"InstallationSize,omitempty"`
|
||||
@@ -83,6 +84,7 @@ type Asset struct {
|
||||
IsZipStreamable bool `json:"_IsZipStreamable" plist:"_IsZipStreamable,omitempty"`
|
||||
MasteredVersion string `json:"_MasteredVersion" plist:"_MasteredVersion,omitempty"`
|
||||
Hash []byte `json:"_Measurement" plist:"_Measurement,omitempty"`
|
||||
Sha256Hash []byte `json:"_Measurement-SHA256" plist:"_Measurement-SHA256,omitempty"`
|
||||
HashAlgorithm string `json:"_MeasurementAlgorithm" plist:"_MeasurementAlgorithm,omitempty"`
|
||||
UnarchivedSize int `json:"_UnarchivedSize" plist:"_UnarchivedSize,omitempty"`
|
||||
AssetDefaultGarbageCollectionBehavior string `json:"__AssetDefaultGarbageCollectionBehavior" plist:"__AssetDefaultGarbageCollectionBehavior,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user