/****************************************************************** * csscat.c - decrypt DVD .vob file to standard output * License: GNU General Public License * Usage: csscat [-d dvd-device] file.vob [file.mpeg] *****************************************************************/ #include #include #include #include #include extern char *optarg; extern int optind; #include #include #include #include #include "dvd_udf.h" #define PROGNAME "csscat" #define BUFFER_BLOCKS 16 #define DEFAULT_DVD_DEVICE "/dev/dvd" #define MAX_PATH 1024 void fail(char *msg, ...) { va_list ap; va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); exit(1); } void fail_perr(char *msg, ...) { va_list ap; va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); if (errno) fprintf(stderr, ": %s", strerror(errno)); putc('\n', stderr); exit(1); } /* Remove symbolic links, "..", ".", etc from path. */ char *get_canonical_path(char *path) { static char res[MAX_PATH]; char *p, buf[MAX_PATH]; int len; while (1) { if ((p = strrchr(path, '/')) != NULL) { char tmp = p[1]; p[1] = '\0'; if (chdir(path) == -1) fail_perr(PROGNAME ": chdir"); p[1] = tmp; path = p + 1; } len = readlink(path, buf, MAX_PATH); if (len == -1) break; if (len >= MAX_PATH) fail(PROGNAME ": link too long.\n"); buf[len] = '\0'; path = buf; } if (getcwd(res, MAX_PATH - 3) == NULL) fail_perr(PROGNAME ": getcwd"); len = strlen(res); if (len > 1) res[len++] = '/'; strncpy(res + len, path, MAX_PATH - len - 1); buf[MAX_PATH - 1] = '\0'; return res; } /* Find path from root of device of file. */ char *get_device_path(char *path, struct stat *path_stat) { struct stat dir_stat; char *p, *res; p = strrchr(path, '/'); if (p == NULL) return path; res = p+1; while (p >= path) { char tmp = p[1]; p[1] = '\0'; if (stat(path, &dir_stat) < 0) fail_perr(PROGNAME ": stat"); p[1] = tmp; if (dir_stat.st_dev != path_stat->st_dev) return res; /* found it! */ res = p+1; while (--p >= path && *p != '/') ; } return res; } void print_usage() { fprintf(stderr, "usage: "PROGNAME" [-d dvd-device] file.vob [file.mpeg]\n"); exit(1); } int main(int ac, char **av) { dvdcss_handle dvdcss; unsigned char p_buffer[DVDCSS_BLOCK_SIZE * BUFFER_BLOCKS]; unsigned int sector; int i, nread, c; off_t blocks; struct stat vob_stat; char *vob_file, *can_vf, *udf_file, save_path[MAX_PATH]; char *dvd_device = DEFAULT_DVD_DEVICE; FILE *out; while ((c = getopt(ac, av, "d:")) != EOF) switch (c) { case 'd': dvd_device = optarg; break; default: print_usage(); } if (ac <= optind || optind < ac - 2) print_usage(); /* Get .vob file size */ vob_file = av[optind++]; if (stat(vob_file, &vob_stat) == -1) fail_perr(PROGNAME ": can't stat %s", vob_file); if (! S_ISREG(vob_stat.st_mode)) fail(PROGNAME ": input must be a regular file.\n"); blocks = (vob_stat.st_size + DVDCSS_BLOCK_SIZE - 1) / DVDCSS_BLOCK_SIZE; /* Initialize libdvdcss */ dvdcss = dvdcss_open(dvd_device); if (dvdcss == NULL) fail_perr(PROGNAME ": dvdcss_open(%s) failed", dvd_device); /* Save current directory before calling get_canonical_path() */ if (getcwd(save_path, MAX_PATH) == NULL) fail_perr(PROGNAME ": getcwd"); /* Produce a filename that DVDUDFFindFile will hopefully understand. * Should work unless some funny mounting is used? */ can_vf = get_canonical_path(vob_file); udf_file = get_device_path(can_vf, &vob_stat); /* Get .vob file location on dvd */ sector = DVDUDFFindFile(dvdcss, udf_file); if (sector == 0 && udf_file != vob_file) sector = DVDUDFFindFile(dvdcss, vob_file); /* try cmd line name */ if (sector == 0) fail(PROGNAME ": DVDUDFFindFile(%s) failed.\n", udf_file); if (dvdcss_seek(dvdcss, sector, DVDCSS_SEEK_KEY) == -1) fail_perr(PROGNAME ": dvdcss_seek"); /* Open output file */ if (chdir(save_path) == -1) fail_perr(PROGNAME ": chdir(%s) failed", save_path); out = stdout; if (optind < ac) { out = fopen(av[optind], "w"); if (out == NULL) fail_perr(PROGNAME ": can't write to %s", av[optind]); } /* Decrypt .vob file using libdvdcss */ i = 0; while (i < blocks) { nread = blocks - i; if (nread > BUFFER_BLOCKS) nread = BUFFER_BLOCKS; if ((nread = dvdcss_read(dvdcss, p_buffer, nread, DVDCSS_READ_DECRYPT)) < 0) fail_perr(PROGNAME ": dvdcss_read"); if (fwrite(p_buffer, DVDCSS_BLOCK_SIZE, nread, out) != nread) fail_perr(PROGNAME ": fwrite"); i += nread; } dvdcss_close(dvdcss); return 0; }