Skip to content

Commit d705b40

Browse files
committed
Always specify BuildManifest in tsschecker arguments: greatly improves blob saving speed & performance (no more freezing)
1 parent 9988303 commit d705b40

File tree

5 files changed

+64
-63
lines changed

5 files changed

+64
-63
lines changed
0 Bytes
Binary file not shown.

dist/windows/lib/jnidispatch.dll

-512 Bytes
Binary file not shown.

src/main/java/com/airsquared/blobsaver/Background.java

+8-7
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@
3737
import java.awt.event.ActionListener;
3838
import java.io.File;
3939
import java.io.IOException;
40+
import java.net.URL;
4041
import java.util.ArrayList;
4142
import java.util.Collections;
4243
import java.util.List;
44+
import java.util.Map;
4345
import java.util.concurrent.Executors;
4446
import java.util.concurrent.ScheduledExecutorService;
4547
import java.util.concurrent.TimeUnit;
@@ -179,9 +181,9 @@ private static void saveBackgroundBlobs(int preset) {
179181
identifier = textToIdentifier(presetPrefs.get("Device Model", ""));
180182
}
181183
log("identifier:" + identifier);
182-
List<String> signedVersions;
184+
List<Map<String, Object>> signedFirmwares;
183185
try {
184-
signedVersions = getAllSignedVersions(identifier);
186+
signedFirmwares = getAllSignedFirmwares(identifier);
185187
} catch (IOException e) {
186188
Notification notification = new Notification("Saving blobs failed", "Check your internet connection.\nIf it is working, click here to report this error.", Notification.ERROR_ICON);
187189
Notification.Notifier.INSTANCE.setPopupLifetime(Duration.minutes(1));
@@ -199,20 +201,19 @@ private static void saveBackgroundBlobs(int preset) {
199201
Notification.Notifier.INSTANCE.notify(notification);
200202
return;
201203
}
202-
log("signed versions:" + signedVersions);
203204
String ecid = presetPrefs.get("ECID", "");
204205
String path = presetPrefs.get("Path", "");
205206
String boardConfig = presetPrefs.get("Board Config", "");
206207
String apnonce = presetPrefs.get("Apnonce", "");
207208
File tsschecker = getTsschecker();
208-
for (String version : signedVersions) {
209+
for (Map<String, Object> firmware : signedFirmwares) {
209210
//noinspection ResultOfMethodCallIgnored
210211
new File(path).mkdirs();
211212
String tsscheckerLog;
212213
try {
213214
ArrayList<String> args = new ArrayList<>();
214215
Collections.addAll(args, tsschecker.getPath(), "--generator", "0x1111111111111111", "--nocache", "-d", identifier, "-s", "-e", ecid,
215-
"--save-path", path, "-i", version);
216+
"--save-path", path, "-m", extractBuildManifest(new URL(firmware.get("url").toString())).toString());
216217
if (!"none".equals(boardConfig) && !"".equals(boardConfig)) { // needs board config
217218
Collections.addAll(args, "--boardconfig", boardConfig);
218219
}
@@ -244,7 +245,7 @@ private static void saveBackgroundBlobs(int preset) {
244245
presetName = appPrefs.get("Name Preset" + preset, "");
245246
}
246247
if (containsIgnoreCase(tsscheckerLog, "Saved")) {
247-
Notification notification = new Notification("Successfully saved blobs for", "iOS " + version + " (" + presetName + ") in\n" + path, Notification.SUCCESS_ICON);
248+
Notification notification = new Notification("Successfully saved blobs for", "iOS " + firmware.get("version").toString() + " (" + presetName + ") in\n" + path, Notification.SUCCESS_ICON);
248249
Notification.Notifier.INSTANCE.setPopupLifetime(Duration.seconds(30));
249250
Notification.Notifier.INSTANCE.setOnNotificationPressed((event) -> {
250251
Notification.Notifier.INSTANCE.stop();
@@ -275,7 +276,7 @@ private static void saveBackgroundBlobs(int preset) {
275276
reportError(alert, tsscheckerLog);
276277
});
277278
Notification.Notifier.INSTANCE.notify(notification);
278-
} else if (containsIgnoreCase(tsscheckerLog, "iOS " + version + " for device " + identifier + " IS NOT being signed")) {
279+
} else if (containsIgnoreCase(tsscheckerLog, " IS NOT being signed")) {
279280
continue;
280281
} else {
281282
Notification notification = new Notification("Saving blobs failed", "An unknown error occurred. Click here to report this error.", Notification.ERROR_ICON);

src/main/java/com/airsquared/blobsaver/Shared.java

+28-4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
import java.util.Map;
4848
import java.util.concurrent.CountDownLatch;
4949
import java.util.stream.Collectors;
50+
import java.util.zip.ZipEntry;
51+
import java.util.zip.ZipInputStream;
5052

5153
import static com.airsquared.blobsaver.Main.appPrefs;
5254
import static com.airsquared.blobsaver.Main.appVersion;
@@ -136,7 +138,7 @@ protected Void call() throws Exception {
136138
service.start();
137139
}
138140

139-
static String makeRequest(URL url) throws IOException {
141+
private static String makeRequest(URL url) throws IOException {
140142
URLConnection urlConnection = url.openConnection();
141143
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
142144
String inputLine;
@@ -173,7 +175,7 @@ static File getTsschecker() {
173175
return tsschecker;
174176
}
175177

176-
static void copyStreamToFile(InputStream inputStream, File file) throws IOException {
178+
private static void copyStreamToFile(InputStream inputStream, File file) throws IOException {
177179
OutputStream out = new FileOutputStream(file);
178180
int read;
179181
byte[] bytes = new byte[1024];
@@ -263,11 +265,33 @@ static void runSafe(Runnable runnable) {
263265
}
264266
}
265267

266-
static List<String> getAllSignedVersions(String deviceIdentifier) throws IOException {
268+
static List<Map<String, Object>> getFirmwareList(String deviceIdentifier) throws IOException {
267269
String response = makeRequest(new URL("https://api.ipsw.me/v4/device/" + deviceIdentifier));
268270
JSONArray firmwareListJson = new JSONObject(response).getJSONArray("firmwares");
269271
@SuppressWarnings("unchecked") List<Map<String, Object>> firmwareList = (List) firmwareListJson.toList();
270-
return firmwareList.stream().filter(map -> Boolean.TRUE.equals(map.get("signed"))).map(map -> map.get("version").toString()).collect(Collectors.toList());
272+
return firmwareList;
273+
}
274+
275+
static List<Map<String, Object>> getAllSignedFirmwares(String deviceIdentifier) throws IOException {
276+
return getFirmwareList(deviceIdentifier).stream().filter(map -> Boolean.TRUE.equals(map.get("signed"))).collect(Collectors.toList());
277+
}
278+
279+
// static List<String> getAllSignedVersions(String deviceIdentifier) throws IOException {
280+
// return getAllSignedFirmwares(deviceIdentifier).map(map -> map.get("version").toString()).collect(Collectors.toList());
281+
// }
282+
283+
static File extractBuildManifest(URL ipswURL) throws IOException {
284+
File buildManifestPlist = File.createTempFile("BuildManifest", ".plist");
285+
ZipInputStream zin = new ZipInputStream(ipswURL.openStream());
286+
ZipEntry ze;
287+
while ((ze = zin.getNextEntry()) != null) {
288+
if ("BuildManifest.plist".equals(ze.getName())) {
289+
copyStreamToFile(zin, buildManifestPlist);
290+
break;
291+
}
292+
}
293+
buildManifestPlist.deleteOnExit();
294+
return buildManifestPlist;
271295
}
272296

273297
// temporary until ProGuard is implemented

src/main/java/com/airsquared/blobsaver/TSSChecker.java

+28-52
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@
2424

2525
import java.io.File;
2626
import java.io.IOException;
27+
import java.net.MalformedURLException;
2728
import java.net.URL;
2829
import java.util.ArrayList;
2930
import java.util.Arrays;
3031
import java.util.Collections;
3132
import java.util.List;
32-
import java.util.zip.ZipEntry;
33-
import java.util.zip.ZipInputStream;
33+
import java.util.Map;
34+
import java.util.stream.Collectors;
3435

3536
import static com.airsquared.blobsaver.Shared.*;
3637

@@ -43,9 +44,10 @@ class TSSChecker {
4344
static void run(String device) {
4445
Controller controller = Controller.INSTANCE;
4546
if (controller.versionCheckBox.isSelected()) {
46-
List<String> signedVersions;
47+
List<Map<String, Object>> allSignedFirmwares; // really is List<Map<String, String>>
48+
ArrayList<String> signedVersionsStringList = new ArrayList<>();
4749
try {
48-
signedVersions = getAllSignedVersions(device);
50+
allSignedFirmwares = getAllSignedFirmwares(device);
4951
} catch (IOException e) {
5052
Alert alert = new Alert(Alert.AlertType.ERROR,
5153
"Saving blobs failed. Check your internet connection.\n\nIf your internet is working and you can connect to the website ipsw.me in your browser, please create a new issue on Github or PM me on Reddit. The log has been copied to your clipboard.",
@@ -57,29 +59,34 @@ static void run(String device) {
5759
}
5860
try {
5961
// can't use the `forEach()` method because exception won't be caught properly
60-
for (String version : signedVersions) {
61-
run(device, version);
62+
for (Map<String, Object> firmware : allSignedFirmwares) { // really is List<Map<String, String>>
63+
signedVersionsStringList.add(firmware.get("version").toString());
64+
run(new URL(firmware.get("url").toString()), device, firmware.get("version").toString());
6265
}
63-
} catch (TSSCheckerException e) { // exception is only needed so lambda doesn't keep on going after an error
66+
} catch (TSSCheckerException | MalformedURLException e) { // exception is only needed so lambda doesn't keep on going after an error
6467
return; // the error alert should already be shown
6568
}
66-
String signedVersionsString = signedVersions.toString().substring(1, signedVersions.toString().length() - 1);
69+
String signedVersionsString = signedVersionsStringList.toString().substring(1, signedVersionsStringList.toString().length() - 1);
6770
Alert alert = new Alert(Alert.AlertType.INFORMATION,
6871
"Successfully saved blobs in\n" + controller.pathField.getText() + "\n\nFor "
69-
+ (signedVersions.size() == 1 ? "version " : "versions ") + signedVersionsString, ButtonType.OK);
72+
+ (signedVersionsStringList.size() == 1 ? "version " : "versions ") + signedVersionsString, ButtonType.OK);
7073
alert.setHeaderText("Success!");
7174
alert.showAndWait();
7275
} else {
7376
try {
74-
run(device, controller.versionField.getText());
77+
Map<String, Object> firmware = getFirmwareList(device).stream().filter(stringObjectMap ->
78+
controller.versionField.getText().equals(stringObjectMap.get("version"))).collect(Collectors.toList()).get(0);
79+
run(new URL(firmware.get("url").toString()), device, controller.versionField.getText());
7580
} catch (TSSCheckerException e) {
7681
// the error alert should already be shown
82+
} catch (IOException e) {
83+
e.printStackTrace();
7784
}
7885

7986
}
8087
}
8188

82-
private static void run(String device, String version) throws TSSCheckerException {
89+
private static void run(URL ipswURL, String device, String version) throws TSSCheckerException {
8390
if ("".equals(device)) {
8491
return;
8592
}
@@ -89,12 +96,10 @@ private static void run(String device, String version) throws TSSCheckerExceptio
8996
String savePath = controller.pathField.getText();
9097
String boardConfig = controller.boardConfigField.getText();
9198
String apnonce = controller.apnonceField.getText();
92-
String ipswURL = controller.ipswField.getText();
9399

94100
Effect errorBorder = Controller.errorBorder;
95101

96102
File tsschecker = getTsschecker();
97-
File buildManifestPlist = null;
98103

99104
File locationToSaveBlobs = new File(controller.pathField.getText());
100105
//noinspection ResultOfMethodCallIgnored
@@ -107,38 +112,18 @@ private static void run(String device, String version) throws TSSCheckerExceptio
107112
Collections.addAll(args, "--apnonce", apnonce);
108113
}
109114
if (controller.betaCheckBox.isSelected()) {
110-
try {
111-
if (!ipswURL.matches("https?://.*apple.*\\.ipsw")) {
112-
newUnreportableError("\"" + ipswURL + "\" is not a valid URL.\n\nMake sure it starts with \"http://\" or \"https://\", has \"apple\" in it, and ends with \".ipsw\"");
113-
return;
114-
}
115-
buildManifestPlist = File.createTempFile("BuildManifest", ".plist");
116-
ZipInputStream zin;
117-
try {
118-
URL url = new URL(ipswURL);
119-
zin = new ZipInputStream(url.openStream());
120-
} catch (IOException e) {
121-
newUnreportableError("\"" + ipswURL + "\" is not a valid URL.\n\nMake sure it starts with \"http://\" or \"https://\", has \"apple\" in it, and ends with \".ipsw\"");
122-
deleteTempFiles(buildManifestPlist);
123-
return;
124-
}
125-
ZipEntry ze;
126-
while ((ze = zin.getNextEntry()) != null) {
127-
if ("BuildManifest.plist".equals(ze.getName())) {
128-
copyStreamToFile(zin, buildManifestPlist);
129-
break;
130-
}
131-
}
132-
buildManifestPlist.deleteOnExit();
133-
} catch (IOException e) {
134-
newReportableError("Unable to get BuildManifest from .ipsw.", e.getMessage());
135-
e.printStackTrace();
136-
deleteTempFiles(buildManifestPlist);
115+
if (!controller.ipswField.getText().matches("https?://.*apple.*\\.ipsw")) {
116+
newUnreportableError("\"" + ipswURL + "\" is not a valid URL.\n\nMake sure it starts with \"http://\" or \"https://\", has \"apple\" in it, and ends with \".ipsw\"");
137117
return;
138118
}
139-
Collections.addAll(args, "-i", version, "--beta", "--buildid", controller.buildIDField.getText(), "-m", buildManifestPlist.toString());
140-
} else {
141-
Collections.addAll(args, "-i", version);
119+
Collections.addAll(args, "--beta", "--buildid", controller.buildIDField.getText());
120+
}
121+
try {
122+
Collections.addAll(args, "-m", extractBuildManifest(ipswURL).toString());
123+
} catch (IOException e) {
124+
newReportableError("Unable to extract BuildManifest from .ipsw.", e.getMessage());
125+
e.printStackTrace();
126+
return;
142127
}
143128
String tsscheckerLog;
144129
try {
@@ -148,7 +133,6 @@ private static void run(String device, String version) throws TSSCheckerExceptio
148133
} catch (IOException e) {
149134
newReportableError("There was an error starting tsschecker.", e.toString());
150135
e.printStackTrace();
151-
deleteTempFiles(buildManifestPlist);
152136
throw new TSSCheckerException(e);
153137
}
154138

@@ -160,7 +144,6 @@ private static void run(String device, String version) throws TSSCheckerExceptio
160144
alert.setHeaderText("Success!");
161145
alert.showAndWait();
162146
}
163-
deleteTempFiles(buildManifestPlist);
164147
return;
165148
} else if (containsIgnoreCase(tsscheckerLog, "[Error] [TSSC] manually specified ecid=" + ecid + ", but parsing failed")) {
166149
newUnreportableError("\"" + ecid + "\"" + " is not a valid ECID. Try getting it from iTunes.\n\nIf this was done to test whether the preset works in the background, please cancel that preset, fix the error, and try again.");
@@ -221,15 +204,8 @@ && containsIgnoreCase(tsscheckerLog, "Error] [TSSC] checking tss status failed!"
221204
} else {
222205
newReportableError("Unknown result.\n\nIf this was done to test whether the preset works in the background, please cancel that preset, fix the error, and try again.", tsscheckerLog);
223206
}
224-
deleteTempFiles(buildManifestPlist);
225207
throw new TSSCheckerException();
226-
}
227208

228-
@SuppressWarnings("ResultOfMethodCallIgnored")
229-
private static void deleteTempFiles(File buildManifestPlist) {
230-
if (buildManifestPlist != null && buildManifestPlist.exists()) {
231-
buildManifestPlist.delete();
232-
}
233209
}
234210

235211
/**

0 commit comments

Comments
 (0)