dvdimgdecss.c: main(): also copy the non-decrypted parts
[cdimgtools.git] / cssdec.c
blobb28fd3079df5a3928b8d0a840d4c4ee6a15ea920
1 /* cssdec.c - simple css descrambling program using libdvdcss
2 * Copyright © 2012 Géraud Meyer <graud@gmx.com>
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 as
5 * published by the Free Software Foundation.
6 */
8 #include <stdlib.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <limits.h>
12 #include <getopt.h>
13 #include <errno.h>
14 #include <string.h>
16 #include <dvdcss/dvdcss.h>
18 #define psz_progname "cssdec"
19 #ifndef DEFAULT_VERBOSITY
20 # define DEFAULT_VERBOSITY 1
21 #endif
22 char c_verbosity = DEFAULT_VERBOSITY;
23 #define READ_ERROR 1<<0
24 #define READ_EOF 1<<1
25 #define SCRAMBLED 1<<2
26 #define DECRYPTED 1<<3
27 #define KEY_CHANGED 1<<4
28 #define FAILED_DECRYPTION 1<<5
30 static int readsector ( dvdcss_t, unsigned char *, const int );
31 static int isscrambled( const unsigned char * );
32 static int dumpsector ( unsigned char * );
33 static int printe ( const char, const char *, ... );
35 static void usage( )
37 printf( "Usage:\n" );
38 printf( "\t%s [-v] [-q] [-e] <file> [<start_sector> [<end_sector>]]\n",
39 psz_progname );
42 int main( int argc, char *argv[] )
44 char *psz_dvdfile;
45 dvdcss_t dvdcss;
46 unsigned char p_data[ DVDCSS_BLOCK_SIZE * 2 ];
47 unsigned char *p_buffer;
48 unsigned int i_sector = 0, i_end = INT_MAX;
49 int i_keys = 0, i_unread = 0, i_scrambled = 0, i_skipped = 0,
50 i_undecrypted = 0, i_unwritten = 0;
51 int i_ret;
53 /* Options */
54 char b_noeof = 0;
55 extern int optind;
56 while( (i_ret = getopt( argc, argv, "qve" )) != -1 )
57 switch( (char)i_ret )
59 case 'q':
60 c_verbosity--;
61 break;
62 case 'v':
63 c_verbosity++;
64 break;
65 case 'e':
66 b_noeof = 1;
67 break;
68 case '?':
69 default:
70 usage( );
71 exit( -1 );
73 argc -= optind;
74 argv += optind;
76 /* Command line args */
77 if( argc < 1 || argc > 3 )
79 printe( 1, "syntax error" );
80 usage( );
81 exit( -1 );
83 psz_dvdfile = argv[0];
84 if( argc >= 2 ) i_sector = atoi( argv[1] );
85 if( argc >= 3 ) i_end = atoi( argv[2] );
87 /* Initialize libdvdcss */
88 printe( 2, "using libdvdcss version %s", dvdcss_interface_2 );
89 dvdcss = dvdcss_open( psz_dvdfile );
90 if( dvdcss == NULL )
92 printe( 1, "opening of the DVD (%s) failed", psz_dvdfile );
93 exit( 127 );
96 /* Align our read buffer */
97 p_buffer = p_data + DVDCSS_BLOCK_SIZE
98 - ((long int)p_data & (DVDCSS_BLOCK_SIZE-1));
100 for( ; i_sector < i_end; i_sector++ )
102 /* Read decrypted */
103 i_ret = readsector( dvdcss, p_buffer, i_sector );
105 /* Check & Count */
106 if( i_ret & READ_EOF )
108 printe( 2, "stop reading before sector %d", i_sector );
109 break;
111 if( i_ret & READ_ERROR )
113 printe( 3, "sect %d: skipping processing", i_sector );
114 i_unread++;
115 continue;
117 if( i_ret & SCRAMBLED )
119 i_scrambled++;
120 if( i_ret & KEY_CHANGED ) i_keys++;
121 if( ! (i_ret & DECRYPTED) )
123 if( i_ret & KEY_CHANGED )
124 i_undecrypted++;
125 else
126 i_skipped++;
130 /* Process the sector */
131 if( ! dumpsector( p_buffer ) ) {
132 printe( 1, "sect %d: writing failed; aborting", i_sector );
133 i_unwritten++;
134 break;
138 /* Close the dvdcss device */
139 i_ret = dvdcss_close( dvdcss );
140 if( i_ret < 0 ) printe( 1, "closing of the DVD failed" );
142 /* Summary & Return status */
143 printe( 2, "summary of processed sectors:" );
144 if( i_unwritten ) printe( 2, "error while writing" );
145 printe( 2, "%d unread sectors", i_unread );
146 printe( 2, "%d undecrypted sectors", i_undecrypted );
147 printe( 2, "%d key changes", i_keys );
148 printe( 2, "%d (seemingly) scrambled sectors without a key", i_skipped );
149 printe( 2, "%d scrambled sectors", i_scrambled );
150 i_ret = 0;
151 if( i_unread || i_unwritten || (b_noeof && i_sector < i_end) ) i_ret |= 1<<4;
152 if( i_undecrypted ) i_ret |= 1;
153 exit( i_ret );
156 /* Read a sector; read decrypted again if it seems crypted;
157 * eventually try to get a key */
158 static int readsector( dvdcss_t dvdcss, unsigned char *p_buffer, const int i_sector )
160 int i_ret, i_return = 0;
162 /* Seek at sector i_sector and read one sector */
163 i_ret = dvdcss_seek( dvdcss, i_sector, DVDCSS_NOFLAGS );
164 if( i_ret < 0 )
166 printe( 1, "sect %d: seek failed (%s)", i_sector, dvdcss_error( dvdcss ) );
167 return i_return | READ_ERROR;
169 i_ret = dvdcss_read( dvdcss, p_buffer, 1, DVDCSS_NOFLAGS );
170 if( i_ret < 0 )
172 printe( 1, "sect %d: read failed (%s)", i_sector, dvdcss_error( dvdcss ) );
173 return i_return | READ_ERROR;
175 if( i_ret == 0 )
177 printe( 1, "sect %d: EOF", i_sector );
178 return i_return | READ_EOF;
181 if( ! isscrambled( p_buffer ) /* Check if sector is encrypted */ )
182 printe( 3, "sect %d: not crypted", i_sector );
183 else
185 printe( 3, "sect %d: crypted", i_sector );
186 i_return |= SCRAMBLED;
188 /* Seek at sector i_sector and try to decrypt sector */
189 i_ret = dvdcss_seek( dvdcss, i_sector, DVDCSS_NOFLAGS );
190 if( i_ret < 0 )
192 printe( 1, "sect %d: seek failed (%s)",
193 i_sector, dvdcss_error( dvdcss ) );
194 return i_return;
196 i_ret = dvdcss_read( dvdcss, p_buffer, 1, DVDCSS_READ_DECRYPT );
197 /* Warning: A failure to decrypt is not considered an error in
198 * libdvdcss 1.2.12 */
199 if( i_ret != 1 )
201 printe( 2, "sect %d: read (decrypted) failed (%s)",
202 i_sector, dvdcss_error( dvdcss ) );
203 return i_return;
206 if( isscrambled( p_buffer ) /* Check if sector is still encrypted */ )
208 /* Seek at sector i_sector and get a new key */
209 i_ret = dvdcss_seek( dvdcss, i_sector, DVDCSS_SEEK_KEY );
210 if( i_ret < 0 )
212 printe( 2, "sect %d: seek (key) failed (%s); skipping decryption",
213 i_sector, dvdcss_error( dvdcss ) );
214 return i_return;
216 printe( 2, "sect %d: new key", i_sector );
217 i_return |= KEY_CHANGED;
219 /* Re-try to decrypt sector */
220 i_ret = dvdcss_read( dvdcss, p_buffer, 1, DVDCSS_READ_DECRYPT );
221 if( i_ret != 1 )
223 printe( 2, "sect %d: read (decrypted) failed (%s); aborting decryption",
224 i_sector, dvdcss_error( dvdcss ) );
225 return i_return;
229 if( isscrambled( p_buffer ) /* Check if the decryption really succeeded */ )
231 /* Probably a bug in libdvdcss not to have given an error earlier */
232 printe( 1, "sect %d: still apparently crypted after decryption",
233 i_sector );
234 i_return |= FAILED_DECRYPTION;
236 else
238 printe( 3, "sect %d: decrypted", i_sector );
239 i_return |= DECRYPTED;
243 return i_return;
246 /* Check if a sector is scrambled */
247 static int isscrambled( const unsigned char *p_buffer )
249 return p_buffer[ 0x14 ] & 0x30;
252 /* Dump the sector on stdout */
253 static int dumpsector( unsigned char *p_buffer )
255 size_t n;
256 n = fwrite( (void *)p_buffer, DVDCSS_BLOCK_SIZE, 1, stdout );
257 if( ferror( stdout ) )
259 printe( 1, "write error (%s)", strerror( errno ) );
260 clearerr( stdout );
261 return 0;
263 return (n == 1);
266 /* Print a line on stderr preceded by the program name */
267 int printe( const char c_level, const char *psz_format, ... )
269 va_list arg;
270 int i_ret;
271 if( c_level > c_verbosity ) return 1;
273 va_start( arg, psz_format );
274 fprintf( stderr, "%s: ", psz_progname );
275 i_ret = vfprintf( stderr, psz_format, arg );
276 va_end( arg );
277 fprintf( stderr, "\n" );
278 return i_ret;