summaryrefslogtreecommitdiffstats
path: root/gtk/src/ghb-dvd.c
diff options
context:
space:
mode:
Diffstat (limited to 'gtk/src/ghb-dvd.c')
-rw-r--r--gtk/src/ghb-dvd.c405
1 files changed, 405 insertions, 0 deletions
diff --git a/gtk/src/ghb-dvd.c b/gtk/src/ghb-dvd.c
new file mode 100644
index 000000000..c20b8fa3e
--- /dev/null
+++ b/gtk/src/ghb-dvd.c
@@ -0,0 +1,405 @@
+/***************************************************************************
+ * ghb-dvd.c
+ *
+ * Sat Apr 19 11:12:53 2008
+ * Copyright 2008 John Stebbins
+ * <john at stebbins dot name>
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
+ */
+
+// Well, I waisted a bit of time on this. It seems libhb has a function for
+// this that I hadn't discovered yet. hb_dvd_name().
+
+// I borrowed most of this from the udev utility vol_id
+// Here is the authors copyright.
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "ghb-dvd.h"
+
+#if 0
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#ifndef PACKED
+#define PACKED __attribute__((packed))
+#endif
+
+struct volume_descriptor {
+ struct descriptor_tag {
+ guint16 id;
+ guint16 version;
+ guint8 checksum;
+ guint8 reserved;
+ guint16 serial;
+ guint16 crc;
+ guint16 crc_len;
+ guint32 location;
+ } PACKED tag;
+ union {
+ struct anchor_descriptor {
+ guint32 length;
+ guint32 location;
+ } PACKED anchor;
+ struct primary_descriptor {
+ guint32 seq_num;
+ guint32 desc_num;
+ struct dstring {
+ guint8 clen;
+ guint8 c[31];
+ } PACKED ident;
+ } PACKED primary;
+ } PACKED type;
+} PACKED;
+
+struct volume_structure_descriptor {
+ guint8 type;
+ guint8 id[5];
+ guint8 version;
+} PACKED;
+
+#define VOLUME_ID_LABEL_SIZE 64
+typedef struct
+{
+ gint fd;
+ gchar label[VOLUME_ID_LABEL_SIZE+1];
+} udf_info_t;
+
+enum endian {
+ LE = 0,
+ BE = 1
+};
+
+#ifdef __BYTE_ORDER
+#if (__BYTE_ORDER == __LITTLE_ENDIAN)
+#define le16_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
+#define le64_to_cpu(x) (x)
+#define be16_to_cpu(x) bswap_16(x)
+#define be32_to_cpu(x) bswap_32(x)
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define cpu_to_be32(x) bswap_32(x)
+#elif (__BYTE_ORDER == __BIG_ENDIAN)
+#define le16_to_cpu(x) bswap_16(x)
+#define le32_to_cpu(x) bswap_32(x)
+#define le64_to_cpu(x) bswap_64(x)
+#define be16_to_cpu(x) (x)
+#define be32_to_cpu(x) (x)
+#define cpu_to_le16(x) bswap_16(x)
+#define cpu_to_le32(x) bswap_32(x)
+#define cpu_to_be32(x) (x)
+#endif
+#endif /* __BYTE_ORDER */
+
+#define UDF_VSD_OFFSET 0x8000
+
+static guint8*
+get_buffer(int fd, guint64 off, gsize len)
+{
+ gint buf_len;
+
+ if (lseek(fd, off, SEEK_SET) < 0)
+ {
+ return NULL;
+ }
+ guint8 *buffer = g_malloc(len);
+ buf_len = read(fd, buffer, len);
+ if (buf_len < 0)
+ {
+ g_free(buffer);
+ return NULL;
+ }
+ return buffer;
+}
+
+static gint
+set_unicode16(guint8 *str, gsize len, const guint8 *buf, gint endianess, gsize count)
+{
+ gint ii, jj;
+ guint16 c;
+
+ jj = 0;
+ for (ii = 0; ii + 2 <= count; ii += 2) {
+ if (endianess == LE)
+ c = (buf[ii+1] << 8) | buf[ii];
+ else
+ c = (buf[ii] << 8) | buf[ii+1];
+ if (c == 0) {
+ str[jj] = '\0';
+ break;
+ } else if (c < 0x80) {
+ if (jj+1 >= len)
+ break;
+ str[jj++] = (guint8) c;
+ } else if (c < 0x800) {
+ if (jj+2 >= len)
+ break;
+ str[jj++] = (guint8) (0xc0 | (c >> 6));
+ str[jj++] = (guint8) (0x80 | (c & 0x3f));
+ } else {
+ if (jj+3 >= len)
+ break;
+ str[jj++] = (guint8) (0xe0 | (c >> 12));
+ str[jj++] = (guint8) (0x80 | ((c >> 6) & 0x3f));
+ str[jj++] = (guint8) (0x80 | (c & 0x3f));
+ }
+ }
+ str[jj] = '\0';
+ return jj;
+}
+
+static void
+set_label_string(guint8 *str, const guint8 *buf, gsize count)
+{
+ gint ii;
+
+ memcpy(str, buf, count);
+ str[count] = 0;
+
+ /* remove trailing whitespace */
+ ii = strlen((gchar*)str);
+ while (ii--)
+ {
+ if (!g_ascii_isspace(str[ii]))
+ break;
+ }
+ str[ii+1] = 0;
+}
+
+static gint
+probe_udf(udf_info_t *id)
+{
+ struct volume_descriptor *vd;
+ struct volume_structure_descriptor *vsd;
+ guint bs;
+ guint b;
+ guint type;
+ guint count;
+ guint loc;
+ guint clen;
+ guint64 off = 0;
+
+ vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET, 0x200);
+ if (vsd == NULL)
+ return -1;
+
+ if (memcmp(vsd->id, "NSR02", 5) == 0)
+ goto blocksize;
+ if (memcmp(vsd->id, "NSR03", 5) == 0)
+ goto blocksize;
+ if (memcmp(vsd->id, "BEA01", 5) == 0)
+ goto blocksize;
+ if (memcmp(vsd->id, "BOOT2", 5) == 0)
+ goto blocksize;
+ if (memcmp(vsd->id, "CD001", 5) == 0)
+ goto blocksize;
+ if (memcmp(vsd->id, "CDW02", 5) == 0)
+ goto blocksize;
+ if (memcmp(vsd->id, "TEA03", 5) == 0)
+ goto blocksize;
+ return -1;
+
+blocksize:
+ /* search the next VSD to get the logical block size of the volume */
+ for (bs = 0x800; bs < 0x8000; bs += 0x800) {
+ vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + bs, 0x800);
+ if (vsd == NULL)
+ return -1;
+ if (vsd->id[0] != '\0')
+ goto nsr;
+ }
+ return -1;
+
+nsr:
+ /* search the list of VSDs for a NSR descriptor */
+ for (b = 0; b < 64; b++) {
+ vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + (b * bs), 0x800);
+ if (vsd == NULL)
+ return -1;
+
+ if (vsd->id[0] == '\0')
+ return -1;
+ if (memcmp(vsd->id, "NSR02", 5) == 0)
+ goto anchor;
+ if (memcmp(vsd->id, "NSR03", 5) == 0)
+ goto anchor;
+ }
+ return -1;
+
+anchor:
+ /* read anchor volume descriptor */
+ vd = (struct volume_descriptor *) get_buffer(id->fd, off + (256 * bs), 0x200);
+ if (vd == NULL)
+ return -1;
+
+ type = le16_to_cpu(vd->tag.id);
+ if (type != 2) /* TAG_ID_AVDP */
+ goto found;
+
+ /* get desriptor list address and block count */
+ count = le32_to_cpu(vd->type.anchor.length) / bs;
+ loc = le32_to_cpu(vd->type.anchor.location);
+
+ /* pick the primary descriptor from the list */
+ for (b = 0; b < count; b++) {
+ vd = (struct volume_descriptor *) get_buffer(id->fd, off + ((loc + b) * bs), 0x200);
+ if (vd == NULL)
+ return -1;
+
+ type = le16_to_cpu(vd->tag.id);
+
+ /* check validity */
+ if (type == 0)
+ goto found;
+ if (le32_to_cpu(vd->tag.location) != loc + b)
+ goto found;
+
+ if (type == 1) /* TAG_ID_PVD */
+ goto pvd;
+ }
+ goto found;
+
+pvd:
+ clen = vd->type.primary.ident.clen;
+ if (clen == 8)
+ set_label_string((guint8*)id->label, vd->type.primary.ident.c, 31);
+ else if (clen == 16)
+ set_unicode16((guint8*)id->label, sizeof(id->label), vd->type.primary.ident.c, BE, 31);
+
+found:
+ return 0;
+}
+
+gchar*
+ghb_dvd_volname(const gchar *device)
+{
+ udf_info_t id;
+ gchar *buffer = NULL;
+
+ id.fd = open(device, O_RDONLY);
+ if (id.fd < 0) {
+ return NULL;
+ }
+ if (probe_udf (&id) == 0)
+ {
+ buffer = g_strdup(id.label);
+ }
+ return buffer;
+}
+#endif
+
+#if defined(__linux__)
+gchar*
+ghb_resolve_symlink(const gchar *name)
+{
+ gchar *file;
+ GFileInfo *info;
+ GFile *gfile;
+
+ gfile = g_file_new_for_path(name);
+ info = g_file_query_info(gfile,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
+ G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ while ((info != NULL) && g_file_info_get_is_symlink(info))
+ {
+ GFile *parent;
+ const gchar *target;
+
+ parent = g_file_get_parent(gfile);
+ g_object_unref(gfile);
+ target = g_file_info_get_symlink_target(info);
+ gfile = g_file_resolve_relative_path(parent, target);
+ g_object_unref(parent);
+
+ g_object_unref(info);
+ info = g_file_query_info(gfile,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
+ G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ }
+ if (info != NULL)
+ {
+ file = g_file_get_path(gfile);
+ g_object_unref(info);
+ }
+ else
+ {
+ file = g_strdup(name);
+ }
+ g_object_unref(gfile);
+ return file;
+}
+#endif
+
+void
+ghb_dvd_set_current(const gchar *name, signal_user_data_t *ud)
+{
+#if defined(__linux__)
+ GFile *gfile;
+ GFileInfo *info;
+ gchar *resolved = ghb_resolve_symlink(name);
+
+ if (ud->current_dvd_device != NULL)
+ {
+ g_free(ud->current_dvd_device);
+ ud->current_dvd_device = NULL;
+ }
+ gfile = g_file_new_for_path(resolved);
+ info = g_file_query_info(gfile,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ if (info != NULL)
+ {
+ if (g_file_info_get_file_type(info) == G_FILE_TYPE_SPECIAL)
+ {
+ // I could go through the trouble to scan the connected drives and
+ // verify that this device is connected and is a DVD. But I don't
+ // think its neccessary.
+ ud->current_dvd_device = resolved;
+ }
+ g_object_unref(info);
+ }
+ else
+ {
+ g_free(resolved);
+ }
+ g_object_unref(gfile);
+#else
+ if (ud->current_dvd_device != NULL)
+ {
+ g_free(ud->current_dvd_device);
+ ud->current_dvd_device = NULL;
+ }
+ ud->current_dvd_device = g_strdup(name);;
+#endif
+}