OSDN Git Service

import 0.9.3
[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 #include <glib.h>
41 #include <gio/gio.h>
42
43 #include "ghb-dvd.h"
44
45 #if 0
46 #include <unistd.h>
47 #include <fcntl.h>
48 #include <string.h>
49
50 #ifndef PACKED
51 #define PACKED                          __attribute__((packed))
52 #endif
53
54 struct volume_descriptor {
55         struct descriptor_tag {
56                 guint16 id;
57                 guint16 version;
58                 guint8  checksum;
59                 guint8  reserved;
60                 guint16 serial;
61                 guint16 crc;
62                 guint16 crc_len;
63                 guint32 location;
64         } PACKED tag;
65         union {
66                 struct anchor_descriptor {
67                         guint32 length;
68                         guint32 location;
69                 } PACKED anchor;
70                 struct primary_descriptor {
71                         guint32 seq_num;
72                         guint32 desc_num;
73                         struct dstring {
74                                 guint8  clen;
75                                 guint8  c[31];
76                         } PACKED ident;
77                 } PACKED primary;
78         } PACKED type;
79 } PACKED;
80
81 struct volume_structure_descriptor {
82         guint8          type;
83         guint8          id[5];
84         guint8          version;
85 } PACKED;
86
87 #define VOLUME_ID_LABEL_SIZE            64
88 typedef struct 
89 {
90         gint fd;
91         gchar label[VOLUME_ID_LABEL_SIZE+1];
92 } udf_info_t;
93
94 enum endian {
95         LE = 0,
96         BE = 1
97 };
98
99 #ifdef __BYTE_ORDER
100 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
101 #define le16_to_cpu(x) (x)
102 #define le32_to_cpu(x) (x)
103 #define le64_to_cpu(x) (x)
104 #define be16_to_cpu(x) bswap_16(x)
105 #define be32_to_cpu(x) bswap_32(x)
106 #define cpu_to_le16(x) (x)
107 #define cpu_to_le32(x) (x)
108 #define cpu_to_be32(x) bswap_32(x)
109 #elif (__BYTE_ORDER == __BIG_ENDIAN)
110 #define le16_to_cpu(x) bswap_16(x)
111 #define le32_to_cpu(x) bswap_32(x)
112 #define le64_to_cpu(x) bswap_64(x)
113 #define be16_to_cpu(x) (x)
114 #define be32_to_cpu(x) (x)
115 #define cpu_to_le16(x) bswap_16(x)
116 #define cpu_to_le32(x) bswap_32(x)
117 #define cpu_to_be32(x) (x)
118 #endif
119 #endif /* __BYTE_ORDER */
120
121 #define UDF_VSD_OFFSET                  0x8000
122
123 static guint8*
124 get_buffer(int fd, guint64 off, gsize len)
125 {
126         gint buf_len;
127         
128         if (lseek(fd, off, SEEK_SET) < 0) 
129         {
130                 return NULL;
131         }
132         guint8 *buffer = g_malloc(len);
133         buf_len = read(fd, buffer, len);
134         if (buf_len < 0) 
135         {
136                 g_free(buffer);
137                 return NULL;
138         }
139         return buffer;
140 }
141
142 static gint 
143 set_unicode16(guint8 *str, gsize len, const guint8 *buf, gint endianess, gsize count)
144 {
145         gint ii, jj;
146         guint16 c;
147
148         jj = 0;
149         for (ii = 0; ii + 2 <= count; ii += 2) {
150                 if (endianess == LE)
151                         c = (buf[ii+1] << 8) | buf[ii];
152                 else
153                         c = (buf[ii] << 8) | buf[ii+1];
154                 if (c == 0) {
155                         str[jj] = '\0';
156                         break;
157                 } else if (c < 0x80) {
158                         if (jj+1 >= len)
159                                 break;
160                         str[jj++] = (guint8) c;
161                 } else if (c < 0x800) {
162                         if (jj+2 >= len)
163                                 break;
164                         str[jj++] = (guint8) (0xc0 | (c >> 6));
165                         str[jj++] = (guint8) (0x80 | (c & 0x3f));
166                 } else {
167                         if (jj+3 >= len)
168                                 break;
169                         str[jj++] = (guint8) (0xe0 | (c >> 12));
170                         str[jj++] = (guint8) (0x80 | ((c >> 6) & 0x3f));
171                         str[jj++] = (guint8) (0x80 | (c & 0x3f));
172                 }
173         }
174         str[jj] = '\0';
175         return jj;
176 }
177
178 static void 
179 set_label_string(guint8 *str, const guint8 *buf, gsize count)
180 {
181         gint ii;
182
183         memcpy(str, buf, count);
184         str[count] = 0;
185         
186         /* remove trailing whitespace */
187         ii = strlen((gchar*)str);
188         while (ii--) 
189         {
190                 if (!g_ascii_isspace(str[ii]))
191                         break;
192         }
193         str[ii+1] = 0;
194 }
195  
196 static gint 
197 probe_udf(udf_info_t *id)
198 {
199         struct volume_descriptor *vd;
200         struct volume_structure_descriptor *vsd;
201         guint bs;
202         guint b;
203         guint type;
204         guint count;
205         guint loc;
206         guint clen;
207         guint64 off = 0;
208
209         vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET, 0x200);
210         if (vsd == NULL)
211                 return -1;
212
213         if (memcmp(vsd->id, "NSR02", 5) == 0)
214                 goto blocksize;
215         if (memcmp(vsd->id, "NSR03", 5) == 0)
216                 goto blocksize;
217         if (memcmp(vsd->id, "BEA01", 5) == 0)
218                 goto blocksize;
219         if (memcmp(vsd->id, "BOOT2", 5) == 0)
220                 goto blocksize;
221         if (memcmp(vsd->id, "CD001", 5) == 0)
222                 goto blocksize;
223         if (memcmp(vsd->id, "CDW02", 5) == 0)
224                 goto blocksize;
225         if (memcmp(vsd->id, "TEA03", 5) == 0)
226                 goto blocksize;
227         return -1;
228
229 blocksize:
230         /* search the next VSD to get the logical block size of the volume */
231         for (bs = 0x800; bs < 0x8000; bs += 0x800) {
232                 vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + bs, 0x800);
233                 if (vsd == NULL)
234                         return -1;
235                 if (vsd->id[0] != '\0')
236                         goto nsr;
237         }
238         return -1;
239
240 nsr:
241         /* search the list of VSDs for a NSR descriptor */
242         for (b = 0; b < 64; b++) {
243                 vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + (b * bs), 0x800);
244                 if (vsd == NULL)
245                         return -1;
246
247                 if (vsd->id[0] == '\0')
248                         return -1;
249                 if (memcmp(vsd->id, "NSR02", 5) == 0)
250                         goto anchor;
251                 if (memcmp(vsd->id, "NSR03", 5) == 0)
252                         goto anchor;
253         }
254         return -1;
255
256 anchor:
257         /* read anchor volume descriptor */
258         vd = (struct volume_descriptor *) get_buffer(id->fd, off + (256 * bs), 0x200);
259         if (vd == NULL)
260                 return -1;
261
262         type = le16_to_cpu(vd->tag.id);
263         if (type != 2) /* TAG_ID_AVDP */
264                 goto found;
265
266         /* get desriptor list address and block count */
267         count = le32_to_cpu(vd->type.anchor.length) / bs;
268         loc = le32_to_cpu(vd->type.anchor.location);
269
270         /* pick the primary descriptor from the list */
271         for (b = 0; b < count; b++) {
272                 vd = (struct volume_descriptor *) get_buffer(id->fd, off + ((loc + b) * bs), 0x200);
273                 if (vd == NULL)
274                         return -1;
275
276                 type = le16_to_cpu(vd->tag.id);
277
278                 /* check validity */
279                 if (type == 0)
280                         goto found;
281                 if (le32_to_cpu(vd->tag.location) != loc + b)
282                         goto found;
283
284                 if (type == 1) /* TAG_ID_PVD */
285                         goto pvd;
286         }
287         goto found;
288
289 pvd:
290         clen = vd->type.primary.ident.clen;
291         if (clen == 8)
292                 set_label_string((guint8*)id->label, vd->type.primary.ident.c, 31);
293         else if (clen == 16)
294                 set_unicode16((guint8*)id->label, sizeof(id->label), vd->type.primary.ident.c, BE, 31);
295
296 found:
297         return 0;
298 }
299
300 gchar*
301 ghb_dvd_volname(const gchar *device)
302 {
303         udf_info_t id;
304         gchar *buffer = NULL;
305
306         id.fd = open(device, O_RDONLY);
307         if (id.fd < 0) {
308                 return NULL;
309         }
310         if (probe_udf (&id) == 0)
311         {
312                 buffer = g_strdup(id.label);
313         }
314         return buffer;
315 }
316 #endif
317
318 #if defined(__linux__)
319 gchar*
320 ghb_resolve_symlink(const gchar *name)
321 {
322         gchar *file;
323         GFileInfo *info;
324         GFile *gfile;
325
326         gfile = g_file_new_for_path(name);
327         info = g_file_query_info(gfile, 
328                 G_FILE_ATTRIBUTE_STANDARD_NAME ","
329                 G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
330                 G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
331                 G_FILE_QUERY_INFO_NONE, NULL, NULL);
332         while ((info != NULL) && g_file_info_get_is_symlink(info))
333         {
334                 GFile *parent;
335                 const gchar *target;
336
337                 parent = g_file_get_parent(gfile);
338                 g_object_unref(gfile);
339                 target = g_file_info_get_symlink_target(info);
340                 gfile = g_file_resolve_relative_path(parent, target);
341                 g_object_unref(parent);
342
343                 g_object_unref(info);
344                 info = g_file_query_info(gfile, 
345                         G_FILE_ATTRIBUTE_STANDARD_NAME ","
346                         G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
347                         G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
348                         G_FILE_QUERY_INFO_NONE, NULL, NULL);
349         }
350         if (info != NULL)
351         {
352                 file = g_file_get_path(gfile);
353                 g_object_unref(info);
354         }
355         else
356         {
357                 file = g_strdup(name);
358         }
359         g_object_unref(gfile);
360         return file;
361 }
362 #endif
363
364 void
365 ghb_dvd_set_current(const gchar *name, signal_user_data_t *ud)
366 {
367 #if defined(__linux__)
368         GFile *gfile;
369         GFileInfo *info;
370         gchar *resolved = ghb_resolve_symlink(name);
371
372         if (ud->current_dvd_device != NULL)
373         {
374                 g_free(ud->current_dvd_device);
375                 ud->current_dvd_device = NULL;
376         }
377         gfile = g_file_new_for_path(resolved);
378         info = g_file_query_info(gfile, 
379                 G_FILE_ATTRIBUTE_STANDARD_TYPE,
380                 G_FILE_QUERY_INFO_NONE, NULL, NULL);
381         if (info != NULL)
382         {
383                 if (g_file_info_get_file_type(info) == G_FILE_TYPE_SPECIAL)
384                 {
385                         // I could go through the trouble to scan the connected drives and 
386                         // verify that this device is connected and is a DVD.  But I don't 
387                         // think its neccessary.
388                         ud->current_dvd_device = resolved;
389                 }
390                 g_object_unref(info);
391         }
392         else
393         {
394                 g_free(resolved);
395         }
396         g_object_unref(gfile);
397 #else
398         if (ud->current_dvd_device != NULL)
399         {
400                 g_free(ud->current_dvd_device);
401                 ud->current_dvd_device = NULL;
402         }
403         ud->current_dvd_device = g_strdup(name);;
404 #endif
405 }