1 /*
2 * Copyright (C) 2001-2004 Billy Biggs <vektor@dumbterm.net>,
3 * Håkan Hjort <d95hjort@dtek.chalmers.se>,
4 * Björn Englund <d4bjorn@dtek.chalmers.se>
5 *
6 * This file is part of libdvdread.
7 *
8 * libdvdread is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * libdvdread is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with libdvdread; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include "config.h"
24 #include <sys/types.h> /* off_t */
25 #include <sys/stat.h> /* stat */
26 #include <sys/time.h> /* For the timing of dvdcss_title crack. */
27 #include <fcntl.h> /* open */
28 #include <stdlib.h> /* free */
29 #include <stdio.h> /* fprintf */
30 #include <errno.h> /* errno, EIN* */
31 #include <string.h> /* memcpy, strlen */
32 #include <unistd.h> /* chdir, getcwd */
33 #include <limits.h> /* PATH_MAX */
34 #include <dirent.h> /* opendir, readdir */
35 #include <ctype.h> /* isalpha */
36
37 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__APPLE__)
38 # define SYS_BSD 1
39 #endif
40
41 #if defined(__sun)
42 # include <sys/mnttab.h>
43 #elif defined(__APPLE__)
44 # include <sys/param.h>
45 # include <sys/ucred.h>
46 # include <sys/mount.h>
47 #elif defined(SYS_BSD)
48 # include <sys/mount.h>
49 #elif defined(__linux__)
50 # include <mntent.h>
51 # include <paths.h>
52 #endif
53
54 #include "dvdread/dvd_udf.h"
55 #include "dvdread/dvd_reader.h"
56 #include "dvd_input.h"
57 #include "dvdread_internal.h"
58 #include "md5.h"
59 #include "dvdread/ifo_read.h"
60
61 #if defined(_WIN32)
62 # include <windows.h>
63 # include "msvc/contrib/win32_cs.h"
64 #endif
65
66 /* misc win32 helpers */
67
68 #ifdef _WIN32
69 # ifndef HAVE_GETTIMEOFDAY
70 /* replacement gettimeofday implementation */
71 # include <sys/timeb.h>
_private_gettimeofday(struct timeval * tv,void * tz)72 static inline int _private_gettimeofday( struct timeval *tv, void *tz )
73 {
74 struct timeb t;
75 ftime( &t );
76 tv->tv_sec = t.time;
77 tv->tv_usec = t.millitm * 1000;
78 return 0;
79 }
80 # define gettimeofday(TV, TZ) _private_gettimeofday((TV), (TZ))
81 # endif
82 #endif /* _WIN32 */
83
84 /* Compat wrapper for stat() */
85
86 #if defined(_WIN32)
87 /* can't re-define stat (used in both function name and struct name) */
88 typedef struct _stat64 dvdstat_t;
dvdstat(const char * path,dvdstat_t * st)89 static inline int dvdstat(const char *path, dvdstat_t *st)
90 {
91 wchar_t *wpath, *it;
92 int ret;
93
94 wpath = _utf8_to_wchar(path);
95 if (!wpath) {
96 return -1;
97 }
98
99 /* need to strip possible trailing \\ */
100 for (it = wpath; *it; it++)
101 if ((*it == '\\' || *it == '/') && *(it+1) == 0)
102 *it = 0;
103
104 ret = _wstat64(wpath, st);
105 free(wpath);
106 return ret;
107 }
108 #else
109 typedef struct stat dvdstat_t;
dvdstat(const char * file,dvdstat_t * st)110 static inline int dvdstat(const char *file, dvdstat_t *st) {
111 return stat(file, st);
112 }
113 #endif
114
115 #if defined(_WIN32)
116 /* UTF-8 aware version of opendir()/readdir() */
117
118 #include <io.h>
119
120 typedef struct {
121 intptr_t handle;
122 struct _wfinddata_t went;
123 struct dirent ent;
124 } win32_dir_t;
125
win32_opendir(const char * path)126 win32_dir_t *win32_opendir(const char *path)
127 {
128 char *filespec;
129 wchar_t *wfilespec;
130 win32_dir_t *d;
131
132 d = calloc(1, sizeof(*d));
133 if (!d)
134 return NULL;
135
136 filespec = malloc(strlen(path) + 3);
137 if (!filespec) {
138 goto fail;
139 }
140 sprintf(filespec, "%s\\*", path);
141
142 wfilespec = _utf8_to_wchar(filespec);
143 free(filespec);
144 if (!wfilespec) {
145 goto fail;
146 }
147
148 d->handle = _wfindfirst(wfilespec, &d->went);
149 free(wfilespec);
150 if (d->handle != -1) {
151 return d;
152 }
153
154 fail:
155 free(d);
156 return NULL;
157 }
158
win32_readdir(win32_dir_t * dir)159 static struct dirent *win32_readdir(win32_dir_t *dir)
160 {
161 if (dir->went.name[0]) {
162 if (!WideCharToMultiByte(CP_UTF8, 0, dir->went.name, -1, dir->ent.d_name, sizeof(dir->ent.d_name), NULL, NULL))
163 dir->ent.d_name[0] = 0; /* allow reading next */
164 dir->went.name[0] = 0;
165 _wfindnext(dir->handle, &dir->went);
166 return &dir->ent;
167 }
168
169 return NULL;
170 }
171
win32_closedir(win32_dir_t * dir)172 static void win32_closedir(win32_dir_t *dir)
173 {
174 _findclose(dir->handle);
175 free(dir);
176 }
177
178 #define DIR win32_dir_t
179 #define opendir win32_opendir
180 #define readdir win32_readdir
181 #define closedir win32_closedir
182
183 #endif /* _WIN32 */
184
185 #define DEFAULT_UDF_CACHE_LEVEL 1
186
187 struct dvd_reader_device_s {
188 /* Basic information. */
189 int isImageFile;
190
191 /* Hack for keeping track of the css status.
192 * 0: no css, 1: perhaps (need init of keys), 2: have done init */
193 int css_state;
194 int css_title; /* Last title that we have called dvdinpute_title for. */
195
196 /* Information required for an image file. */
197 dvd_input_t dev;
198
199 /* Information required for a directory path drive. */
200 char *path_root;
201
202 /* Filesystem cache */
203 int udfcache_level; /* 0 - turned off, 1 - on */
204 void *udfcache;
205 };
206
207 #define TITLES_MAX 9
208
209 struct dvd_file_s {
210 /* Basic information. */
211 dvd_reader_t *ctx;
212
213 /* Hack for selecting the right css title. */
214 int css_title;
215
216 /* Information required for an image file. */
217 uint32_t lb_start;
218 uint32_t seek_pos;
219
220 /* Information required for a directory path drive. */
221 size_t title_sizes[ TITLES_MAX ];
222 dvd_input_t title_devs[ TITLES_MAX ];
223
224 /* Calculated at open-time, size in blocks. */
225 ssize_t filesize;
226
227 /* Cache of the dvd_file. If not NULL, the cache corresponds to the whole
228 * dvd_file. Used only for IFO and BUP. */
229 unsigned char *cache;
230 };
231
232 /**
233 * Set the level of caching on udf
234 * level = 0 (no caching)
235 * level = 1 (caching filesystem info)
236 */
DVDUDFCacheLevel(dvd_reader_t * reader,int level)237 int DVDUDFCacheLevel(dvd_reader_t *reader, int level)
238 {
239 dvd_reader_device_t *dev = reader->rd;
240
241 if(level > 0) {
242 level = 1;
243 } else if(level < 0) {
244 return dev->udfcache_level;
245 }
246
247 dev->udfcache_level = level;
248
249 return level;
250 }
251
GetUDFCacheHandle(dvd_reader_t * reader)252 void *GetUDFCacheHandle(dvd_reader_t *reader)
253 {
254 dvd_reader_device_t *dev = reader->rd;
255
256 return dev->udfcache;
257 }
258
SetUDFCacheHandle(dvd_reader_t * reader,void * cache)259 void SetUDFCacheHandle(dvd_reader_t *reader, void *cache)
260 {
261 dvd_reader_device_t *dev = reader->rd;
262
263 dev->udfcache = cache;
264 }
265
266
267
268 /* Loop over all titles and call dvdcss_title to crack the keys. */
initAllCSSKeys(dvd_reader_t * ctx)269 static int initAllCSSKeys( dvd_reader_t *ctx )
270 {
271 dvd_reader_device_t *dvd = ctx->rd;
272 struct timeval all_s, all_e;
273 struct timeval t_s, t_e;
274 char filename[ MAX_UDF_FILE_NAME_LEN ];
275 uint32_t start, len;
276 int title;
277
278 const char *nokeys_str = getenv("DVDREAD_NOKEYS");
279 if(nokeys_str != NULL)
280 return 0;
281
282 Log2(ctx,"Attempting to retrieve all CSS keys" );
283 Log2(ctx,"This can take a _long_ time, please be patient" );
284 gettimeofday(&all_s, NULL);
285
286 for( title = 0; title < 100; title++ ) {
287 gettimeofday( &t_s, NULL );
288 if( title == 0 ) {
289 sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
290 } else {
291 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
292 }
293 start = UDFFindFile( ctx, filename, &len );
294 if( start != 0 && len != 0 ) {
295 /* Perform CSS key cracking for this title. */
296 Log3(ctx,"Get key for %s at 0x%08x",filename, start );
297 if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
298 Log1(ctx,"Error cracking CSS key for %s (0x%08x)", filename, start);
299 }
300 gettimeofday( &t_e, NULL );
301 Log3(ctx,"Elapsed time %ld", (long int) t_e.tv_sec - t_s.tv_sec );
302 }
303
304 if( title == 0 ) continue;
305
306 gettimeofday( &t_s, NULL );
307 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
308 start = UDFFindFile( ctx, filename, &len );
309 if( start == 0 || len == 0 ) break;
310
311 /* Perform CSS key cracking for this title. */
312 Log3(ctx,"Get key for %s at 0x%08x",filename, start );
313 if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
314 Log1(ctx,"Error cracking CSS key for %s (0x%08x)", filename, start);
315 }
316 gettimeofday( &t_e, NULL );
317 Log3(ctx,"Elapsed time %ld", (long int) t_e.tv_sec - t_s.tv_sec );
318 }
319 title--;
320
321 Log3(ctx,"Found %d VTS's", title );
322 gettimeofday(&all_e, NULL);
323 Log3(ctx,"Elapsed time %ld", (long int) all_e.tv_sec - all_s.tv_sec );
324
325 return 0;
326 }
327
328
329
330 /**
331 * Open a DVD image or block device file or use stream_cb functions.
332 */
DVDOpenImageFile(dvd_reader_t * ctx,const char * location,dvd_reader_stream_cb * stream_cb,int have_css)333 static dvd_reader_device_t *DVDOpenImageFile( dvd_reader_t *ctx,
334 const char *location,
335 dvd_reader_stream_cb *stream_cb,
336 int have_css )
337 {
338 dvd_reader_device_t *dvd;
339 dvd_input_t dev;
340
341 dev = dvdinput_open( ctx->priv, &ctx->logcb, location, stream_cb );
342 if( !dev ) {
343 Log0(ctx,"Can't open %s for reading", location );
344 return NULL;
345 }
346
347 dvd = calloc( 1, sizeof( dvd_reader_device_t ) );
348 if( !dvd ) {
349 dvdinput_close(dev);
350 return NULL;
351 }
352 dvd->isImageFile = 1;
353 dvd->dev = dev;
354
355 dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
356
357 if( have_css ) {
358 /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
359 * DVDCSS_METHOD = key but region mismatch. Unfortunately we
360 * don't have that information. */
361
362 dvd->css_state = 1; /* Need key init. */
363 }
364
365 return dvd;
366 }
367
DVDOpenPath(const char * path_root)368 static dvd_reader_device_t *DVDOpenPath( const char *path_root )
369 {
370 dvd_reader_device_t *dvd;
371
372 dvd = calloc( 1, sizeof( dvd_reader_device_t ) );
373 if( !dvd ) return NULL;
374 dvd->path_root = strdup( path_root );
375 if(!dvd->path_root) {
376 free(dvd);
377 return NULL;
378 }
379 dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
380
381 return dvd;
382 }
383
384 #if defined(__sun)
385 /* /dev/rdsk/c0t6d0s0 (link to /devices/...)
386 /vol/dev/rdsk/c0t6d0/??
387 /vol/rdsk/<name> */
sun_block2char(const char * path)388 static char *sun_block2char( const char *path )
389 {
390 char *new_path;
391
392 /* Must contain "/dsk/" */
393 if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
394
395 /* Replace "/dsk/" with "/rdsk/" */
396 new_path = malloc( strlen(path) + 2 );
397 if(!new_path) return NULL;
398 strcpy( new_path, path );
399 strcpy( strstr( new_path, "/dsk/" ), "" );
400 strcat( new_path, "/rdsk/" );
401 strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
402
403 return new_path;
404 }
405 #endif
406
407 #if defined(SYS_BSD)
408 /* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recommended to _not_ use r
409 update: FreeBSD and DragonFly no longer uses the prefix so don't add it.
410 OpenBSD /dev/rcd0c, it needs to be the raw device
411 NetBSD /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
412 Darwin /dev/rdisk0, it needs to be the raw device
413 BSD/OS /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do)
414 returns a string allocated with strdup. It should be freed when no longer
415 used. */
bsd_block2char(const char * path)416 static char *bsd_block2char( const char *path )
417 {
418 #if defined(__FreeBSD__) || defined(__DragonFly__)
419 return (char *) strdup( path );
420 #else
421 char *new_path;
422
423 /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
424 if( strncmp( path, "/dev/", 5 ) || !strncmp( path, "/dev/r", 6 ) )
425 return (char *) strdup( path );
426
427 /* Replace "/dev/" with "/dev/r" */
428 new_path = malloc( strlen(path) + 2 );
429 if(!new_path) return NULL;
430 strcpy( new_path, "/dev/r" );
431 strcat( new_path, path + strlen( "/dev/" ) );
432
433 return new_path;
434 #endif /* __FreeBSD__ || __DragonFly__ */
435 }
436 #endif
437
DVDOpenCommon(void * priv,const dvd_logger_cb * logcb,const char * ppath,dvd_reader_stream_cb * stream_cb)438 static dvd_reader_t *DVDOpenCommon( void *priv,
439 const dvd_logger_cb *logcb,
440 const char *ppath,
441 dvd_reader_stream_cb *stream_cb )
442 {
443 dvdstat_t fileinfo;
444 int ret, have_css, cdir = -1;
445 char *dev_name = NULL;
446 char *path = NULL, *new_path = NULL, *path_copy = NULL;
447 dvd_reader_t *ctx = calloc(1, sizeof(*ctx));
448 if(!ctx)
449 return NULL;
450
451 ctx->priv = priv;
452 if(logcb)
453 ctx->logcb = *logcb;
454
455 #if defined(_WIN32) || defined(__OS2__)
456 int len;
457 #endif
458
459 /* Try to open DVD using stream_cb functions */
460 if( priv != NULL && stream_cb != NULL )
461 {
462 have_css = dvdinput_setup( ctx->priv, &ctx->logcb );
463 ctx->rd = DVDOpenImageFile( ctx, NULL, stream_cb, have_css );
464 if(!ctx->rd)
465 {
466 free(ctx);
467 return NULL;
468 }
469 return ctx;
470 }
471
472 if( ppath == NULL )
473 goto DVDOpen_error;
474
475 path = strdup(ppath);
476 if( path == NULL )
477 goto DVDOpen_error;
478
479 /* Try to open libdvdcss or fall back to standard functions */
480 have_css = dvdinput_setup( ctx->priv, &ctx->logcb );
481
482 #if defined(_WIN32) || defined(__OS2__)
483 /* Strip off the trailing \ if it is not a drive */
484 len = strlen(path);
485 if ((len > 1) &&
486 (path[len - 1] == '\\') &&
487 (path[len - 2] != ':'))
488 {
489 path[len-1] = '\0';
490 }
491 #endif
492
493 ret = dvdstat( path, &fileinfo );
494
495 if( ret < 0 ) {
496
497 /* maybe "host:port" url? try opening it with acCeSS library */
498 if( strchr(path,':') ) {
499 ctx->rd = DVDOpenImageFile( ctx, path, NULL, have_css );
500 free(path);
501 if(!ctx->rd)
502 {
503 free(ctx);
504 return NULL;
505 }
506 return ctx;
507 }
508
509 /* If we can't stat the file, give up */
510 Log0(ctx, "Can't stat %s", path );
511 perror("");
512 goto DVDOpen_error;
513 }
514
515 /* First check if this is a block/char device or a file*/
516 if( S_ISBLK( fileinfo.st_mode ) ||
517 S_ISCHR( fileinfo.st_mode ) ||
518 S_ISREG( fileinfo.st_mode ) ) {
519
520 /**
521 * Block devices and regular files are assumed to be DVD-Video images.
522 */
523 #if defined(__sun)
524 dev_name = sun_block2char( path );
525 #elif defined(SYS_BSD)
526 dev_name = bsd_block2char( path );
527 #else
528 dev_name = strdup( path );
529 #endif
530 if(!dev_name)
531 goto DVDOpen_error;
532 ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
533 free( dev_name );
534 free(path);
535 if(!ctx->rd)
536 {
537 free(ctx);
538 return NULL;
539 }
540 return ctx;
541 } else if( S_ISDIR( fileinfo.st_mode ) ) {
542 #if defined(SYS_BSD)
543 struct statfs fs;
544 #elif defined(__sun) || defined(__linux__)
545 FILE *mntfile;
546 #endif
547
548 /* XXX: We should scream real loud here. */
549 if( !(path_copy = strdup( path ) ) )
550 goto DVDOpen_error;
551
552 #ifndef _WIN32 /* don't have fchdir, and getcwd( NULL, ... ) is strange */
553 /* Also WIN32 does not have symlinks, so we don't need this bit of code. */
554
555 /* Resolve any symlinks and get the absolute dir name. */
556 {
557 if( ( cdir = open( ".", O_RDONLY ) ) >= 0 ) {
558 int retval;
559 if( chdir( path_copy ) == -1 ) {
560 goto DVDOpen_error;
561 }
562 new_path = malloc(PATH_MAX+1);
563 if(!new_path) {
564 goto DVDOpen_error;
565 }
566 if( getcwd( new_path, PATH_MAX ) == NULL ) {
567 goto DVDOpen_error;
568 }
569 retval = fchdir( cdir );
570 close( cdir );
571 cdir = -1;
572 if( retval == -1 ) {
573 goto DVDOpen_error;
574 }
575 free(path_copy);
576 path_copy = new_path;
577 new_path = NULL;
578 }
579 }
580 #endif
581
582 /**
583 * If we're being asked to open a directory, check if that directory
584 * is the mount point for a DVD-ROM which we can use instead.
585 */
586
587 if( strlen( path_copy ) > 1 ) {
588 if( path_copy[ strlen( path_copy ) - 1 ] == '/' ) {
589 path_copy[ strlen( path_copy ) - 1 ] = '\0';
590 }
591 }
592
593 #if defined(_WIN32) || defined(__OS2__)
594 if( strlen( path_copy ) > 9 ) {
595 if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
596 "\\video_ts"))
597 path_copy[ strlen( path_copy ) - (9-1) ] = '\0';
598 }
599 #endif
600 if( strlen( path_copy ) > 9 ) {
601 if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
602 "/video_ts" ) ) {
603 path_copy[ strlen( path_copy ) - 9 ] = '\0';
604 }
605 }
606
607 if(path_copy[0] == '\0') {
608 free( path_copy );
609 if( !(path_copy = strdup( "/" ) ) )
610 goto DVDOpen_error;
611 }
612
613 #if defined(__APPLE__)
614 struct statfs s[128];
615 int r = getfsstat(NULL, 0, MNT_NOWAIT);
616 if (r > 0) {
617 if (r > 128)
618 r = 128;
619 r = getfsstat(s, r * sizeof(s[0]), MNT_NOWAIT);
620 int i;
621 for (i=0; i<r; i++) {
622 if (!strcmp(path_copy, s[i].f_mntonname)) {
623 dev_name = bsd_block2char(s[i].f_mntfromname);
624 Log3(ctx, "Attempting to use device %s"
625 " mounted on %s for CSS authentication",
626 dev_name,
627 s[i].f_mntonname);
628 ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
629 break;
630 }
631 }
632 }
633 #elif defined(SYS_BSD)
634 if( statfs( path_copy, &fs ) == 0 ) {
635 if( !strcmp( path_copy, fs.f_mntonname ) ) {
636 dev_name = bsd_block2char( fs.f_mntfromname );
637 Log3(ctx, "Attempting to use device %s"
638 " mounted on %s for CSS authentication",
639 dev_name,
640 fs.f_mntonname );
641 ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
642 }
643 }
644 #elif defined(__sun)
645 mntfile = fopen( MNTTAB, "r" );
646 if( mntfile ) {
647 struct mnttab mp;
648 int res;
649
650 while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
651 if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
652 dev_name = sun_block2char( mp.mnt_special );
653 Log3(ctx, "Attempting to use device %s"
654 " mounted on %s for CSS authentication",
655 dev_name,
656 mp.mnt_mountp );
657 ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
658 break;
659 }
660 }
661 fclose( mntfile );
662 }
663 #elif defined(__linux__)
664 mntfile = fopen( _PATH_MOUNTED, "r" );
665 if( mntfile ) {
666
667 #ifdef HAVE_GETMNTENT_R
668 struct mntent *me, mbuf;
669 char buf [8192];
670 while( ( me = getmntent_r( mntfile, &mbuf, buf, sizeof(buf) ) ) ) {
671 #else
672 struct mntent *me;
673 while( ( me = getmntent( mntfile ) ) ) {
674 #endif
675 if( !strcmp( me->mnt_dir, path_copy ) ) {
676 Log3(ctx, "Attempting to use device %s"
677 " mounted on %s for CSS authentication",
678 me->mnt_fsname,
679 me->mnt_dir );
680 ctx->rd = DVDOpenImageFile( ctx, me->mnt_fsname, NULL, have_css );
681 dev_name = strdup(me->mnt_fsname);
682 break;
683 }
684 }
685 fclose( mntfile );
686 }
687 #elif defined(_WIN32) || defined(__OS2__)
688 #ifdef __OS2__
689 /* Use DVDOpenImageFile() only if it is a drive */
690 if(isalpha(path[0]) && path[1] == ':' &&
691 ( !path[2] ||
692 ((path[2] == '\\' || path[2] == '/') && !path[3])))
693 #endif
694 ctx->rd = DVDOpenImageFile( ctx, path, NULL, have_css );
695 #endif
696
697 #if !defined(_WIN32) && !defined(__OS2__)
698 if( !dev_name ) {
699 Log0(ctx, "Couldn't find device name." );
700 } else if( !ctx->rd ) {
701 Log0(ctx, "Device %s inaccessible, "
702 "CSS authentication not available.", dev_name );
703 }
704 #else
705 if( !ctx->rd ) {
706 Log0(ctx, "Device %s inaccessible, "
707 "CSS authentication not available.", path );
708 }
709 #endif
710
711 free( dev_name );
712 dev_name = NULL;
713 free( path_copy );
714 path_copy = NULL;
715
716 /**
717 * If we've opened a drive, just use that.
718 */
719 if(ctx->rd)
720 {
721 free(path);
722 return ctx;
723 }
724 /**
725 * Otherwise, we now try to open the directory tree instead.
726 */
727 ctx->rd = DVDOpenPath( path );
728 free( path );
729 if(!ctx->rd)
730 {
731 free(ctx);
732 return NULL;
733 }
734 return ctx;
735 }
736
737 DVDOpen_error:
738 /* If it's none of the above, screw it. */
739 Log0(ctx, "Could not open %s", path );
740 free( path );
741 free( path_copy );
742 if ( cdir >= 0 )
743 close( cdir );
744 free( new_path );
745 return NULL;
746 }
747
748 dvd_reader_t *DVDOpen( const char *ppath )
749 {
750 return DVDOpenCommon( NULL, NULL, ppath, NULL );
751 }
752
753 dvd_reader_t *DVDOpenStream( void *stream,
754 dvd_reader_stream_cb *stream_cb )
755 {
756 return DVDOpenCommon( stream, NULL, NULL, stream_cb );
757 }
758
759 dvd_reader_t *DVDOpen2( void *priv, const dvd_logger_cb *logcb,
760 const char *ppath )
761 {
762 return DVDOpenCommon( priv, logcb, ppath, NULL );
763 }
764
765 dvd_reader_t *DVDOpenStream2( void *priv, const dvd_logger_cb *logcb,
766 dvd_reader_stream_cb *stream_cb )
767 {
768 return DVDOpenCommon( priv, logcb, NULL, stream_cb );
769 }
770
771 void DVDClose( dvd_reader_t *dvd )
772 {
773 if( dvd ) {
774 if( dvd->rd->dev ) dvdinput_close( dvd->rd->dev );
775 if( dvd->rd->path_root ) free( dvd->rd->path_root );
776 if( dvd->rd->udfcache ) FreeUDFCache( dvd->rd->udfcache );
777 free( dvd->rd );
778 free( dvd );
779 }
780 }
781
782 /**
783 * Open an unencrypted file on a DVD image file.
784 */
785 static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *ctx, const char *filename,
786 int do_cache )
787 {
788 uint32_t start, len;
789 dvd_file_t *dvd_file;
790
791 start = UDFFindFile( ctx, filename, &len );
792 if( !start ) {
793 Log0(ctx, "DVDOpenFileUDF:UDFFindFile %s failed", filename );
794 return NULL;
795 }
796
797 dvd_file = calloc( 1, sizeof( dvd_file_t ) );
798 if( !dvd_file ) {
799 Log0(ctx, "DVDOpenFileUDF:malloc failed" );
800 return NULL;
801 }
802 dvd_file->ctx = ctx;
803 dvd_file->lb_start = start;
804 dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
805
806 /* Read the whole file in cache (unencrypted) if asked and if it doesn't
807 * exceed 128KB */
808 if( do_cache && len < 64 * DVD_VIDEO_LB_LEN ) {
809 int ret;
810
811 dvd_file->cache = malloc( len );
812 if( !dvd_file->cache )
813 return dvd_file;
814
815 ret = InternalUDFReadBlocksRaw( ctx, dvd_file->lb_start,
816 dvd_file->filesize, dvd_file->cache,
817 DVDINPUT_NOFLAGS );
818 if( ret != dvd_file->filesize ) {
819 free( dvd_file->cache );
820 dvd_file->cache = NULL;
821 }
822 }
823
824 return dvd_file;
825 }
826
827 /**
828 * Searches for <file> in directory <path>, ignoring case.
829 * Returns 0 and full filename in <filename>.
830 * or -1 on file not found.
831 * or -2 on path not found.
832 */
833 static int findDirFile( const char *path, const char *file, char *filename )
834 {
835 DIR *dir;
836 struct dirent *ent;
837
838 dir = opendir( path );
839 if( !dir ) return -2;
840
841 while( ( ent = readdir( dir ) ) != NULL ) {
842 if( !strcasecmp( ent->d_name, file ) ) {
843 sprintf( filename, "%s%s%s", path,
844 ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
845 ent->d_name );
846 closedir(dir);
847 return 0;
848 }
849 }
850 closedir(dir);
851 return -1;
852 }
853
854 static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
855 {
856 const char *nodirfile;
857 int ret;
858
859 /* Strip off the directory for our search */
860 if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
861 nodirfile = &(file[ 10 ]);
862 } else {
863 nodirfile = file;
864 }
865
866 ret = findDirFile( dvd->rd->path_root, nodirfile, filename );
867 if( ret < 0 ) {
868 char video_path[ PATH_MAX + 1 ];
869
870 /* Try also with adding the path, just in case. */
871 sprintf( video_path, "%s/VIDEO_TS/", dvd->rd->path_root );
872 ret = findDirFile( video_path, nodirfile, filename );
873 if( ret < 0 ) {
874 /* Try with the path, but in lower case. */
875 sprintf( video_path, "%s/video_ts/", dvd->rd->path_root );
876 ret = findDirFile( video_path, nodirfile, filename );
877 if( ret < 0 ) {
878 return 0;
879 }
880 }
881 }
882
883 return 1;
884 }
885
886 /**
887 * Open an unencrypted file from a DVD directory tree.
888 */
889 static dvd_file_t *DVDOpenFilePath( dvd_reader_t *ctx, const char *filename )
890 {
891 char full_path[ PATH_MAX + 1 ];
892 dvd_file_t *dvd_file;
893 dvdstat_t fileinfo;
894 dvd_input_t dev;
895
896 /* Get the full path of the file. */
897 if( !findDVDFile( ctx, filename, full_path ) ) {
898 Log0(ctx, "DVDOpenFilePath:findDVDFile %s failed", filename );
899 return NULL;
900 }
901
902 dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
903 if( !dev ) {
904 Log0(ctx, "DVDOpenFilePath:dvdinput_open %s failed", full_path );
905 return NULL;
906 }
907
908 dvd_file = calloc( 1, sizeof( dvd_file_t ) );
909 if( !dvd_file ) {
910 Log0(ctx, "DVDOpenFilePath:dvd_file malloc failed" );
911 dvdinput_close(dev);
912 return NULL;
913 }
914 dvd_file->ctx = ctx;
915
916 if( dvdstat( full_path, &fileinfo ) < 0 ) {
917 Log0(ctx, "Can't stat() %s.", filename );
918 free( dvd_file );
919 dvdinput_close( dev );
920 return NULL;
921 }
922 dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
923 dvd_file->title_devs[ 0 ] = dev;
924 dvd_file->filesize = dvd_file->title_sizes[ 0 ];
925
926 return dvd_file;
927 }
928
929 static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *ctx, int title, int menu )
930 {
931 char filename[ MAX_UDF_FILE_NAME_LEN ];
932 uint32_t start, len;
933 dvd_file_t *dvd_file;
934
935 if( title == 0 ) {
936 strcpy( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
937 } else {
938 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
939 }
940 start = UDFFindFile( ctx, filename, &len );
941 if( start == 0 ) return NULL;
942
943 dvd_file = calloc( 1, sizeof( dvd_file_t ) );
944 if( !dvd_file ) return NULL;
945 dvd_file->ctx = ctx;
946 /*Hack*/ dvd_file->css_title = title << 1 | menu;
947 dvd_file->lb_start = start;
948 dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
949
950 /* Calculate the complete file size for every file in the VOBS */
951 if( !menu ) {
952 int cur;
953
954 for( cur = 2; cur < 10; cur++ ) {
955 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
956 if( !UDFFindFile( ctx, filename, &len ) ) break;
957 dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
958 }
959 }
960
961 if( ctx->rd->css_state == 1 /* Need key init */ ) {
962 initAllCSSKeys( ctx );
963 ctx->rd->css_state = 2;
964 }
965 /*
966 if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
967 Log0(ctx, "Error cracking CSS key for %s", filename );
968 }
969 */
970
971 return dvd_file;
972 }
973
974 static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu )
975 {
976 char filename[ MAX_UDF_FILE_NAME_LEN ];
977 char full_path[ PATH_MAX + 1 ];
978 dvdstat_t fileinfo;
979 dvd_file_t *dvd_file;
980
981 dvd_file = calloc( 1, sizeof( dvd_file_t ) );
982 if( !dvd_file ) return NULL;
983 dvd_file->ctx = ctx;
984 /*Hack*/ dvd_file->css_title = title << 1 | menu;
985
986 if( menu ) {
987 dvd_input_t dev;
988
989 if( title == 0 ) {
990 strcpy( filename, "VIDEO_TS.VOB" );
991 } else {
992 sprintf( filename, "VTS_%02i_0.VOB", title );
993 }
994 if( !findDVDFile( ctx, filename, full_path ) ) {
995 free( dvd_file );
996 return NULL;
997 }
998
999 dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
1000 if( dev == NULL ) {
1001 free( dvd_file );
1002 return NULL;
1003 }
1004
1005 if( dvdstat( full_path, &fileinfo ) < 0 ) {
1006 Log0(ctx, "Can't stat() %s.", filename );
1007 dvdinput_close(dev);
1008 free( dvd_file );
1009 return NULL;
1010 }
1011 dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
1012 dvd_file->title_devs[ 0 ] = dev;
1013 dvdinput_title( dvd_file->title_devs[0], 0);
1014 dvd_file->filesize = dvd_file->title_sizes[ 0 ];
1015
1016 } else {
1017 int i;
1018
1019 for( i = 0; i < TITLES_MAX; ++i ) {
1020
1021 sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
1022 if( !findDVDFile( ctx, filename, full_path ) ) {
1023 break;
1024 }
1025
1026 if( dvdstat( full_path, &fileinfo ) < 0 ) {
1027 Log0(ctx, "Can't stat() %s.", filename );
1028 break;
1029 }
1030
1031 dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
1032 dvd_file->title_devs[ i ] = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
1033 dvdinput_title( dvd_file->title_devs[ i ], 0 );
1034 dvd_file->filesize += dvd_file->title_sizes[ i ];
1035 }
1036 if( !dvd_file->title_devs[ 0 ] ) {
1037 free( dvd_file );
1038 return NULL;
1039 }
1040 }
1041
1042 return dvd_file;
1043 }
1044
1045 dvd_file_t *DVDOpenFile( dvd_reader_t *ctx, int titlenum,
1046 dvd_read_domain_t domain )
1047 {
1048 dvd_reader_device_t *dvd = ctx->rd;
1049 char filename[ MAX_UDF_FILE_NAME_LEN ];
1050 int do_cache = 0;
1051
1052 /* Check arguments. */
1053 if( dvd == NULL || titlenum < 0 )
1054 return NULL;
1055
1056 switch( domain ) {
1057 case DVD_READ_INFO_FILE:
1058 if( titlenum == 0 ) {
1059 strcpy( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
1060 } else {
1061 sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
1062 }
1063 do_cache = 1;
1064 break;
1065 case DVD_READ_INFO_BACKUP_FILE:
1066 if( titlenum == 0 ) {
1067 strcpy( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
1068 } else {
1069 sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
1070 }
1071 do_cache = 1;
1072 break;
1073 case DVD_READ_MENU_VOBS:
1074 if( dvd->isImageFile ) {
1075 return DVDOpenVOBUDF( ctx, titlenum, 1 );
1076 } else {
1077 return DVDOpenVOBPath( ctx, titlenum, 1 );
1078 }
1079 break;
1080 case DVD_READ_TITLE_VOBS:
1081 if( titlenum == 0 ) return NULL;
1082 if( dvd->isImageFile ) {
1083 return DVDOpenVOBUDF( ctx, titlenum, 0 );
1084 } else {
1085 return DVDOpenVOBPath( ctx, titlenum, 0 );
1086 }
1087 break;
1088 default:
1089 Log1(ctx, "Invalid domain for file open." );
1090 return NULL;
1091 }
1092
1093 if( dvd->isImageFile ) {
1094 return DVDOpenFileUDF( ctx, filename, do_cache );
1095 } else {
1096 return DVDOpenFilePath( ctx, filename );
1097 }
1098 }
1099
1100 void DVDCloseFile( dvd_file_t *dvd_file )
1101 {
1102 dvd_reader_device_t *dvd = dvd_file->ctx->rd;
1103 if( dvd_file && dvd ) {
1104 if( !dvd->isImageFile ) {
1105 int i;
1106
1107 for( i = 0; i < TITLES_MAX; ++i ) {
1108 if( dvd_file->title_devs[ i ] ) {
1109 dvdinput_close( dvd_file->title_devs[i] );
1110 }
1111 }
1112 }
1113
1114 free( dvd_file->cache );
1115 free( dvd_file );
1116 dvd_file = NULL;
1117 }
1118 }
1119
1120 static int DVDFileStatVOBUDF( dvd_reader_t *dvd, int title,
1121 int menu, dvd_stat_t *statbuf )
1122 {
1123 char filename[ MAX_UDF_FILE_NAME_LEN ];
1124 uint32_t size;
1125 off_t tot_size;
1126 off_t parts_size[ 9 ];
1127 int nr_parts = 0;
1128 int n;
1129
1130 if( title == 0 )
1131 strcpy( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
1132 else
1133 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
1134
1135 if( !UDFFindFile( dvd, filename, &size ) )
1136 return -1;
1137
1138 tot_size = size;
1139 nr_parts = 1;
1140 parts_size[ 0 ] = size;
1141
1142 if( !menu ) {
1143 int cur;
1144
1145 for( cur = 2; cur < 10; cur++ ) {
1146 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
1147 if( !UDFFindFile( dvd, filename, &size ) )
1148 break;
1149
1150 parts_size[ nr_parts ] = size;
1151 tot_size += size;
1152 nr_parts++;
1153 }
1154 }
1155
1156 statbuf->size = tot_size;
1157 statbuf->nr_parts = nr_parts;
1158 for( n = 0; n < nr_parts; n++ )
1159 statbuf->parts_size[ n ] = parts_size[ n ];
1160
1161 return 0;
1162 }
1163
1164
1165 static int DVDFileStatVOBPath( dvd_reader_t *dvd, int title,
1166 int menu, dvd_stat_t *statbuf )
1167 {
1168 char filename[ MAX_UDF_FILE_NAME_LEN ];
1169 char full_path[ PATH_MAX + 1 ];
1170 dvdstat_t fileinfo;
1171 off_t tot_size;
1172 off_t parts_size[ 9 ];
1173 int nr_parts = 0;
1174 int n;
1175
1176 if( title == 0 )
1177 strcpy( filename, "VIDEO_TS.VOB" );
1178 else
1179 sprintf( filename, "VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
1180
1181 if( !findDVDFile( dvd, filename, full_path ) )
1182 return -1;
1183
1184 if( dvdstat( full_path, &fileinfo ) < 0 ) {
1185 Log1(dvd, "Can't stat() %s.", filename );
1186 return -1;
1187 }
1188
1189 tot_size = fileinfo.st_size;
1190 nr_parts = 1;
1191 parts_size[ 0 ] = fileinfo.st_size;
1192
1193 if( !menu ) {
1194 int cur;
1195 for( cur = 2; cur < 10; cur++ ) {
1196 sprintf( filename, "VTS_%02d_%d.VOB", title, cur );
1197 if( !findDVDFile( dvd, filename, full_path ) )
1198 break;
1199
1200 if( dvdstat( full_path, &fileinfo ) < 0 ) {
1201 Log1(dvd, "Can't stat() %s.", filename );
1202 break;
1203 }
1204
1205 parts_size[ nr_parts ] = fileinfo.st_size;
1206 tot_size += parts_size[ nr_parts ];
1207 nr_parts++;
1208 }
1209 }
1210
1211 statbuf->size = tot_size;
1212 statbuf->nr_parts = nr_parts;
1213 for( n = 0; n < nr_parts; n++ )
1214 statbuf->parts_size[ n ] = parts_size[ n ];
1215
1216 return 0;
1217 }
1218
1219
1220 int DVDFileStat( dvd_reader_t *reader, int titlenum,
1221 dvd_read_domain_t domain, dvd_stat_t *statbuf )
1222 {
1223 dvd_reader_device_t *dvd = reader->rd;
1224 char filename[ MAX_UDF_FILE_NAME_LEN ];
1225 dvdstat_t fileinfo;
1226 uint32_t size;
1227
1228 /* Check arguments. */
1229 if( dvd == NULL || titlenum < 0 ) {
1230 errno = EINVAL;
1231 return -1;
1232 }
1233
1234 switch( domain ) {
1235 case DVD_READ_INFO_FILE:
1236 if( titlenum == 0 )
1237 strcpy( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
1238 else
1239 sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
1240
1241 break;
1242 case DVD_READ_INFO_BACKUP_FILE:
1243 if( titlenum == 0 )
1244 strcpy( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
1245 else
1246 sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
1247
1248 break;
1249 case DVD_READ_MENU_VOBS:
1250 if( dvd->isImageFile )
1251 return DVDFileStatVOBUDF( reader, titlenum, 1, statbuf );
1252 else
1253 return DVDFileStatVOBPath( reader, titlenum, 1, statbuf );
1254
1255 break;
1256 case DVD_READ_TITLE_VOBS:
1257 if( titlenum == 0 )
1258 return -1;
1259
1260 if( dvd->isImageFile )
1261 return DVDFileStatVOBUDF( reader, titlenum, 0, statbuf );
1262 else
1263 return DVDFileStatVOBPath( reader, titlenum, 0, statbuf );
1264
1265 break;
1266 default:
1267 Log1(reader, "Invalid domain for file stat." );
1268 errno = EINVAL;
1269 return -1;
1270 }
1271
1272 if( dvd->isImageFile ) {
1273 if( UDFFindFile( reader, filename, &size ) ) {
1274 statbuf->size = size;
1275 statbuf->nr_parts = 1;
1276 statbuf->parts_size[ 0 ] = size;
1277 return 0;
1278 }
1279 } else {
1280 char full_path[ PATH_MAX + 1 ];
1281
1282 if( findDVDFile( reader, filename, full_path ) ) {
1283 if( dvdstat( full_path, &fileinfo ) < 0 )
1284 Log1(reader, "Can't stat() %s.", filename );
1285 else {
1286 statbuf->size = fileinfo.st_size;
1287 statbuf->nr_parts = 1;
1288 statbuf->parts_size[ 0 ] = statbuf->size;
1289 return 0;
1290 }
1291 }
1292 }
1293 return -1;
1294 }
1295
1296 /* Internal, but used from dvd_udf.c */
1297 int InternalUDFReadBlocksRaw( const dvd_reader_t *ctx, uint32_t lb_number,
1298 size_t block_count, unsigned char *data,
1299 int encrypted )
1300 {
1301 int ret;
1302
1303 if( !ctx->rd->dev ) {
1304 Log0(ctx, "Fatal error in block read." );
1305 return -1;
1306 }
1307
1308 ret = dvdinput_seek( ctx->rd->dev, (int) lb_number );
1309 if( ret != (int) lb_number ) {
1310 Log1(ctx, "Can't seek to block %u", lb_number );
1311 return ret;
1312 }
1313
1314 ret = dvdinput_read( ctx->rd->dev, (char *) data,
1315 (int) block_count, encrypted );
1316 return ret;
1317 }
1318
1319 /* This is using a single input and starting from 'dvd_file->lb_start' offset.
1320 *
1321 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
1322 * into the buffer located at 'data' and if 'encrypted' is set
1323 * descramble the data if it's encrypted. Returning either an
1324 * negative error or the number of blocks read. */
1325 static int DVDReadBlocksUDF( const dvd_file_t *dvd_file, uint32_t offset,
1326 size_t block_count, unsigned char *data,
1327 int encrypted )
1328 {
1329 /* If the cache is present and we don't need to decrypt, use the cache to
1330 * feed the data */
1331 if( dvd_file->cache && (encrypted & DVDINPUT_READ_DECRYPT) == 0 ) {
1332 /* Check if we don't exceed the cache (or file) size */
1333 if( block_count + offset > (size_t) dvd_file->filesize )
1334 return 0;
1335
1336 /* Copy the cache at a specified offset into data. offset and block_count
1337 * must be converted into bytes */
1338 memcpy( data, dvd_file->cache + (off_t)offset * (off_t)DVD_VIDEO_LB_LEN,
1339 (off_t)block_count * (off_t)DVD_VIDEO_LB_LEN );
1340
1341 /* return the amount of blocks copied */
1342 return block_count;
1343 } else {
1344 /* use dvdinput access */
1345 return InternalUDFReadBlocksRaw( dvd_file->ctx, dvd_file->lb_start + offset,
1346 block_count, data, encrypted );
1347 }
1348 }
1349
1350 /* This is using possibly several inputs and starting from an offset of '0'.
1351 *
1352 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
1353 * into the buffer located at 'data' and if 'encrypted' is set
1354 * descramble the data if it's encrypted. Returning either an
1355 * negative error or the number of blocks read. */
1356 static int DVDReadBlocksPath( const dvd_file_t *dvd_file, unsigned int offset,
1357 size_t block_count, unsigned char *data,
1358 int encrypted )
1359 {
1360 const dvd_reader_t *ctx = dvd_file->ctx;
1361 int i;
1362 int ret, ret2, off;
1363
1364 ret = 0;
1365 ret2 = 0;
1366 for( i = 0; i < TITLES_MAX; ++i ) {
1367 if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */
1368
1369 if( offset < dvd_file->title_sizes[ i ] ) {
1370 if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
1371 off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
1372 if( off < 0 || off != (int)offset ) {
1373 Log1(ctx, "Can't seek to block %u", offset );
1374 return off < 0 ? off : 0;
1375 }
1376 ret = dvdinput_read( dvd_file->title_devs[ i ], data,
1377 (int)block_count, encrypted );
1378 break;
1379 } else {
1380 size_t part1_size = dvd_file->title_sizes[ i ] - offset;
1381 /* FIXME: Really needs to be a while loop.
1382 * (This is only true if you try and read >1GB at a time) */
1383
1384 /* Read part 1 */
1385 off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
1386 if( off < 0 || off != (int)offset ) {
1387 Log1(ctx, "Can't seek to block %u", offset );
1388 return off < 0 ? off : 0;
1389 }
1390 ret = dvdinput_read( dvd_file->title_devs[ i ], data,
1391 (int)part1_size, encrypted );
1392 if( ret < 0 ) return ret;
1393 /* FIXME: This is wrong if i is the last file in the set.
1394 * also error from this read will not show in ret. */
1395
1396 /* Does the next part exist? If not then return now. */
1397 if( i + 1 >= TITLES_MAX || !dvd_file->title_devs[ i + 1 ] )
1398 return ret;
1399
1400 /* Read part 2 */
1401 off = dvdinput_seek( dvd_file->title_devs[ i + 1 ], 0 );
1402 if( off < 0 || off != 0 ) {
1403 Log1(ctx, "Can't seek to block %d", 0 );
1404 return off < 0 ? off : 0;
1405 }
1406 ret2 = dvdinput_read( dvd_file->title_devs[ i + 1 ],
1407 data + ( part1_size
1408 * (int64_t)DVD_VIDEO_LB_LEN ),
1409 (int)(block_count - part1_size),
1410 encrypted );
1411 if( ret2 < 0 ) return ret2;
1412 break;
1413 }
1414 } else {
1415 offset -= dvd_file->title_sizes[ i ];
1416 }
1417 }
1418
1419 return ret + ret2;
1420 }
1421
1422 /* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
1423 ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
1424 size_t block_count, unsigned char *data )
1425 {
1426 dvd_reader_t *ctx = dvd_file->ctx;
1427 dvd_reader_device_t *dvd = ctx->rd;
1428 int ret;
1429
1430 /* Check arguments. */
1431 if( dvd_file == NULL || offset < 0 || data == NULL )
1432 return -1;
1433
1434 /* Hack, and it will still fail for multiple opens in a threaded app ! */
1435 if( dvd->css_title != dvd_file->css_title ) {
1436 dvd->css_title = dvd_file->css_title;
1437 if( dvd->isImageFile ) {
1438 dvdinput_title( dvd->dev, (int)dvd_file->lb_start );
1439 }
1440 /* Here each vobu has it's own dvdcss handle, so no need to update
1441 else {
1442 dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
1443 }*/
1444 }
1445
1446 if( dvd->isImageFile ) {
1447 ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset,
1448 block_count, data, DVDINPUT_READ_DECRYPT );
1449 } else {
1450 ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset,
1451 block_count, data, DVDINPUT_READ_DECRYPT );
1452 }
1453
1454 return (ssize_t)ret;
1455 }
1456
1457 int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
1458 {
1459 /* Check arguments. */
1460 if( dvd_file == NULL || offset < 0 )
1461 return -1;
1462
1463 if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
1464 return -1;
1465 }
1466 dvd_file->seek_pos = (uint32_t) offset;
1467 return offset;
1468 }
1469
1470 int DVDFileSeekForce(dvd_file_t *dvd_file, int offset, int force_size)
1471 {
1472 dvd_reader_t *ctx = dvd_file->ctx;
1473 dvd_reader_device_t *dvd = ctx->rd;
1474 /* Check arguments. */
1475 if( dvd_file == NULL || offset <= 0 )
1476 return -1;
1477
1478 if( dvd->isImageFile ) {
1479 if( force_size < 0 )
1480 force_size = (offset - 1) / DVD_VIDEO_LB_LEN + 1;
1481 if( dvd_file->filesize < force_size ) {
1482 dvd_file->filesize = force_size;
1483 free(dvd_file->cache);
1484 dvd_file->cache = NULL;
1485 Log2(ctx, "Ignored size of file indicated in UDF.");
1486 }
1487 }
1488
1489 if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN )
1490 return -1;
1491
1492 dvd_file->seek_pos = (uint32_t) offset;
1493 return offset;
1494 }
1495
1496 ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
1497 {
1498 dvd_reader_t *ctx = dvd_file->ctx;
1499 dvd_reader_device_t *dvd = ctx->rd;
1500 unsigned char *secbuf_base, *secbuf;
1501 unsigned int numsec, seek_sector, seek_byte;
1502 int ret;
1503
1504 /* Check arguments. */
1505 if( dvd_file == NULL || data == NULL || (ssize_t)byte_size < 0 )
1506 return -1;
1507
1508 seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
1509 seek_byte = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
1510
1511 numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) +
1512 ( ( ( seek_byte + byte_size ) % DVD_VIDEO_LB_LEN ) ? 1 : 0 );
1513
1514 secbuf_base = malloc( numsec * DVD_VIDEO_LB_LEN + 2048 );
1515 if( !secbuf_base ) {
1516 Log0(ctx, "Can't allocate memory for file read" );
1517 return 0;
1518 }
1519 secbuf = (unsigned char *)(((uintptr_t)secbuf_base & ~((uintptr_t)2047)) + 2048);
1520
1521 if( dvd->isImageFile ) {
1522 ret = DVDReadBlocksUDF( dvd_file, (uint32_t) seek_sector,
1523 (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
1524 } else {
1525 ret = DVDReadBlocksPath( dvd_file, seek_sector,
1526 (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
1527 }
1528
1529 if( ret != (int) numsec ) {
1530 free( secbuf_base );
1531 return ret < 0 ? ret : 0;
1532 }
1533
1534 memcpy( data, &(secbuf[ seek_byte ]), byte_size );
1535 free( secbuf_base );
1536
1537 DVDFileSeekForce(dvd_file, dvd_file->seek_pos + byte_size, -1);
1538 return byte_size;
1539 }
1540
1541 ssize_t DVDFileSize( dvd_file_t *dvd_file )
1542 {
1543 /* Check arguments. */
1544 if( dvd_file == NULL )
1545 return -1;
1546
1547 return dvd_file->filesize;
1548 }
1549
1550 int DVDDiscID( dvd_reader_t *dvd, unsigned char *discid )
1551 {
1552 struct md5_s ctx;
1553 int title;
1554 int title_sets;
1555 int nr_of_files = 0;
1556 ifo_handle_t *vmg_ifo;
1557
1558 /* Check arguments. */
1559 if( dvd == NULL || discid == NULL )
1560 return 0;
1561
1562 vmg_ifo = ifoOpen( dvd, 0 );
1563 if( !vmg_ifo ) {
1564 Log0(dvd, "DVDDiscId, failed to open VMG IFO" );
1565 return -1;
1566 }
1567
1568 title_sets = vmg_ifo->vmgi_mat->vmg_nr_of_title_sets + 1;
1569 ifoClose( vmg_ifo );
1570
1571 if( title_sets > 10 )
1572 title_sets = 10;
1573
1574 /* Go through the first IFO:s, in order, up until the tenth,
1575 * and md5sum them, i.e VIDEO_TS.IFO and VTS_0?_0.IFO */
1576 InitMD5( &ctx );
1577 for( title = 0; title < title_sets; title++ ) {
1578 dvd_file_t *dvd_file = DVDOpenFile( dvd, title, DVD_READ_INFO_FILE );
1579 if( dvd_file != NULL ) {
1580 ssize_t bytes_read;
1581 ssize_t file_size = dvd_file->filesize * DVD_VIDEO_LB_LEN;
1582 char *buffer_base = malloc( file_size + 2048 );
1583
1584 if( buffer_base == NULL ) {
1585 DVDCloseFile( dvd_file );
1586 Log0(dvd, "DVDDiscId, failed to allocate memory for file read" );
1587 return -1;
1588 }
1589
1590 char *buffer = (char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 2048);
1591
1592 bytes_read = DVDReadBytes( dvd_file, buffer, file_size );
1593 if( bytes_read != file_size ) {
1594 Log1(dvd, "DVDDiscId read returned %zd bytes"
1595 ", wanted %zd", bytes_read, file_size );
1596 DVDCloseFile( dvd_file );
1597 free( buffer_base );
1598 return -1;
1599 }
1600
1601 AddMD5( &ctx, buffer, file_size );
1602
1603 DVDCloseFile( dvd_file );
1604 free( buffer_base );
1605 nr_of_files++;
1606 }
1607 }
1608 EndMD5( &ctx );
1609 memcpy( discid, ctx.buf, 16 );
1610 if(!nr_of_files)
1611 return -1;
1612
1613 return 0;
1614 }
1615
1616
1617 int DVDISOVolumeInfo( dvd_reader_t *ctx,
1618 char *volid, unsigned int volid_size,
1619 unsigned char *volsetid, unsigned int volsetid_size )
1620 {
1621 dvd_reader_device_t *dvd = ctx->rd;
1622 unsigned char *buffer, *buffer_base;
1623 int ret;
1624
1625 /* Check arguments. */
1626 if( dvd == NULL )
1627 return 0;
1628
1629 if( dvd->dev == NULL ) {
1630 /* No block access, so no ISO... */
1631 return -1;
1632 }
1633
1634 buffer_base = malloc( DVD_VIDEO_LB_LEN + 2048 );
1635
1636 if( buffer_base == NULL ) {
1637 Log0(ctx, "DVDISOVolumeInfo, failed to "
1638 "allocate memory for file read" );
1639 return -1;
1640 }
1641
1642 buffer = (unsigned char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 2048);
1643
1644 ret = InternalUDFReadBlocksRaw( ctx, 16, 1, buffer, 0 );
1645 if( ret != 1 ) {
1646 Log0(ctx, "DVDISOVolumeInfo, failed to "
1647 "read ISO9660 Primary Volume Descriptor" );
1648 free( buffer_base );
1649 return -1;
1650 }
1651
1652 if( (volid != NULL) && (volid_size > 0) ) {
1653 unsigned int n;
1654 for(n = 0; n < 32; n++) {
1655 if(buffer[40+n] == 0x20) {
1656 break;
1657 }
1658 }
1659
1660 if(volid_size > n+1) {
1661 volid_size = n+1;
1662 }
1663
1664 memcpy(volid, &buffer[40], volid_size-1);
1665 volid[volid_size-1] = '\0';
1666 }
1667
1668 if( (volsetid != NULL) && (volsetid_size > 0) ) {
1669 if(volsetid_size > 128) {
1670 volsetid_size = 128;
1671 }
1672 memcpy(volsetid, &buffer[190], volsetid_size);
1673 }
1674 free( buffer_base );
1675 return 0;
1676 }
1677
1678
1679 int DVDUDFVolumeInfo( dvd_reader_t *ctx,
1680 char *volid, unsigned int volid_size,
1681 unsigned char *volsetid, unsigned int volsetid_size )
1682 {
1683 int ret;
1684 /* Check arguments. */
1685 if( ctx == NULL || ctx->rd == NULL )
1686 return -1;
1687
1688 if( ctx->rd->dev == NULL ) {
1689 /* No block access, so no UDF VolumeSet Identifier */
1690 return -1;
1691 }
1692
1693 if( (volid != NULL) && (volid_size > 0) ) {
1694 ret = UDFGetVolumeIdentifier(ctx, volid, volid_size);
1695 if(!ret) {
1696 return -1;
1697 }
1698 }
1699 if( (volsetid != NULL) && (volsetid_size > 0) ) {
1700 ret = UDFGetVolumeSetIdentifier(ctx, volsetid, volsetid_size);
1701 if(!ret) {
1702 return -1;
1703 }
1704 }
1705
1706 return 0;
1707 }
1708