version/0.3
[cdimgtools.git] / cssdec.c
blob4ad4e8440a87250211bc871206ea981b3410bf79
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.
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
10 * for more details.
12 * You should have received a copy of the GNU General Public License along
13 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 #if HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <limits.h>
23 #include <getopt.h>
24 #include <errno.h>
25 #include <string.h>
27 #include <dvdcss/dvdcss.h>
29 #define EX_SUCCESS 0
30 #define EX_USAGE (~((~0)<<8))
31 #define EX_OPEN (~((~0)<<7))
32 #define EX_IO (1<<6)
33 #define EX_KEY (1<<5)
34 #define EX_UNDECRYPTED (1<<0)
35 #ifndef PROGRAM_NAME
36 # define PROGRAM_NAME "cssdec"
37 #endif
38 #ifndef PROGRAM_VERSION
39 # define PROGRAM_VERSION "0.1"
40 #endif
41 const char *progname = PROGRAM_NAME;
42 const char *progversion = PROGRAM_VERSION;
43 char verbosity = 1;
45 /* readsector() return flags */
46 #define READ_ERROR 1<<0
47 #define READ_EOF 1<<1
48 #define SCRAMBLED 1<<2
49 #define DECRYPTED 1<<3
50 #define FAILED_DECRYPTION 1<<5
52 static int readsector ( dvdcss_t, unsigned char *, const int );
53 static int isscrambled( const unsigned char * );
54 static int dumpsector ( unsigned char *, FILE * );
55 static int printe ( const char, const char *, ... );
57 static void usage( )
59 fprintf( stderr, "Usage:\n" );
60 fprintf( stderr, "\t%s -V\n", progname );
61 fprintf( stderr, "\t%s [-v|-q] [-e] [-o <out_file> [-a]] <file> [<start_sect> [<end_sect>]]\n",
62 progname );
63 fprintf( stderr, "\t%s [-v|-q] -k <file> [<start_sect>]\n", progname );
66 int main( int argc, char *argv[] )
68 int status = EX_SUCCESS;
69 const char *dvdfile, *outfile = NULL;
70 dvdcss_t dvdcss;
71 FILE *out = stdout;
72 const char *outfile_mode = "w+";
73 unsigned char data[ DVDCSS_BLOCK_SIZE * 2 ];
74 unsigned char *buffer;
75 unsigned int sector = 0, end = INT_MAX;
76 int n_processed = 0, n_scrambled = 0, n_undecrypted = 0;
77 int rc;
79 /* Options */
80 char b_noeof = 0, b_keyonly = 0;
81 extern int optind;
82 extern char *optarg;
83 while( (rc = getopt( argc, argv, "qveo:akV" )) != -1 )
84 switch( (char)rc )
86 case 'q':
87 verbosity--;
88 break;
89 case 'v':
90 verbosity++;
91 break;
92 case 'e':
93 b_noeof = 1;
94 break;
95 case 'o':
96 outfile = optarg;
97 break;
98 case 'a':
99 outfile_mode = "a+";
100 break;
101 case 'k':
102 b_keyonly = 1;
103 break;
104 case 'V':
105 printf( "%s version %s (libdvdcss version %s)\n",
106 progname, progversion, dvdcss_interface_2 );
107 exit( EX_SUCCESS );
108 break;
109 case '?':
110 default:
111 usage( );
112 exit( EX_USAGE );
114 argc -= optind;
115 argv += optind;
117 /* Command line args */
118 if( argc < 1 || argc > 3 || (b_keyonly && argc > 2) )
120 printe( 1, "syntax error" );
121 usage( );
122 exit( EX_USAGE );
124 dvdfile = argv[0];
125 if( argc >= 2 ) sector = (int)strtol( argv[1], (char **)NULL, 0 );
126 if( argc >= 3 ) end = (int)strtol( argv[2], (char **)NULL, 0 );
128 /* Initialize libdvdcss */
129 printe( 2, "%s version %s (libdvdcss version %s)",
130 progname, progversion, dvdcss_interface_2 );
131 dvdcss = dvdcss_open( (char *)dvdfile );
132 if( dvdcss == NULL )
134 printe( 1, "opening of the DVD (%s) failed", dvdfile );
135 exit( status | EX_OPEN );
138 /* Try to get a key */
139 printe( 2, "trying to obtain the title key at sector %d", sector );
140 rc = dvdcss_seek( dvdcss, sector, DVDCSS_SEEK_KEY );
141 if( rc < 0 )
143 printe( 1, "getting the title key failed (%s)",
144 dvdcss_error( dvdcss ) );
145 exit( status | EX_KEY );
147 if( b_keyonly ) goto CLOSEDVD_EXIT;
149 /* Open the output file */
150 if( outfile )
152 out = fopen( outfile, outfile_mode );
153 if( out == NULL )
155 printe( 1, "opening of the output file (%s) failed (%s)",
156 outfile, strerror( errno ) );
157 exit( status | EX_OPEN );
161 /* Align our read buffer */
162 buffer = data + DVDCSS_BLOCK_SIZE
163 - ((long int)data & (DVDCSS_BLOCK_SIZE-1));
165 for( ; sector < end; sector++ )
167 /* Read decrypted */
168 rc = readsector( dvdcss, buffer, sector );
170 /* Check & Count */
171 if( rc & READ_EOF )
173 printe( 2, "stop reading before sector %d", sector );
174 break;
176 if( rc & READ_ERROR )
178 printe( 1, "sect %d: read error; aborting", sector );
179 status |= EX_IO;
180 break;
182 n_processed++;
183 if( rc & SCRAMBLED )
185 n_scrambled++;
186 if( ! (rc & DECRYPTED) )
187 n_undecrypted++;
190 /* Process the sector */
191 if( ! dumpsector( buffer, out ) ) {
192 printe( 1, "sect %d: writing failed; aborting", sector );
193 status |= EX_IO;
194 break;
198 /* Summary & Return status */
199 printe( 2, "summary of processed sectors:" );
200 printe( 2, "%d undecrypted scrambled sectors", n_undecrypted );
201 printe( 2, "%d scrambled sectors", n_scrambled );
202 printe( 2, "%d processed sectors", n_processed );
203 if( status & EX_IO )
204 printe( 2, "partial processing because of an I/O error" );
205 if( b_noeof && sector < end ) status |= EX_IO;
206 if( n_undecrypted > 0 ) status |= EX_UNDECRYPTED;
208 /* Close & Exit */
209 if( fflush( out ) != 0 )
211 printe( 1, "flushing of the output failed (%s)", strerror( errno ) );
212 status |= EX_IO;
214 if( outfile )
215 if( fclose( out ) == EOF )
216 printe( 1, "closing of the ouput file failed (%s)",
217 strerror( errno ) );
218 CLOSEDVD_EXIT:
219 rc = dvdcss_close( dvdcss );
220 if( rc < 0 ) printe( 1, "closing of the DVD failed" );
221 exit( status );
224 /* Read a sector; read decrypted again if it seems crypted */
225 static int readsector( dvdcss_t dvdcss, unsigned char *buffer, const int sector )
227 int rc, flags = 0;
229 /* Seek at sector sector and read one sector */
230 rc = dvdcss_seek( dvdcss, sector, DVDCSS_NOFLAGS );
231 if( rc < 0 )
233 printe( 1, "sect %d: seek failed (%s)", sector, dvdcss_error( dvdcss ) );
234 return flags | READ_ERROR;
236 rc = dvdcss_read( dvdcss, buffer, 1, DVDCSS_NOFLAGS );
237 if( rc < 0 )
239 printe( 1, "sect %d: read failed (%s)", sector, dvdcss_error( dvdcss ) );
240 return flags | READ_ERROR;
242 if( rc == 0 )
244 printe( 1, "sect %d: EOF", sector );
245 return flags | READ_EOF;
248 if( ! isscrambled( buffer ) /* Check if sector is encrypted */ )
249 printe( 3, "sect %d: not crypted", sector );
250 else
252 printe( 3, "sect %d: crypted", sector );
253 flags |= SCRAMBLED;
255 /* Seek at sector sector and try to decrypt sector */
256 rc = dvdcss_seek( dvdcss, sector, DVDCSS_NOFLAGS );
257 if( rc < 0 )
259 printe( 1, "sect %d: seek failed (%s)",
260 sector, dvdcss_error( dvdcss ) );
261 return flags;
263 rc = dvdcss_read( dvdcss, buffer, 1, DVDCSS_READ_DECRYPT );
264 /* Warning: A failure to decrypt is not considered an error in
265 * libdvdcss 1.2.12 */
266 if( rc != 1 )
268 printe( 2, "sect %d: read (decrypted) failed (%s)",
269 sector, dvdcss_error( dvdcss ) );
270 return flags;
273 if( isscrambled( buffer ) /* Check if the decryption really succeeded */ )
275 /* Probably a bug in libdvdcss not to have given an error earlier */
276 printe( 1, "sect %d: still apparently crypted after decryption",
277 sector );
278 flags |= FAILED_DECRYPTION;
280 else
282 printe( 3, "sect %d: decrypted", sector );
283 flags |= DECRYPTED;
287 return flags;
290 /* Check if a sector is scrambled */
291 static int isscrambled( const unsigned char *buffer )
293 return buffer[ 0x14 ] & 0x30;
296 /* Dump the sector on stdout */
297 static int dumpsector( unsigned char *buffer, FILE *out )
299 size_t n;
300 n = fwrite( (void *)buffer, DVDCSS_BLOCK_SIZE, 1, out );
301 if( ferror( out ) )
303 printe( 1, "write error (%s)", strerror( errno ) );
304 clearerr( out );
305 return 0;
307 return (n == 1);
310 /* Print a line on stderr preceded by the program name */
311 int printe( const char level, const char *format, ... )
313 va_list arg;
314 int rc;
315 if( level > verbosity ) return 1;
317 va_start( arg, format );
318 fprintf( stderr, "%s: ", progname );
319 rc = vfprintf( stderr, format, arg );
320 va_end( arg );
321 fprintf( stderr, "\n" );
322 return rc;