OSDN Git Service

MacGui: Use nil instead of NULL. Patch by blindjimmy. http://handbrake.djgraphite...
[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.fr/>.
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     // Now let's get the device name
105     GetVolParmsInfoBuffer volumeParms;
106     err = FSGetVolumeParms ( volRefNum, &volumeParms, sizeof( volumeParms ) );
107
108     if( err != noErr )
109     {
110         return nil;
111     }
112
113     // A version 4 GetVolParmsInfoBuffer contains the BSD node name in the vMDeviceID field.
114     // It is actually a char * value. This is mentioned in the header CoreServices/CarbonCore/Files.h.
115     return [NSString stringWithCString:(char *)volumeParms.vMDeviceID];
116 }
117
118
119 - (BOOL)pathHasVideoTS
120 {
121     // Check one level under the path
122     if( [[NSFileManager defaultManager] fileExistsAtPath:
123         [path stringByAppendingPathComponent:@"VIDEO_TS"]] )
124     {
125         return YES;
126     }
127
128     // Now check above the path
129     return [[path pathComponents] containsObject:@"VIDEO_TS"];
130 }
131
132
133 - (BOOL)deviceIsDVD
134 {
135     io_service_t service = [self getIOKitServiceForBSDName];
136     if( service == IO_OBJECT_NULL )
137     {
138         return NO;
139     }
140     BOOL result = [self isDVDService:service];
141     IOObjectRelease(service);
142     return result;
143 }
144
145
146 - (io_service_t)getIOKitServiceForBSDName
147 {
148     CFMutableDictionaryRef  matchingDict;
149     matchingDict = IOBSDNameMatching( kIOMasterPortDefault, 0, [bsdName UTF8String] );
150     if( matchingDict == NULL )
151     {
152         return IO_OBJECT_NULL;
153     }
154         
155     // Fetch the object with the matching BSD node name. There should only be
156     // one match, so IOServiceGetMatchingService is used instead of
157     // IOServiceGetMatchingServices to simplify the code.
158     return IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict );    
159 }
160
161
162 - (BOOL)isDVDService: (io_service_t)service
163 {
164     // Find the IOMedia object that represents the entire (whole) media that the
165     // volume is on. 
166     //
167     // If the volume is on partitioned media, the whole media object will be a
168     // parent of the volume's media object. If the media is not partitioned, the
169     // volume's media object will be the whole media object.
170     // 
171     // The whole media object is indicated in the IORegistry by the presence of
172     // a property with the key "Whole" and value "Yes".
173
174     // Create an iterator across all parents of the service object passed in.
175     kern_return_t  kernResult;
176     io_iterator_t  iter;
177     kernResult = IORegistryEntryCreateIterator( service,
178                                                 kIOServicePlane,
179                                                 kIORegistryIterateRecursively | kIORegistryIterateParents,
180                                                 &iter );
181     if( kernResult != KERN_SUCCESS )
182     {
183         return NO;
184     }
185     if( iter == IO_OBJECT_NULL )
186     {
187         return NO;
188     }
189
190
191     // A reference on the initial service object is released in the do-while loop below,
192     // so add a reference to balance.
193     IOObjectRetain( service );
194
195     BOOL isDVD = NO;
196     do
197     {
198         isDVD = ( [self isWholeMediaService:service] &&
199                   IOObjectConformsTo(service, kIODVDMediaClass) );
200         IOObjectRelease(service);
201     } while( !isDVD && (service = IOIteratorNext(iter)) );
202     IOObjectRelease( iter );
203
204     return isDVD;
205 }
206
207
208 - (BOOL)isWholeMediaService: (io_service_t)service
209 {
210     //
211     // Determine if the object passed in represents an IOMedia (or subclass) object.
212     // If it does, test the "Whole" property.
213     //
214     
215     Boolean isWholeMedia = NO;
216     
217     if( IOObjectConformsTo(service, kIOMediaClass) )
218     {
219         CFTypeRef wholeMedia;
220         wholeMedia = IORegistryEntryCreateCFProperty( service, 
221                                                       CFSTR(kIOMediaWholeKey), 
222                                                       kCFAllocatorDefault, 
223                                                       0);
224         if( !wholeMedia )
225         {
226             return NO;
227         }
228         isWholeMedia = CFBooleanGetValue( (CFBooleanRef)wholeMedia );
229         CFRelease(wholeMedia);
230     }
231
232     return isWholeMedia;
233 }
234
235
236 @end