2121import  com .google .gson .Gson ;
2222import  javafx .concurrent .Task ;
2323
24- import  java .io .File ;
2524import  java .io .FileNotFoundException ;
2625import  java .io .IOException ;
2726import  java .net .MalformedURLException ;
@@ -74,7 +73,6 @@ public class TSS extends Task<String> {
7473 private  TSS (String  deviceIdentifier , String  ecid , String  savePath , String  boardConfig , boolean  includeBetas , String  manualVersion , String  manualIpswURL , String  apnonce , String  generator , boolean  saveToTSSSaver , boolean  saveToSHSHHost ) {
7574 this .deviceIdentifier  = deviceIdentifier ;
7675 this .ecid  = ecid ;
77-  this .savePath  = savePath ;
7876 this .boardConfig  = boardConfig ;
7977 this .includeBetas  = includeBetas ;
8078 this .manualVersion  = manualVersion ;
@@ -83,6 +81,7 @@ private TSS(String deviceIdentifier, String ecid, String savePath, String boardC
8381 this .generator  = generator ;
8482 this .saveToTSSSaver  = saveToTSSSaver ;
8583 this .saveToSHSHHost  = saveToSHSHHost ;
84+  this .savePath  = parsePath (savePath );
8685 }
8786
8887 /** 
@@ -98,7 +97,7 @@ protected String call() throws TSSException {
9897 ArrayList <String > args  = constructArgs ();
9998
10099 var  alreadySaved  = new  StringJoiner (", " );
101-  var  savedFor  = new  StringJoiner (", " );
100+  var  newlySaved  = new  StringJoiner (", " );
102101 for  (Utils .IOSVersion  iosVersion  : iosVersions ) {
103102 if  (!Prefs .getAlwaysSaveNewBlobs () && checkAlreadySaved (iosVersion )) {
104103 alreadySaved .add (iosVersion .versionString ());
@@ -116,14 +115,14 @@ protected String call() throws TSSException {
116115 }
117116
118117 if  (iosVersion .versionString () != null ) {
119-  savedFor .add (iosVersion .versionString ());
118+  newlySaved .add (iosVersion .versionString ());
120119 }
121120 }
122-  StringBuilder  responseBuilder  = new  StringBuilder ();
123-  if  (manualIpswURL  != null  || savedFor .length () > 0 ) {
121+  var  responseBuilder  = new  StringBuilder ();
122+  if  (manualIpswURL  != null  || newlySaved .length () > 0 ) {
124123 responseBuilder .append ("Successfully saved blobs in\n " ).append (savePath );
125-  if  (savedFor .length () > 0 ) {
126-  responseBuilder .append ("\n \n For version" ).append (iosVersions .size () == 1  ? " "  : "s " ).append (savedFor );
124+  if  (newlySaved .length () > 0 ) {
125+  responseBuilder .append ("\n \n For version" ).append (iosVersions .size () == 1  ? " "  : "s " ).append (newlySaved );
127126 }
128127 if  (alreadySaved .length () > 0 ) {
129128 responseBuilder .append ("\n \n " );
@@ -156,7 +155,7 @@ private boolean checkAlreadySaved(Utils.IOSVersion ios) {
156155 String  fileName  = "%s_%s_%s_%s-%s_%s.shsh2" 
157156 .formatted (parseECID (), deviceIdentifier , getBoardConfig (), versionStringOnly , ios .buildid (), apnonce );
158157
159-  if  (Files .exists (Path .of (savePath , fileName ))) {
158+  if  (Files .exists (Path .of (parsePathWithVersion ( ios ) , fileName ))) {
160159 System .out .println ("Already Saved: "  + fileName );
161160 return  true ;
162161 }
@@ -168,16 +167,59 @@ private long parseECID() {
168167 : Long .parseLong (ecid .startsWith ("0x" ) ? ecid .substring (2 ) : ecid , 16 );
169168 }
170169
170+  private  String  parsePath (String  input ) {
171+  if  (!input .contains ("${" )) return  input ;
172+  String  template  = input ;
173+ 
174+  var  variables  = Map .of ("${DeviceIdentifier}" , deviceIdentifier ,
175+  "${BoardConfig}" , getBoardConfig (),
176+  "${APNonce}" , apnonce ,
177+  "${Generator}" , generator ,
178+  "${DeviceModel}" , Devices .identifierToModel (deviceIdentifier ),
179+  "${ECID}" , ecid );
180+  for  (Map .Entry <String , String > entry  : variables .entrySet ()) {
181+  template  = template .replace (entry .getKey (), entry .getValue ());
182+  }
183+  return  template ;
184+  }
185+ 
186+  private  String  parsePathWithVersion (Utils .IOSVersion  ios ) {
187+  if  (!savePath .contains ("${" )) return  savePath ;
188+  var  template  = savePath ;
189+ 
190+  Map <String , String > variables ;
191+  if  (ios .versionString () != null ) {
192+  variables  = Map .of ("${FullVersionString}" , ios .versionString (),
193+  "${BuildID}" , ios .buildid (),
194+  "${MajorVersion}" , ios .versionString ().replaceFirst ("\\ ..*" , "" ));
195+  } else  {
196+  variables  = Map .of ("${FullVersionString}" , "UnknownVersion" ,
197+  "${BuildID}" , "UnknownBuildID" ,
198+  "${MajorVersion}" , "UnknownVersion" );
199+  }
200+  for  (Map .Entry <String , String > entry  : variables .entrySet ()) {
201+  template  = template .replace (entry .getKey (), entry .getValue ());
202+  }
203+ 
204+  return  template ;
205+  }
171206
172207 private  void  saveFor (Utils .IOSVersion  iosVersion , ArrayList <String > args ) throws  TSSException  {
173208 final  int  urlIndex  = args .size () - 1 ;
209+  final  int  pathIndex  = args .size () - 3 ;
174210 Path  manifest ;
175211 try  {
176212 manifest  = extractBuildManifest (iosVersion .ipswURL ());
177213 args .set (urlIndex , manifest .toString ());
178214 } catch  (IOException  e ) {
179215 throw  new  TSSException ("Unable to extract BuildManifest." , true , e );
180216 }
217+  try  {
218+  args .set (pathIndex , parsePathWithVersion (iosVersion ));
219+  Files .createDirectories (Path .of (args .get (pathIndex )));
220+  } catch  (IOException  e ) {
221+  throw  new  TSSException ("Unable to create save directory. Try with a different save path. If you are using variables, make sure they are spelled correctly." , false , e );
222+  }
181223 try  {
182224 System .out .println ("Running: "  + args );
183225 String  tssLog  = executeProgram (args );
@@ -258,9 +300,7 @@ private List<Utils.IOSVersion> getIOSVersions() throws TSSException {
258300 private  ArrayList <String > constructArgs () {
259301 ArrayList <String > args  = new  ArrayList <>(17 );
260302 String  tsscheckerPath  = Utils .getTsschecker ().getAbsolutePath ();
261-  //noinspection ResultOfMethodCallIgnored 
262-  new  File (savePath ).mkdirs ();
263-  Collections .addAll (args , tsscheckerPath , "--nocache" , "--save" , "--device" , deviceIdentifier , "--ecid" , ecid , "--save-path" , savePath );
303+  Collections .addAll (args , tsscheckerPath , "--nocache" , "--save" , "--device" , deviceIdentifier , "--ecid" , ecid );
264304 Collections .addAll (args , "--boardconfig" , getBoardConfig ());
265305 if  (apnonce  != null ) {
266306 Collections .addAll (args , "--apnonce" , apnonce );
@@ -270,7 +310,9 @@ private ArrayList<String> constructArgs() {
270310 } else  {
271311 Collections .addAll (args , "--generator" , "0x1111111111111111" );
272312 }
273-  Collections .addAll (args , "--build-manifest" , "will be replaced in loop" );
313+ 
314+  Collections .addAll (args , "--save-path" , "will be replaced in loop" ,
315+  "--build-manifest" , "will be replaced in loop" );
274316
275317 return  args ;
276318 }
@@ -294,7 +336,7 @@ && containsIgnoreCase(tsscheckerLog, "checking tss status failed")) {
294336 } else  if  (containsIgnoreCase (tsscheckerLog , "Could not resolve host" )) {
295337 throw  new  TSSException ("Saving blobs failed. Check your internet connection." , false , tsscheckerLog );
296338 } else  if  (containsIgnoreCase (tsscheckerLog , "can't save shsh at" )) {
297-  throw  new  TSSException ("'"  + savePath  + "' is not a valid path" , false );
339+  throw  new  TSSException ("'"  + savePath  + "' is not a valid path. If you are using variables, make sure they are spelled correctly. " , false );
298340 } else  if  (containsIgnoreCase (tsscheckerLog , "IS NOT being signed" )) {
299341 if  (manualVersion  == null ) {
300342 throw  new  TSSException ("The "  + Devices .getOSNameForType (Devices .getDeviceType (deviceIdentifier )) + " version is not being signed for device "  + deviceIdentifier , false );
0 commit comments