OSDN Git Service

1d3422caf5eebe3bfa934ffd821df507350687d6
[handbrake-jp/handbrake-jp.git] / gtk / src / ghb-dvd.c
1 /***************************************************************************
2  *            ghb-dvd.c
3  *
4  *  Sat Apr 19 11:12:53 2008
5  *  Copyright  2008  John Stebbins
6  *  <john at stebbins dot name>
7  ****************************************************************************/
8
9 /*
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Library General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
23  */
24  
25 // Well, I waisted a bit of time on this.  It seems libhb has a function for
26 // this that I hadn't discovered yet. hb_dvd_name().
27
28 // I borrowed most of this from the udev utility vol_id
29 // Here is the authors copyright.
30 /*
31  * volume_id - reads filesystem label and uuid
32  *
33  * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
34  *
35  *      This program is free software; you can redistribute it and/or modify it
36  *      under the terms of the GNU General Public License as published by the
37  *      Free Software Foundation version 2 of the License.
38  */
39
40 #if defined(_WIN32)
41 #include <windows.h>
42 #endif
43
44 #include <glib.h>
45 #include <gio/gio.h>
46
47 #include "ghb-dvd.h"
48
49 #if 0
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <string.h>
53
54 #ifndef PACKED
55 #define PACKED                          __attribute__((packed))
56 #endif
57
58 struct volume_descriptor {
59         struct descriptor_tag {
60                 guint16 id;
61                 guint16 version;
62                 guint8  checksum;
63                 guint8  reserved;
64                 guint16 serial;
65                 guint16 crc;
66                 guint16 crc_len;
67                 guint32 location;
68         } PACKED tag;
69         union {
70                 struct anchor_descriptor {
71                         guint32 length;
72                         guint32 location;
73                 } PACKED anchor;
74                 struct primary_descriptor {
75                         guint32 seq_num;
76                         guint32 desc_num;
77                         struct dstring {
78                                 guint8  clen;
79                                 guint8  c[31];
80                         } PACKED ident;
81                 } PACKED primary;
82         } PACKED type;
83 } PACKED;
84
85 struct volume_structure_descriptor {
86         guint8          type;
87         guint8          id[5];
88         guint8          version;
89 } PACKED;
90
91 #define VOLUME_ID_LABEL_SIZE            64
92 typedef struct 
93 {
94         gint fd;
95         gchar label[VOLUME_ID_LABEL_SIZE+1];
96 } udf_info_t;
97
98 enum endian {
99         LE = 0,
100         BE = 1
101 };
102
103 #ifdef __BYTE_ORDER
104 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
105 #define le16_to_cpu(x) (x)
106 #define le32_to_cpu(x) (x)
107 #define le64_to_cpu(x) (x)
108 #define be16_to_cpu(x) bswap_16(x)
109 #define be32_to_cpu(x) bswap_32(x)
110 #define cpu_to_le16(x) (x)
111 #define cpu_to_le32(x) (x)
112 #define cpu_to_be32(x) bswap_32(x)
113 #elif (__BYTE_ORDER == __BIG_ENDIAN)
114 #define le16_to_cpu(x) bswap_16(x)
115 #define le32_to_cpu(x) bswap_32(x)
116 #define le64_to_cpu(x) bswap_64(x)
117 #define be16_to_cpu(x) (x)
118 #define be32_to_cpu(x) (x)
119 #define cpu_to_le16(x) bswap_16(x)
120 #define cpu_to_le32(x) bswap_32(x)
121 #define cpu_to_be32(x) (x)
122 #endif
123 #endif /* __BYTE_ORDER */
124
125 #define UDF_VSD_OFFSET                  0x8000
126
127 static guint8*
128 get_buffer(int fd, guint64 off, gsize len)
129 {
130         gint buf_len;
131         
132         if (lseek(fd, off, SEEK_SET) < 0) 
133         {
134                 return NULL;
135         }
136         guint8 *buffer = g_malloc(len);
137         buf_len = read(fd, buffer, len);
138         if (buf_len < 0) 
139         {
140                 g_free(buffer);
141                 return NULL;
142         }
143         return buffer;
144 }
145
146 static gint 
147 set_unicode16(guint8 *str, gsize len, const guint8 *buf, gint endianess, gsize count)
148 {
149         gint ii, jj;
150         guint16 c;
151
152         jj = 0;
153         for (ii = 0; ii + 2 <= count; ii += 2) {
154                 if (endianess == LE)
155                         c = (buf[ii+1] << 8) | buf[ii];
156                 else
157                         c = (buf[ii] << 8) | buf[ii+1];
158                 if (c == 0) {
159                         str[jj] = '\0';
160                         break;
161                 } else if (c < 0x80) {
162                         if (jj+1 >= len)
163                                 break;
164                         str[jj++] = (guint8) c;
165                 } else if (c < 0x800) {
166                         if (jj+2 >= len)
167                                 break;
168                         str[jj++] = (guint8) (0xc0 | (c >> 6));
169                         str[jj++] = (guint8) (0x80 | (c & 0x3f));
170                 } else {
171                         if (jj+3 >= len)
172                                 break;
173                         str[jj++] = (guint8) (0xe0 | (c >> 12));
174                         str[jj++] = (guint8) (0x80 | ((c >> 6) & 0x3f));
175                         str[jj++] = (guint8) (0x80 | (c & 0x3f));
176                 }
177         }
178         str[jj] = '\0';
179         return jj;
180 }
181
182 static void 
183 set_label_string(guint8 *str, const guint8 *buf, gsize count)
184 {
185         gint ii;
186
187         memcpy(str, buf, count);
188         str[count] = 0;
189         
190         /* remove trailing whitespace */
191         ii = strlen((gchar*)str);
192         while (ii--) 
193         {
194                 if (!g_ascii_isspace(str[ii]))
195                         break;
196         }
197         str[ii+1] = 0;
198 }
199  
200 static gint 
201 probe_udf(udf_info_t *id)
202 {
203         struct volume_descriptor *vd;
204         struct volume_structure_descriptor *vsd;
205         guint bs;
206         guint b;
207         guint type;
208         guint count;
209         guint loc;
210         guint clen;
211         guint64 off = 0;
212
213         vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET, 0x200);
214         if (vsd == NULL)
215                 return -1;
216
217         if (memcmp(vsd->id, "NSR02", 5) == 0)
218                 goto blocksize;
219         if (memcmp(vsd->id, "NSR03", 5) == 0)
220                 goto blocksize;
221         if (memcmp(vsd->id, "BEA01", 5) == 0)
222                 goto blocksize;
223         if (memcmp(vsd->id, "BOOT2", 5) == 0)
224                 goto blocksize;
225         if (memcmp(vsd->id, "CD001", 5) == 0)
226                 goto blocksize;
227         if (memcmp(vsd->id, "CDW02", 5) == 0)
228                 goto blocksize;
229         if (memcmp(vsd->id, "TEA03", 5) == 0)
230                 goto blocksize;
231         return -1;
232
233 blocksize:
234         /* search the next VSD to get the logical block size of the volume */
235         for (bs = 0x800; bs < 0x8000; bs += 0x800) {
236                 vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + bs, 0x800);
237                 if (vsd == NULL)
238                         return -1;
239                 if (vsd->id[0] != '\0')
240                         goto nsr;
241         }
242         return -1;
243
244 nsr:
245         /* search the list of VSDs for a NSR descriptor */
246         for (b = 0; b < 64; b++) {
247                 vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + (b * bs), 0x800);
248                 if (vsd == NULL)
249                         return -1;
250
251                 if (vsd->id[0] == '\0')
252                         return -1;
253                 if (memcmp(vsd->id, "NSR02", 5) == 0)
254                         goto anchor;
255                 if (memcmp(vsd->id, "NSR03", 5) == 0)
256                         goto anchor;
257         }
258         return -1;
259
260 anchor:
261         /* read anchor volume descriptor */
262         vd = (struct volume_descriptor *) get_buffer(id->fd, off + (256 * bs), 0x200);
263         if (vd == NULL)
264                 return -1;
265
266         type = le16_to_cpu(vd->tag.id);
267         if (type != 2) /* TAG_ID_AVDP */
268                 goto found;
269
270         /* get desriptor list address and block count */
271         count = le32_to_cpu(vd->type.anchor.length) / bs;
272         loc = le32_to_cpu(vd->type.anchor.location);
273
274         /* pick the primary descriptor from the list */
275         for (b = 0; b < count; b++) {
276                 vd = (struct volume_descriptor *) get_buffer(id->fd, off + ((loc + b) * bs), 0x200);
277                 if (vd == NULL)
278                         return -1;
279
280                 type = le16_to_cpu(vd->tag.id);
281
282                 /* check validity */
283                 if (type == 0)
284                         goto found;
285                 if (le32_to_cpu(vd->tag.location) != loc + b)
286                         goto found;
287
288                 if (type == 1) /* TAG_ID_PVD */
289                         goto pvd;
290         }
291         goto found;
292
293 pvd:
294         clen = vd->type.primary.ident.clen;
295         if (clen == 8)
296                 set_label_string((guint8*)id->label, vd->type.primary.ident.c, 31);
297         else if (clen == 16)
298                 set_unicode16((guint8*)id->label, sizeof(id->label), vd->type.primary.ident.c, BE, 31);
299
300 found:
301         return 0;
302 }
303
304 gchar*
305 ghb_dvd_volname(const gchar *device)
306 {
307         udf_info_t id;
308         gchar *buffer = NULL;
309
310         id.fd = open(device, O_RDONLY);
311         if (id.fd < 0) {
312                 return NULL;
313         }
314         if (probe_udf (&id) == 0)
315         {
316                 buffer = g_strdup(id.label);
317         }
318         return buffer;
319 }
320 #endif
321
322 gchar*
323 ghb_resolve_symlink(const gchar *name)
324 {
325         gchar *file;
326         GFileInfo *info;
327         GFile *gfile;
328
329         gfile = g_file_new_for_path(name);
330         info = g_file_query_info(gfile, 
331                 G_FILE_ATTRIBUTE_STANDARD_NAME ","
332                 G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
333                 G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
334                 G_FILE_QUERY_INFO_NONE, NULL, NULL);
335         while ((info != NULL) && g_file_info_get_is_symlink(info))
336         {
337                 GFile *parent;
338                 const gchar *target;
339
340                 parent = g_file_get_parent(gfile);
341                 g_object_unref(gfile);
342                 target = g_file_info_get_symlink_target(info);
343                 gfile = g_file_resolve_relative_path(parent, target);
344                 g_object_unref(parent);
345
346                 g_object_unref(info);
347                 info = g_file_query_info(gfile, 
348                         G_FILE_ATTRIBUTE_STANDARD_NAME ","
349                         G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
350                         G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
351                         G_FILE_QUERY_INFO_NONE, NULL, NULL);
352         }
353         if (info != NULL)
354         {
355                 file = g_file_get_path(gfile);
356                 g_object_unref(info);
357         }
358         else
359         {
360                 file = g_strdup(name);
361         }
362         g_object_unref(gfile);
363         return file;
364 }
365
366 void
367 ghb_dvd_set_current(const gchar *name, signal_user_data_t *ud)
368 {
369 #if !defined(_WIN32)
370         GFile *gfile;
371         GFileInfo *info;
372         gchar *resolved = ghb_resolve_symlink(name);
373
374         if (ud->current_dvd_device != NULL)
375         {
376                 g_free(ud->current_dvd_device);
377                 ud->current_dvd_device = NULL;
378         }
379         gfile = g_file_new_for_path(resolved);
380         info = g_file_query_info(gfile, 
381                 G_FILE_ATTRIBUTE_STANDARD_TYPE,
382                 G_FILE_QUERY_INFO_NONE, NULL, NULL);
383         if (info != NULL)
384         {
385                 if (g_file_info_get_file_type(info) == G_FILE_TYPE_SPECIAL)
386                 {
387                         // I could go through the trouble to scan the connected drives and 
388                         // verify that this device is connected and is a DVD.  But I don't 
389                         // think its neccessary.
390                         ud->current_dvd_device = resolved;
391                 }
392                 g_object_unref(info);
393         }
394         else
395         {
396                 g_free(resolved);
397         }
398         g_object_unref(gfile);
399 #else
400         gchar drive[4];
401         guint dtype;
402
403         if (ud->current_dvd_device != NULL)
404         {
405                 g_free(ud->current_dvd_device);
406                 ud->current_dvd_device = NULL;
407         }
408         g_strlcpy(drive, name, 4);
409         dtype = GetDriveType(drive);
410         if (dtype == DRIVE_CDROM)
411         {
412                 ud->current_dvd_device = g_strdup(name);
413         }
414 #endif
415 }