@@ -134,7 +134,13 @@ - (instancetype)initWithFile:(NSURL *)fileURL password:(NSString*)password error
134134 if (error) {
135135 *error = nil ;
136136 }
137-
137+
138+ NSURL *firstVolumeURL = [URKArchive firstVolumeURL: fileURL];
139+ if (firstVolumeURL && ![firstVolumeURL.absoluteString isEqualToString: fileURL.absoluteString]) {
140+ URKLogDebug (" Overriding fileURL with first volume URL: %{public}@" , firstVolumeURL);
141+ fileURL = firstVolumeURL;
142+ }
143+
138144 URKLogDebug (" Initializing private fields" );
139145
140146 NSError *bookmarkError = nil ;
@@ -253,6 +259,21 @@ - (NSNumber *)compressedSize
253259 return [NSNumber numberWithUnsignedLongLong: attributes.fileSize];
254260}
255261
262+ - (BOOL )hasMultipleVolumes
263+ {
264+ URKCreateActivity (" Check If Multi-Volume Archive" );
265+
266+ NSError *listError = nil ;
267+ NSArray <NSURL *> *volumeURLs = [self listVolumeURLs: &listError];
268+
269+ if (!volumeURLs) {
270+ URKLogError (" Error getting file volumes list: %{public}@" , listError);
271+ return false ;
272+ }
273+
274+ return volumeURLs.count > 1 ;
275+ }
276+
256277
257278
258279#pragma mark - Zip file detection
@@ -380,6 +401,33 @@ + (BOOL)urlIsARAR:(NSURL *)fileURL
380401 return [NSArray arrayWithArray: fileInfos];
381402}
382403
404+ - (nullable NSArray <NSURL*> *)listVolumeURLs : (NSError **)error
405+ {
406+ URKCreateActivity (" Listing Volume URLs" );
407+
408+ NSArray <URKFileInfo*> *listFileInfo = [self listFileInfo: error];
409+
410+ if (listFileInfo == nil ) {
411+ return nil ;
412+ }
413+
414+ NSMutableSet <NSURL *> *volumeURLs = [[NSMutableSet alloc ] init ];
415+
416+ for (URKFileInfo* info in listFileInfo) {
417+ NSURL *archiveURL = [NSURL fileURLWithPath: info.archiveName];
418+
419+ if (archiveURL) {
420+ [volumeURLs addObject: archiveURL];
421+ }
422+ }
423+
424+ SEL sortBySelector = @selector (path );
425+ NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey: NSStringFromSelector (sortBySelector) ascending: YES ];
426+ NSArray <NSURL *> *sortedVolumes = [volumeURLs sortedArrayUsingDescriptors: @[sortDescriptor]];
427+
428+ return sortedVolumes;
429+ }
430+
383431- (BOOL )extractFilesTo : (NSString *)filePath
384432 overwrite : (BOOL )overwrite
385433 error : (NSError **)error
@@ -1262,6 +1310,8 @@ - (BOOL)headerContainsErrors:(NSError **)error
12621310
12631311- (NSProgress *)beginProgressOperation : (NSUInteger )totalUnitCount
12641312{
1313+ URKCreateActivity (" -beginProgressOperation:" );
1314+
12651315 NSProgress *progress;
12661316 progress = self.progress ;
12671317 if (!progress) {
@@ -1279,4 +1329,79 @@ - (NSProgress *)beginProgressOperation:(NSUInteger)totalUnitCount
12791329 return progress;
12801330}
12811331
1332+ + (NSURL *)firstVolumeURL : (NSURL *)volumeURL {
1333+ URKCreateActivity (" +firstVolumeURL:" );
1334+
1335+ URKLogDebug (" Checking if the file is part of a multi-volume archive..." );
1336+
1337+ if (!volumeURL) {
1338+ URKLogError (" +firstVolumeURL: nil volumeURL passed" )
1339+ }
1340+
1341+ NSString *volumePath = volumeURL.path ;
1342+
1343+ NSError *regexError = nil ;
1344+ NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: @" (.part)([0-9]+)(.rar)$"
1345+ options: NSRegularExpressionCaseInsensitive
1346+ error: ®exError];
1347+ if (!regex) {
1348+ URKLogError (" Error constructing filename regex" )
1349+ return nil ;
1350+ }
1351+
1352+ NSString *firstVolumePath = nil ;
1353+
1354+ // Check if it's following the current convention, like "Archive.part03.rar"
1355+ NSTextCheckingResult *match = [regex firstMatchInString: volumePath options: 0 range: NSMakeRange (0 , volumePath.length)];
1356+ if (match) {
1357+ URKLogDebug (" The file is part of a multi-volume archive" );
1358+
1359+ NSRange numberRange = [match rangeAtIndex: 2 ];
1360+ NSString * partOne = [[@" " stringByPaddingToLength: numberRange.length - 1
1361+ withString: @" 0"
1362+ startingAtIndex: 0 ]
1363+ stringByAppendingString: @" 1" ];
1364+
1365+ NSString * regexTemplate = [NSString stringWithFormat: @" $1%@ $3" , partOne];
1366+ firstVolumePath = [regex stringByReplacingMatchesInString: volumePath
1367+ options: 0
1368+ range: NSMakeRange (0 , volumePath.length)
1369+ withTemplate: regexTemplate];
1370+ }
1371+
1372+ // It still might be a multivolume archive. Check for the legacy naming convention, like "Archive.r03"
1373+ else {
1374+ // After rXX, rar uses r-z and symbols like {}|~... so accepting anything but a number
1375+ NSError *legacyRegexError = nil ;
1376+ regex = [NSRegularExpression regularExpressionWithPattern: @" (\\ .[^0-9])([0-9]+)$"
1377+ options: NSRegularExpressionCaseInsensitive
1378+ error: &legacyRegexError];
1379+
1380+ if (!regex) {
1381+ URKLogError (" Error constructing legacy filename regex" )
1382+ return nil ;
1383+ }
1384+
1385+ match = [regex firstMatchInString: volumePath options: 0 range: NSMakeRange (0 , volumePath.length)];
1386+ if (match) {
1387+ URKLogDebug (" The archive is part of a legacy volume" );
1388+ firstVolumePath = [[volumePath stringByDeletingPathExtension ] stringByAppendingPathExtension: @" rar" ];
1389+ }
1390+ }
1391+
1392+ // If it's a volume of either naming convention, use it
1393+ if (firstVolumePath) {
1394+ if ([[NSFileManager defaultManager ] fileExistsAtPath: firstVolumePath]) {
1395+ URKLogDebug (" First volume part %{public}@ found. Using as the main archive" , firstVolumePath);
1396+ return [NSURL fileURLWithPath: firstVolumePath];
1397+ }
1398+ else {
1399+ URKLogInfo (" First volume part not found: %{public}@. Skipping first volume selection" , firstVolumePath);
1400+ return nil ;
1401+ }
1402+ }
1403+
1404+ return volumeURL;
1405+ }
1406+
12821407@end
0 commit comments