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