OSDN Git Service

Repeat after me, eddyg is a wally.
[handbrake-jp/handbrake-jp-git.git] / macosx / HBDVDDetector.m
1 /**
2  * HBDriveDetector.m
3  * 8/17/2007
4  * 
5  * This file is part of the HandBrake source code.
6  * Homepage: <http://handbrake.m0k.org/>.
7  * It may be used under the terms of the GNU General Public License.
8  */
9
10 #include <IOKit/IOKitLib.h>
11 #include <IOKit/storage/IOMedia.h>
12 #include <IOKit/storage/IODVDMedia.h>
13
14 #import "HBDVDDetector.h"
15
16
17 @interface HBDVDDetector (Private)
18
19 - (NSString *)bsdNameForPath;
20 - (BOOL)pathHasVideoTS;
21 - (BOOL)deviceIsDVD;
22 - (io_service_t)getIOKitServiceForBSDName;
23 - (BOOL)isDVDService: (io_service_t)service;
24 - (BOOL)isWholeMediaService: (io_service_t)service;
25
26 @end
27
28
29 @implementation HBDVDDetector
30
31 + (HBDVDDetector *)detectorForPath: (NSString *)aPath
32 {
33     return [[[self alloc] initWithPath:aPath] autorelease];
34 }
35
36
37 - (HBDVDDetector *)initWithPath: (NSString *)aPath
38 {
39     NSAssert(aPath, @"nil string passed to drive detector.");
40         if( self = [super init] )       
41         {
42         path = [aPath retain];
43         bsdName = nil;
44         }
45     return self;
46 }
47
48
49 - (void)dealloc
50 {
51     [path release];
52     path = nil;
53     [bsdName release];
54     bsdName = nil;
55     [super dealloc];
56 }
57
58
59 - (BOOL)isVideoDVD
60 {
61     if( !bsdName )
62     {
63         bsdName = [[self bsdNameForPath] retain];
64     }
65     return ( [self pathHasVideoTS] && [self deviceIsDVD] );
66 }
67
68
69 - (NSString *)devicePath
70 {
71     if( !bsdName )
72     {
73         bsdName = [[self bsdNameForPath] retain];
74     }
75     return [NSString stringWithFormat:@"/dev/%@", bsdName];
76 }
77
78 @end
79
80
81 @implementation HBDVDDetector (Private)
82
83 - (NSString *)bsdNameForPath
84 {
85     OSStatus err;
86         FSRef ref;
87         err = FSPathMakeRef( (const UInt8 *) [path fileSystemRepresentation],
88                          &ref, NULL );  
89         if( err != noErr )
90     {
91         return nil;
92     }
93
94     // Get the volume reference number.
95     FSCatalogInfo catalogInfo;
96     err = FSGetCatalogInfo( &ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL,
97                             NULL);
98     if( err != noErr )
99     {
100         return nil;
101     }
102     FSVolumeRefNum volRefNum = catalogInfo.volume;
103
104         // Mow let's get the device name
105         GetVolParmsInfoBuffer volumeParms;
106         HParamBlockRec pb;
107         pb.ioParam.ioNamePtr = NULL;
108         pb.ioParam.ioVRefNum = volRefNum;
109         pb.ioParam.ioBuffer = (Ptr) &volumeParms;
110         pb.ioParam.ioReqCount = sizeof( volumeParms );
111         err = PBHGetVolParmsSync( &pb );
112     if( err != noErr )
113     {
114         return nil;
115     }
116
117         // A version 4 GetVolParmsInfoBuffer contains the BSD node name in the vMDeviceID field.
118         // It is actually a char * value. This is mentioned in the header CoreServices/CarbonCore/Files.h.
119     return [NSString stringWithCString:(char *)volumeParms.vMDeviceID];
120 }
121
122
123 - (BOOL)pathHasVideoTS
124 {
125     // Check one level under the path
126     if( [[NSFileManager defaultManager] fileExistsAtPath:
127         [path stringByAppendingPathComponent:@"VIDEO_TS"]] )
128     {
129         return YES;
130     }
131
132     // Now check above the path
133     return [[path pathComponents] containsObject:@"VIDEO_TS"];
134 }
135
136
137 - (BOOL)deviceIsDVD
138 {
139     io_service_t service = [self getIOKitServiceForBSDName];
140     if( service == IO_OBJECT_NULL )
141     {
142         return NO;
143     }
144     BOOL result = [self isDVDService:service];
145     IOObjectRelease(service);
146     return result;
147 }
148
149
150 - (io_service_t)getIOKitServiceForBSDName
151 {
152     CFMutableDictionaryRef  matchingDict;
153     matchingDict = IOBSDNameMatching( kIOMasterPortDefault, 0, [bsdName cString] );
154     if( matchingDict == NULL )
155     {
156         return IO_OBJECT_NULL;
157     }
158         
159     // Fetch the object with the matching BSD node name. There should only be
160     // one match, so IOServiceGetMatchingService is used instead of
161     // IOServiceGetMatchingServices to simplify the code.
162     return IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict );    
163 }
164
165
166 - (BOOL)isDVDService: (io_service_t)service
167 {
168     // Find the IOMedia object that represents the entire (whole) media that the
169     // volume is on. 
170     //
171     // If the volume is on partitioned media, the whole media object will be a
172     // parent of the volume's media object. If the media is not partitioned, the
173     // volume's media object will be the whole media object.
174     // 
175     // The whole media object is indicated in the IORegistry by the presence of
176     // a property with the key "Whole" and value "Yes".
177
178     // Create an iterator across all parents of the service object passed in.
179     kern_return_t  kernResult;
180     io_iterator_t  iter;
181     kernResult = IORegistryEntryCreateIterator( service,
182                                                 kIOServicePlane,
183                                                 kIORegistryIterateRecursively | kIORegistryIterateParents,
184                                                 &iter );
185     if( kernResult != KERN_SUCCESS )
186     {
187         return NO;
188     }
189     if( iter == IO_OBJECT_NULL )
190     {
191         return NO;
192     }
193
194
195     // A reference on the initial service object is released in the do-while loop below,
196     // so add a reference to balance.
197     IOObjectRetain( service );
198
199     BOOL isDVD = NO;
200     do
201     {
202         isDVD = ( [self isWholeMediaService:service] &&
203                   IOObjectConformsTo(service, kIODVDMediaClass) );
204         IOObjectRelease(service);
205     } while( !isDVD && (service = IOIteratorNext(iter)) );
206     IOObjectRelease( iter );
207
208     return isDVD;
209 }
210
211
212 - (BOOL)isWholeMediaService: (io_service_t)service
213 {
214     //
215     // Determine if the object passed in represents an IOMedia (or subclass) object.
216     // If it does, test the "Whole" property.
217     //
218     
219     Boolean isWholeMedia = NO;
220     
221     if( IOObjectConformsTo(service, kIOMediaClass) )
222     {
223         CFTypeRef wholeMedia;
224         wholeMedia = IORegistryEntryCreateCFProperty( service, 
225                                                       CFSTR(kIOMediaWholeKey), 
226                                                       kCFAllocatorDefault, 
227                                                       0);
228         if( !wholeMedia )
229         {
230             return NO;
231         }
232         isWholeMedia = CFBooleanGetValue( (CFBooleanRef)wholeMedia );
233         CFRelease(wholeMedia);
234     }
235
236     return isWholeMedia;
237 }
238
239
240 @end